💎 Hash Web Directive in TuskLang for Ruby
Hash Web Directive in TuskLang for Ruby
Welcome to the revolutionary world of TuskLang's Hash Web Directive! This is where we shatter the traditional web development paradigms and embrace the power of declarative, configuration-driven web applications. In Ruby, this means combining TuskLang's elegant hash directives with Ruby's dynamic web capabilities to create applications that are as expressive as they are powerful.
What is the Hash Web Directive?
The Hash Web Directive (#web
) is TuskLang's declaration of independence from traditional web frameworks. It allows you to define complete web applications, routes, views, and behaviors using simple hash configurations. In Ruby, this translates to powerful, declarative web definitions that can be processed, rendered, and executed with minimal ceremony.
Basic Web Directive Syntax
Basic web application definition
web_app_config = {
"#web" => {
"title" => "My TuskLang Web App",
"base_url" => "https://example.com",
"routes" => {
"/" => {
"controller" => "HomeController",
"action" => "index",
"view" => "home/index"
},
"/users" => {
"controller" => "UsersController",
"action" => "index",
"view" => "users/index",
"middleware" => ["auth"]
}
},
"assets" => {
"css" => ["/assets/app.css", "/assets/components.css"],
"js" => ["/assets/app.js", "/assets/components.js"]
}
}
}Ruby class to process the web directive
class WebDirectiveProcessor
def initialize(config)
@config = config
@tusk_config = load_tusk_config
end def process_web_directive
web_config = @config["#web"]
return nil unless web_config
{
title: web_config["title"],
base_url: web_config["base_url"],
routes: process_routes(web_config["routes"]),
assets: process_assets(web_config["assets"]),
layout: web_config["layout"] || "application",
theme: web_config["theme"] || "default"
}
end
private
def process_routes(routes_config)
routes_config.transform_values do |route_config|
{
controller: route_config["controller"],
action: route_config["action"],
view: route_config["view"],
middleware: Array(route_config["middleware"]),
layout: route_config["layout"],
cache: route_config["cache"] || false
}
end
end
def process_assets(assets_config)
{
css: Array(assets_config["css"]),
js: Array(assets_config["js"]),
images: Array(assets_config["images"]),
fonts: Array(assets_config["fonts"])
}
end
def load_tusk_config
TuskConfig.load("peanu.tsk")
end
end
Advanced Web Configuration
Comprehensive web application configuration
advanced_web_config = {
"#web" => {
"title" => "Advanced TuskLang Web App",
"base_url" => "https://app.example.com",
"environment" => "production",
"routes" => {
"/" => {
"controller" => "HomeController",
"action" => "index",
"view" => "home/index",
"cache" => true,
"cache_ttl" => 3600
},
"/dashboard" => {
"controller" => "DashboardController",
"action" => "show",
"view" => "dashboard/show",
"middleware" => ["auth", "subscription"],
"layout" => "dashboard"
},
"/api/users" => {
"controller" => "Api::UsersController",
"action" => "index",
"format" => "json",
"middleware" => ["api_auth", "rate_limit:100,1m"]
}
},
"layouts" => {
"application" => {
"template" => "layouts/application",
"assets" => ["/assets/app.css", "/assets/app.js"],
"meta" => {
"viewport" => "width=device-width, initial-scale=1",
"description" => "A powerful web application built with TuskLang"
}
},
"dashboard" => {
"template" => "layouts/dashboard",
"assets" => ["/assets/dashboard.css", "/assets/dashboard.js"],
"sidebar" => true,
"header" => true
}
},
"components" => {
"navigation" => {
"template" => "components/navigation",
"data" => {
"menu_items" => [
{ "label" => "Home", "url" => "/" },
{ "label" => "Dashboard", "url" => "/dashboard" },
{ "label" => "Users", "url" => "/users" }
]
}
},
"footer" => {
"template" => "components/footer",
"data" => {
"copyright" => "2024 TuskLang Web App",
"links" => [
{ "label" => "Privacy", "url" => "/privacy" },
{ "label" => "Terms", "url" => "/terms" }
]
}
}
}
}
}Ruby processor for advanced web configurations
class AdvancedWebProcessor < WebDirectiveProcessor
def process_advanced_config
base_config = process_web_directive
web_config = @config["#web"]
base_config.merge({
layouts: process_layouts(web_config["layouts"]),
components: process_components(web_config["components"]),
environment: web_config["environment"],
security: process_security(web_config["security"]),
performance: process_performance(web_config["performance"])
})
end private
def process_layouts(layouts_config)
layouts_config.transform_values do |layout_config|
{
template: layout_config["template"],
assets: Array(layout_config["assets"]),
meta: layout_config["meta"] || {},
features: {
sidebar: layout_config["sidebar"] || false,
header: layout_config["header"] || false,
footer: layout_config["footer"] || false
}
}
end
end
def process_components(components_config)
components_config.transform_values do |component_config|
{
template: component_config["template"],
data: component_config["data"] || {},
cache: component_config["cache"] || false,
cache_ttl: component_config["cache_ttl"] || 300
}
end
end
def process_security(security_config)
return {} unless security_config
{
csrf: security_config["csrf"] || true,
xss_protection: security_config["xss_protection"] || true,
content_security_policy: security_config["content_security_policy"],
https_only: security_config["https_only"] || false
}
end
def process_performance(performance_config)
return {} unless performance_config
{
compression: performance_config["compression"] || true,
caching: performance_config["caching"] || true,
minification: performance_config["minification"] || true,
cdn: performance_config["cdn"] || false
}
end
end
Dynamic Routing and Views
Dynamic routing configuration with TuskLang features
dynamic_routing_config = {
"#web" => {
"routes" => {
"/users/:id" => {
"controller" => "UsersController",
"action" => "show",
"view" => "users/show",
"params" => {
"id" => "integer|required"
},
"before_action" => "load_user",
"after_action" => "track_view"
},
"/posts/:slug" => {
"controller" => "PostsController",
"action" => "show",
"view" => "posts/show",
"params" => {
"slug" => "string|required"
},
"cache" => true,
"cache_key" => "post:{slug}"
},
"/search" => {
"controller" => "SearchController",
"action" => "index",
"view" => "search/index",
"query_params" => {
"q" => "string",
"category" => "string",
"page" => "integer|default:1"
},
"pagination" => true
}
},
"dynamic_views" => {
"users/show" => {
"template" => "users/show",
"data_source" => "@user",
"components" => ["user_profile", "user_posts"],
"seo" => {
"title" => "User Profile - {user.name}",
"description" => "View {user.name}'s profile and posts",
"og_image" => "{user.avatar_url}"
}
},
"posts/show" => {
"template" => "posts/show",
"data_source" => "@post",
"components" => ["post_content", "comments", "related_posts"],
"seo" => {
"title" => "{post.title}",
"description" => "{post.excerpt}",
"og_image" => "{post.featured_image}"
}
}
}
}
}Ruby dynamic routing processor
class DynamicRoutingProcessor
def initialize(config)
@config = config["#web"]
end def generate_routes
routes = []
@config["routes"].each do |path, route_config|
route = build_route(path, route_config)
routes << route
end
routes
end
def process_dynamic_view(view_name, data)
view_config = @config["dynamic_views"][view_name]
return nil unless view_config
{
template: view_config["template"],
data: process_view_data(view_config["data_source"], data),
components: process_components(view_config["components"], data),
seo: process_seo(view_config["seo"], data)
}
end
private
def build_route(path, config)
{
path: path,
controller: config["controller"],
action: config["action"],
view: config["view"],
params: config["params"] || {},
query_params: config["query_params"] || {},
middleware: Array(config["middleware"]),
before_action: config["before_action"],
after_action: config["after_action"],
cache: config["cache"] || false,
cache_key: config["cache_key"],
pagination: config["pagination"] || false
}
end
def process_view_data(data_source, data)
# Process TuskLang @ variables
data_source.gsub(/@(\w+)/) do |match|
variable_name = $1
data[variable_name.to_sym]
end
end
def process_components(component_names, data)
return [] unless component_names
component_names.map do |component_name|
{
name: component_name,
data: extract_component_data(component_name, data)
}
end
end
def process_seo(seo_config, data)
seo_config.transform_values do |value|
value.gsub(/\{(\w+(?:\.\w+)*)\}/) do |match|
extract_nested_value(data, $1)
end
end
end
def extract_nested_value(data, path)
path.split('.').inject(data) do |obj, key|
obj&.dig(key.to_sym) || obj&.dig(key)
end
end
def extract_component_data(component_name, data)
case component_name
when "user_profile"
{
user: data[:user],
show_avatar: true,
show_stats: true
}
when "user_posts"
{
posts: data[:user]&.posts,
pagination: true
}
when "post_content"
{
post: data[:post],
show_author: true,
show_date: true
}
else
{}
end
end
end
Template Engine Integration
Template engine configuration with TuskLang
template_config = {
"#web" => {
"templates" => {
"engine" => "erb",
"layout" => "application",
"partials" => {
"header" => "partials/header",
"footer" => "partials/footer",
"sidebar" => "partials/sidebar"
},
"helpers" => {
"format_date" => "DateHelper#format",
"pluralize" => "TextHelper#pluralize",
"link_to" => "UrlHelper#link_to"
},
"variables" => {
"app_name" => "TuskLang Web App",
"version" => "1.0.0",
"environment" => "@env"
}
},
"views" => {
"home/index" => {
"template" => "home/index",
"layout" => "application",
"data" => {
"title" => "Welcome to TuskLang",
"hero" => {
"title" => "Build Powerful Web Apps",
"subtitle" => "With declarative configuration and Ruby power",
"cta" => "Get Started"
},
"features" => [
{
"title" => "Declarative",
"description" => "Define your app in simple hashes"
},
{
"title" => "Powerful",
"description" => "Leverage Ruby's full capabilities"
},
{
"title" => "Fast",
"description" => "Optimized for performance"
}
]
}
}
}
}
}Ruby template processor
class TemplateProcessor
def initialize(config)
@config = config["#web"]["templates"]
@views_config = config["#web"]["views"]
end def render_view(view_name, data = {})
view_config = @views_config[view_name]
return nil unless view_config
template_data = merge_template_data(view_config["data"], data)
template_vars = process_template_variables(@config["variables"])
{
template: view_config["template"],
layout: view_config["layout"] || @config["layout"],
data: template_data.merge(template_vars),
partials: @config["partials"],
helpers: @config["helpers"]
}
end
def render_partial(partial_name, data = {})
partial_path = @config["partials"][partial_name]
return nil unless partial_path
{
template: partial_path,
data: data,
layout: false
}
end
private
def merge_template_data(template_data, dynamic_data)
template_data.merge(dynamic_data) do |key, template_val, dynamic_val|
if template_val.is_a?(Hash) && dynamic_val.is_a?(Hash)
template_val.merge(dynamic_val)
else
dynamic_val || template_val
end
end
end
def process_template_variables(variables_config)
variables_config.transform_values do |value|
if value.start_with?("@")
# Process TuskLang @ variables
variable_name = value[1..-1]
TuskConfig.get(variable_name)
else
value
end
end
end
end
Asset Management and Optimization
Asset management configuration
asset_config = {
"#web" => {
"assets" => {
"css" => {
"main" => ["/assets/base.css", "/assets/components.css"],
"dashboard" => ["/assets/dashboard.css"],
"admin" => ["/assets/admin.css"]
},
"js" => {
"main" => ["/assets/app.js", "/assets/components.js"],
"dashboard" => ["/assets/dashboard.js"],
"admin" => ["/assets/admin.js"]
},
"images" => {
"favicon" => "/assets/favicon.ico",
"logo" => "/assets/logo.png",
"icons" => "/assets/icons/"
},
"fonts" => {
"primary" => "Inter",
"secondary" => "Roboto",
"monospace" => "Fira Code"
},
"optimization" => {
"minification" => true,
"compression" => true,
"bundling" => true,
"cache_busting" => true,
"cdn" => {
"enabled" => true,
"url" => "https://cdn.example.com"
}
}
}
}
}Ruby asset processor
class AssetProcessor
def initialize(config)
@config = config["#web"]["assets"]
end def process_assets_for_layout(layout_name)
{
css: get_css_for_layout(layout_name),
js: get_js_for_layout(layout_name),
images: @config["images"],
fonts: @config["fonts"]
}
end
def optimize_assets(assets)
optimization_config = @config["optimization"]
if optimization_config["minification"]
assets = minify_assets(assets)
end
if optimization_config["bundling"]
assets = bundle_assets(assets)
end
if optimization_config["cache_busting"]
assets = add_cache_busting(assets)
end
if optimization_config["cdn"]["enabled"]
assets = apply_cdn(assets, optimization_config["cdn"]["url"])
end
assets
end
private
def get_css_for_layout(layout_name)
case layout_name
when "dashboard"
@config["css"]["dashboard"]
when "admin"
@config["css"]["admin"]
else
@config["css"]["main"]
end
end
def get_js_for_layout(layout_name)
case layout_name
when "dashboard"
@config["js"]["dashboard"]
when "admin"
@config["js"]["admin"]
else
@config["js"]["main"]
end
end
def minify_assets(assets)
assets.transform_values do |asset_list|
Array(asset_list).map do |asset|
minified_path = asset.gsub(/\.(css|js)$/, '.min.\1')
File.exist?(minified_path) ? minified_path : asset
end
end
end
def bundle_assets(assets)
assets.transform_values do |asset_list|
bundle_name = generate_bundle_name(asset_list)
["/assets/bundles/#{bundle_name}"]
end
end
def add_cache_busting(assets)
assets.transform_values do |asset_list|
Array(asset_list).map do |asset|
"#{asset}?v=#{asset_version}"
end
end
end
def apply_cdn(assets, cdn_url)
assets.transform_values do |asset_list|
Array(asset_list).map do |asset|
if asset.start_with?("/assets/")
"#{cdn_url}#{asset}"
else
asset
end
end
end
end
def generate_bundle_name(asset_list)
Digest::MD5.hexdigest(asset_list.join)[0..7]
end
def asset_version
@asset_version ||= File.mtime("public/assets").to_i
end
end
Form Handling and Validation
Form configuration with TuskLang
form_config = {
"#web" => {
"forms" => {
"user_registration" => {
"action" => "/users",
"method" => "POST",
"fields" => {
"email" => {
"type" => "email",
"label" => "Email Address",
"required" => true,
"validation" => "email|required|unique:users",
"placeholder" => "Enter your email"
},
"password" => {
"type" => "password",
"label" => "Password",
"required" => true,
"validation" => "string|min:8|max:128",
"placeholder" => "Enter your password"
},
"password_confirmation" => {
"type" => "password",
"label" => "Confirm Password",
"required" => true,
"validation" => "string|same:password",
"placeholder" => "Confirm your password"
},
"terms" => {
"type" => "checkbox",
"label" => "I agree to the terms and conditions",
"required" => true,
"validation" => "accepted"
}
},
"submit" => {
"text" => "Create Account",
"class" => "btn btn-primary"
},
"success" => {
"redirect" => "/dashboard",
"message" => "Account created successfully!"
},
"error" => {
"template" => "users/new",
"message" => "Please correct the errors below."
}
}
}
}
}Ruby form processor
class FormProcessor
def initialize(config)
@config = config["#web"]["forms"]
end def process_form(form_name, data = {})
form_config = @config[form_name]
return nil unless form_config
{
action: form_config["action"],
method: form_config["method"],
fields: process_fields(form_config["fields"], data),
submit: form_config["submit"],
success: form_config["success"],
error: form_config["error"]
}
end
def validate_form(form_name, params)
form_config = @config[form_name]
return { valid: false, errors: ["Form not found"] } unless form_config
errors = {}
form_config["fields"].each do |field_name, field_config|
field_errors = validate_field(field_name, field_config, params[field_name])
errors[field_name] = field_errors if field_errors.any?
end
{ valid: errors.empty?, errors: errors }
end
private
def process_fields(fields_config, data)
fields_config.transform_values do |field_config|
{
type: field_config["type"],
label: field_config["label"],
required: field_config["required"] || false,
validation: field_config["validation"],
placeholder: field_config["placeholder"],
value: data[field_config["name"]],
options: field_config["options"]
}
end
end
def validate_field(field_name, field_config, value)
errors = []
validation_rules = field_config["validation"]&.split("|") || []
validation_rules.each do |rule|
case rule
when "required"
errors << "#{field_config['label']} is required" if value.blank?
when "email"
errors << "#{field_config['label']} must be a valid email" unless valid_email?(value)
when /^min:(\d+)$/
min_length = $1.to_i
errors << "#{field_config['label']} must be at least #{min_length} characters" if value&.length.to_i < min_length
when /^max:(\d+)$/
max_length = $1.to_i
errors << "#{field_config['label']} must be no more than #{max_length} characters" if value&.length.to_i > max_length
when /^unique:(.+)$/
table_name = $1
errors << "#{field_config['label']} is already taken" if record_exists?(table_name, field_name, value)
when /^same:(.+)$/
other_field = $1
errors << "#{field_config['label']} must match #{other_field}" unless value == params[other_field]
when "accepted"
errors << "#{field_config['label']} must be accepted" unless value == "1" || value == true
end
end
errors
end
def valid_email?(email)
email =~ /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
end
def record_exists?(table_name, field_name, value)
# Implementation would check database
false
end
end
Integration with Ruby Web Frameworks
Rails integration example
class TuskWebController < ApplicationController
include WebDirectiveProcessor def initialize
@tusk_config = load_tusk_config
@web_processor = WebDirectiveProcessor.new(@tusk_config)
@template_processor = TemplateProcessor.new(@tusk_config)
@asset_processor = AssetProcessor.new(@tusk_config)
end
def process_web_request
web_config = @web_processor.process_web_directive
route_config = find_route(request.path)
return render_404 unless route_config
# Apply middleware
apply_middleware(route_config[:middleware])
# Execute controller action
result = execute_action(route_config[:controller], route_config[:action])
# Render view
render_view(route_config[:view], result)
end
private
def find_route(path)
web_config = @web_processor.process_web_directive
web_config[:routes].find { |route_path, _| match_route(route_path, path) }&.last
end
def match_route(route_path, request_path)
# Simple route matching - would be more sophisticated in production
route_path.gsub(/:\w+/, '[^/]+') == request_path
end
def apply_middleware(middleware_list)
middleware_list.each do |middleware|
case middleware
when "auth"
authenticate_user!
when "subscription"
check_subscription!
when /^rate_limit:(.+)$/
apply_rate_limit($1)
end
end
end
def execute_action(controller_name, action_name)
controller_class = controller_name.constantize
controller = controller_class.new
controller.send(action_name)
end
def render_view(view_name, data)
view_config = @template_processor.render_view(view_name, data)
assets = @asset_processor.process_assets_for_layout(view_config[:layout])
render template: view_config[:template],
layout: view_config[:layout],
locals: view_config[:data].merge(assets: assets)
end
def load_tusk_config
TuskConfig.load("peanu.tsk")
end
end
Sinatra integration example
class TuskSinatraApp < Sinatra::Base
include WebDirectiveProcessor configure do
@tusk_config = TuskConfig.load("peanu.tsk")
@web_processor = WebDirectiveProcessor.new(@tusk_config)
@template_processor = TemplateProcessor.new(@tusk_config)
end
before do
process_web_directive
end
def process_web_directive
web_config = @web_processor.process_web_directive
return unless web_config
# Set up global variables
@app_title = web_config[:title]
@base_url = web_config[:base_url]
# Set up routes
setup_sinatra_routes(web_config[:routes])
end
private
def setup_sinatra_routes(routes)
routes.each do |path, route_config|
method = route_config[:controller]&.downcase || "get"
send(method, path) do
apply_sinatra_middleware(route_config[:middleware])
execute_sinatra_action(route_config[:controller], route_config[:action])
render_sinatra_view(route_config[:view])
end
end
end
def apply_sinatra_middleware(middleware_list)
middleware_list.each do |middleware|
case middleware
when "auth"
authenticate_user
when "cors"
enable_cors
end
end
end
def execute_sinatra_action(controller_name, action_name)
# Execute action and store result
@action_result = "#{controller_name}##{action_name}"
end
def render_sinatra_view(view_name)
view_config = @template_processor.render_view(view_name, @action_result)
erb view_config[:template].to_sym, locals: view_config[:data]
end
end
Best Practices and Patterns
Best practices for web directive usage
class WebDirectiveBestPractices
def self.validate_config(config)
errors = []
# Check required fields
unless config["#web"]
errors << "Missing #web directive"
return errors
end web_config = config["#web"]
# Validate title
if web_config["title"]&.empty?
errors << "Web app title cannot be empty"
end
# Validate routes
unless web_config["routes"]&.any?
errors << "At least one route must be defined"
end
# Validate route configurations
web_config["routes"]&.each do |path, route_config|
route_errors = validate_route(path, route_config)
errors.concat(route_errors)
end
errors
end
def self.optimize_config(config)
web_config = config["#web"]
# Set defaults
web_config["layout"] ||= "application"
web_config["theme"] ||= "default"
web_config["environment"] ||= "development"
# Optimize assets
if web_config["assets"]
web_config["assets"]["optimization"] ||= {}
web_config["assets"]["optimization"]["minification"] ||= true
web_config["assets"]["optimization"]["compression"] ||= true
end
config
end
def self.generate_documentation(config)
web_config = config["#web"]
{
title: web_config["title"],
base_url: web_config["base_url"],
routes: web_config["routes"]&.keys,
layouts: web_config["layouts"]&.keys,
components: web_config["components"]&.keys,
assets: {
css: web_config["assets"]&.dig("css")&.keys,
js: web_config["assets"]&.dig("js")&.keys
}
}
end
private
def self.validate_route(path, route_config)
errors = []
unless route_config["controller"]
errors << "Route #{path} must have a controller"
end
unless route_config["action"]
errors << "Route #{path} must have an action"
end
errors
end
end
Usage example
config = {
"#web" => {
"title" => "My TuskLang Web App",
"base_url" => "https://example.com",
"routes" => {
"/" => {
"controller" => "HomeController",
"action" => "index",
"view" => "home/index"
}
}
}
}Validate and optimize
errors = WebDirectiveBestPractices.validate_config(config)
if errors.empty?
optimized_config = WebDirectiveBestPractices.optimize_config(config)
documentation = WebDirectiveBestPractices.generate_documentation(config)
puts "Configuration is valid!"
puts "Documentation: #{documentation}"
else
puts "Configuration errors: #{errors}"
end
Conclusion
The Hash Web Directive in TuskLang represents a revolutionary approach to web development. By combining declarative configuration with Ruby's powerful web capabilities, you can create sophisticated, maintainable web applications with minimal boilerplate code.
Key benefits: - Declarative Configuration: Define complete web applications in simple hash structures - Ruby Integration: Leverage Ruby's web frameworks and ecosystem - Dynamic Routing: Powerful route matching with parameter extraction - Template Engine: Flexible template processing with TuskLang variables - Asset Management: Automatic optimization, bundling, and CDN integration - Form Handling: Comprehensive form validation and processing - Performance Optimization: Built-in caching, compression, and optimization
Remember, TuskLang is about breaking free from conventions and embracing the power of declarative, configuration-driven development. The Hash Web Directive is your gateway to building web applications that are as expressive as they are powerful!