💎 Hash Web Directive in TuskLang for Ruby

Ruby Documentation

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!