tanuki 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/app/tanuki/meta_model/manager.ttxt +1 -1
- data/app/tanuki/meta_model/model.ttxt +1 -1
- data/app/tanuki/meta_model/model_base.ttxt +2 -2
- data/bin/tanuki +34 -19
- data/config/common_application.rb +2 -0
- data/lib/tanuki/application.rb +41 -6
- data/lib/tanuki/behavior/controller_behavior.rb +24 -15
- data/lib/tanuki/behavior/meta_model_behavior.rb +85 -10
- data/lib/tanuki/behavior/model_behavior.rb +35 -26
- data/lib/tanuki/configurator.rb +0 -2
- data/lib/tanuki/context.rb +59 -34
- data/lib/tanuki/extensions/rack/builder.rb +26 -0
- data/lib/tanuki/extensions/rack/server.rb +18 -0
- data/lib/tanuki/extensions/rack/static_dir.rb +1 -1
- data/lib/tanuki/i18n.rb +1 -1
- data/lib/tanuki/version.rb +1 -1
- data/lib/tanuki.rb +3 -0
- metadata +44 -17
| @@ -1,2 +1,2 @@ | |
| 1 | 
            -
            class <%= class_name_for :manager %> | 
| 1 | 
            +
            class <%= class_name_for :manager %>
         | 
| 2 2 | 
             
            end
         | 
| @@ -1,2 +1,2 @@ | |
| 1 | 
            -
            class <%= class_name_for :model %> | 
| 1 | 
            +
            class <%= class_name_for :model %>
         | 
| 2 2 | 
             
            end
         | 
| @@ -3,10 +3,10 @@ class <%= class_name_for :model_base %> < Tanuki_Base | |
| 3 3 |  | 
| 4 4 | 
             
              class << self
         | 
| 5 5 | 
             
                def extract_key(data)
         | 
| 6 | 
            -
                  [<% key.each do | | 
| 6 | 
            +
                  [<% @key.each do |qualified| %>data[<%= qualified[0].inspect %>][<%= qualified[1].inspect %>], <% end %>]
         | 
| 7 7 | 
             
                end
         | 
| 8 8 |  | 
| 9 | 
            -
                def get(ctx | 
| 9 | 
            +
                def get(ctx, *args)
         | 
| 10 10 | 
             
                end
         | 
| 11 11 | 
             
              end
         | 
| 12 12 | 
             
            end
         | 
    
        data/bin/tanuki
    CHANGED
    
    | @@ -35,8 +35,9 @@ module Tanuki | |
| 35 35 |  | 
| 36 36 | 
             
                  def generate(cwd)
         | 
| 37 37 | 
             
                    version unless @in_repl
         | 
| 38 | 
            -
                    cwd = File.expand_path(cwd  | 
| 38 | 
            +
                    cwd = cwd ? File.expand_path(cwd) : Dir.pwd
         | 
| 39 39 | 
             
                    puts "Working directory is: #{cwd}\nTo specify another: `generate <path>'"
         | 
| 40 | 
            +
                    require 'active_support/inflector'
         | 
| 40 41 | 
             
                    require 'yaml'
         | 
| 41 42 | 
             
                    require 'fileutils'
         | 
| 42 43 | 
             
                    require 'tanuki/extensions/object'
         | 
| @@ -48,12 +49,17 @@ module Tanuki | |
| 48 49 | 
             
                    require 'tanuki/loader'
         | 
| 49 50 | 
             
                    require 'tanuki/template_compiler'
         | 
| 50 51 | 
             
                    Loader.context = ctx = Context
         | 
| 51 | 
            -
                     | 
| 52 | 
            -
                    cfg. | 
| 52 | 
            +
                    default_root = File.expand_path(File.join('..', '..'), __FILE__)
         | 
| 53 | 
            +
                    cfg = Configurator.new(Context, cwd)
         | 
| 54 | 
            +
                    if cwd != default_root
         | 
| 55 | 
            +
                      cfg.config_root = File.join(default_root, 'config')
         | 
| 56 | 
            +
                      cfg.load_config :common
         | 
| 57 | 
            +
                    end
         | 
| 53 58 | 
             
                    cfg.config_root = File.join(cwd, 'config')
         | 
| 54 | 
            -
                    cfg.load_config :common,  | 
| 59 | 
            +
                    cfg.load_config :common, cwd != default_root
         | 
| 55 60 | 
             
                    puts "\n looking for models"
         | 
| 56 61 | 
             
                    @tried = {}
         | 
| 62 | 
            +
                    @models = {}
         | 
| 57 63 | 
             
                    local_schema_root = File.expand_path(File.join('..', '..', 'schema'), __FILE__)
         | 
| 58 64 | 
             
                    generate_in(ctx.schema_root, ctx)
         | 
| 59 65 | 
             
                    generate_in(local_schema_root, ctx) if ctx.schema_root != local_schema_root
         | 
| @@ -65,15 +71,34 @@ module Tanuki | |
| 65 71 |  | 
| 66 72 | 
             
                  def generate_in(schema_root, ctx)
         | 
| 67 73 | 
             
                    Dir.entries(schema_root)[2..-1].each do |namespace_path|
         | 
| 68 | 
            -
                       | 
| 74 | 
            +
                      namespace_name = namespace_path.split('_').map {|s| s.capitalize }.join
         | 
| 75 | 
            +
                      namespace = @models[namespace_name] = {}
         | 
| 69 76 | 
             
                      Dir.glob(File.join(schema_root, namespace_path, 'models', '*.yml')) do |file_path|
         | 
| 70 77 | 
             
                        model_name = File.basename(file_path, '.yml').split('_').map {|s| s.capitalize }.join
         | 
| 71 | 
            -
                         | 
| 78 | 
            +
                        meta_model = namespace[model_name] = Tanuki_MetaModel.new(
         | 
| 79 | 
            +
                          namespace_name,
         | 
| 80 | 
            +
                          model_name,
         | 
| 81 | 
            +
                          YAML.load_file(file_path),
         | 
| 82 | 
            +
                          @models
         | 
| 83 | 
            +
                        )
         | 
| 84 | 
            +
                        meta_model.process!
         | 
| 85 | 
            +
                        if @tried.include? namespace_model_name = "#{namespace_name}.#{model_name}"
         | 
| 72 86 | 
             
                          next
         | 
| 73 87 | 
             
                        else
         | 
| 74 88 | 
             
                          @tried[namespace_model_name] = {:generated => [], :failed => [], :skipped => []}
         | 
| 75 89 | 
             
                        end
         | 
| 76 | 
            -
             | 
| 90 | 
            +
                      end
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    @models.each do |namespace_name, namespace |
         | 
| 94 | 
            +
                      namespace.each do |model_name, meta_model|
         | 
| 95 | 
            +
                        meta_model.process_relations!
         | 
| 96 | 
            +
                      end
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                    @models.each do |namespace_name, namespace |
         | 
| 100 | 
            +
                      namespace.each do |model_name, meta_model|
         | 
| 101 | 
            +
                        namespace_model_name = "#{namespace_name}.#{model_name}"
         | 
| 77 102 | 
             
                        {
         | 
| 78 103 | 
             
                          :model => false,
         | 
| 79 104 | 
             
                          :model_base => true,
         | 
| @@ -136,19 +161,9 @@ module Tanuki | |
| 136 161 |  | 
| 137 162 | 
             
                  def server(env=nil)
         | 
| 138 163 | 
             
                    env = env ? env.to_sym : :development
         | 
| 139 | 
            -
                    puts %{Calling for a Tanuki in "#{ | 
| 164 | 
            +
                    puts %{Calling for a Tanuki in "#{Dir.pwd}"}
         | 
| 140 165 | 
             
                    version unless @in_repl
         | 
| 141 166 | 
             
                    require 'tanuki'
         | 
| 142 | 
            -
                    allow_run = false
         | 
| 143 | 
            -
                    begin
         | 
| 144 | 
            -
                      @cfg = Configurator.new(Context, pwd, File.expand_path(File.join('..', '..', 'config'), __FILE__))
         | 
| 145 | 
            -
                      @cfg.load_config(([:development, :production].include? env) ? :"#{env}_application" : :common_application)
         | 
| 146 | 
            -
                      @cfg.config_root = File.join(pwd, 'config')
         | 
| 147 | 
            -
                      @cfg.load_config :"#{env}_application", true
         | 
| 148 | 
            -
                      allow_run = true
         | 
| 149 | 
            -
                    rescue NameError => e
         | 
| 150 | 
            -
                      puts "Tanuki wanted `#{e.name}', but you didn't have any."
         | 
| 151 | 
            -
                    end
         | 
| 152 167 | 
             
                    begin
         | 
| 153 168 | 
             
                      Application.run
         | 
| 154 169 | 
             
                      false
         | 
| @@ -158,7 +173,7 @@ module Tanuki | |
| 158 173 | 
             
                    rescue SystemCallError
         | 
| 159 174 | 
             
                      puts 'Tanuki ran away! Someone else is playing here.'
         | 
| 160 175 | 
             
                      true
         | 
| 161 | 
            -
                    end if  | 
| 176 | 
            +
                    end if Application.configure(env)
         | 
| 162 177 | 
             
                  end
         | 
| 163 178 |  | 
| 164 179 | 
             
                  def start_repl
         | 
| @@ -8,6 +8,7 @@ use Rack::StaticDir, 'public' | |
| 8 8 | 
             
            set :server, [:thin, :mongrel, :webrick]
         | 
| 9 9 | 
             
            set :host, '0.0.0.0'
         | 
| 10 10 | 
             
            set :port, 3000
         | 
| 11 | 
            +
            set :development, false
         | 
| 11 12 |  | 
| 12 13 | 
             
            # Default controllers
         | 
| 13 14 | 
             
            set :root_page, ::User_Page_Index
         | 
| @@ -15,6 +16,7 @@ set :missing_page, ::Tanuki_Page_Missing | |
| 15 16 |  | 
| 16 17 | 
             
            # Internationalization
         | 
| 17 18 | 
             
            set :i18n, false
         | 
| 19 | 
            +
            set :i18n_redirect, false
         | 
| 18 20 | 
             
            set :language, nil
         | 
| 19 21 | 
             
            set :language_fallback, {}
         | 
| 20 22 | 
             
            set :languages, proc { language_fallback.keys }
         | 
    
        data/lib/tanuki/application.rb
    CHANGED
    
    | @@ -9,20 +9,55 @@ module Tanuki | |
| 9 9 |  | 
| 10 10 | 
             
                class << self
         | 
| 11 11 |  | 
| 12 | 
            +
                  # Initializes application settings using configuration for environment +env+.
         | 
| 13 | 
            +
                  # These include settings for server, context, and middleware.
         | 
| 14 | 
            +
                  # Returns true, if configuration is successful.
         | 
| 15 | 
            +
                  def configure(env)
         | 
| 16 | 
            +
                    @environment = env
         | 
| 17 | 
            +
                    begin
         | 
| 18 | 
            +
                      default_root = File.expand_path(File.join('..', '..', '..'), __FILE__)
         | 
| 19 | 
            +
                      @cfg = Configurator.new(Context, pwd = Dir.pwd)
         | 
| 20 | 
            +
                      if pwd != default_root
         | 
| 21 | 
            +
                        @cfg.config_root = File.join(default_root, 'config')
         | 
| 22 | 
            +
                        @cfg.load_config(([:development, :production].include? env) ? :"#{env}_application" : :common_application)
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
                      @cfg.config_root = File.join(pwd, 'config')
         | 
| 25 | 
            +
                      @cfg.load_config :"#{env}_application", pwd != default_root
         | 
| 26 | 
            +
                      return true
         | 
| 27 | 
            +
                    rescue NameError => e
         | 
| 28 | 
            +
                      if e.name =~ /\AA-Z/
         | 
| 29 | 
            +
                        raise NameError, "missing class or module for constant `#{e.name}'", e.backtrace
         | 
| 30 | 
            +
                      else
         | 
| 31 | 
            +
                        raise e
         | 
| 32 | 
            +
                      end
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                    false
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  # Add utilized middleware to a given Rack::Builder instance +rack_builder+.
         | 
| 38 | 
            +
                  def configure_middleware(rack_builder)
         | 
| 39 | 
            +
                    @rack_middleware.each {|item| rack_builder.use(item[0], *item[1], &item[2]) }
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 12 42 | 
             
                  # Removes all occurences of a given +middleware+ from the Rack middleware pipeline.
         | 
| 13 43 | 
             
                  def discard(middleware)
         | 
| 14 44 | 
             
                    @rack_middleware.delete_if {|item| item[0] == middleware }
         | 
| 15 45 | 
             
                  end
         | 
| 16 46 |  | 
| 47 | 
            +
                  # Returns current environment +Symbol+, if application is configured.
         | 
| 48 | 
            +
                  # Returns +nil+ otherwise.
         | 
| 49 | 
            +
                  def environment
         | 
| 50 | 
            +
                    @environment ||= nil
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 17 53 | 
             
                  # Runs the application with current settings.
         | 
| 18 54 | 
             
                  def run
         | 
| 19 | 
            -
                    rack_builder = Rack::Builder.new
         | 
| 20 | 
            -
                    @rack_middleware.each {|item| rack_builder.use(item[0], *item[1], &item[2]) }
         | 
| 55 | 
            +
                    configure_middleware(rack_builder = Rack::Builder.new)
         | 
| 21 56 | 
             
                    rack_builder.run(rack_app)
         | 
| 22 | 
            -
                     | 
| 23 | 
            -
                    puts "A  | 
| 24 | 
            -
                      "You used #{ | 
| 25 | 
            -
                     | 
| 57 | 
            +
                    @context.running_server = available_server
         | 
| 58 | 
            +
                    puts "A#{'n' if @environment =~ /\A[aeiou]/} #{@environment} Tanuki appears! Press Ctrl-C to set it free.",
         | 
| 59 | 
            +
                      "You used #{@context.running_server.name.gsub(/.*::/, '')} at #{@context.host}:#{@context.port}."
         | 
| 60 | 
            +
                    @context.running_server.run rack_builder.to_app, :Host => @context.host, :Port => @context.port
         | 
| 26 61 | 
             
                  end
         | 
| 27 62 |  | 
| 28 63 | 
             
                  # Adds a given +middleware+ with optional +args+ and +block+ to the Rack middleware pipeline.
         | 
| @@ -77,7 +77,7 @@ module Tanuki | |
| 77 77 | 
             
                  @_cache[key] = child # Thread safe (possible overwrite, but within consistent state)
         | 
| 78 78 | 
             
                end
         | 
| 79 79 |  | 
| 80 | 
            -
                # Returns true | 
| 80 | 
            +
                # Returns +true+, if controller is active.
         | 
| 81 81 | 
             
                def active?
         | 
| 82 82 | 
             
                  @_active
         | 
| 83 83 | 
             
                end
         | 
| @@ -112,12 +112,16 @@ module Tanuki | |
| 112 112 | 
             
                def configure
         | 
| 113 113 | 
             
                end
         | 
| 114 114 |  | 
| 115 | 
            -
                # Returns true | 
| 115 | 
            +
                # Returns +true+, if controller is current.
         | 
| 116 116 | 
             
                def current?
         | 
| 117 117 | 
             
                  @_current
         | 
| 118 118 | 
             
                end
         | 
| 119 119 |  | 
| 120 120 | 
             
                # If set, controller navigates to a given child route by default.
         | 
| 121 | 
            +
                # Returned object should be either +nil+ (don't navigate), or a +Hash+ with keys:
         | 
| 122 | 
            +
                # * +:route+ is the +Symbol+ for the route
         | 
| 123 | 
            +
                # * +:args+ contain route arguments +Hash+
         | 
| 124 | 
            +
                # * +:redirect+ makes a 302 redirect to this route, if true (optional)
         | 
| 121 125 | 
             
                def default_route
         | 
| 122 126 | 
             
                  nil
         | 
| 123 127 | 
             
                end
         | 
| @@ -267,28 +271,33 @@ module Tanuki | |
| 267 271 |  | 
| 268 272 | 
             
                  # Dispathes route chain in context +ctx+ on +request_path+, starting with controller +klass+.
         | 
| 269 273 | 
             
                  def dispatch(ctx, klass, request_path)
         | 
| 270 | 
            -
                     | 
| 274 | 
            +
                    route_parts = parse_path(request_path)
         | 
| 271 275 | 
             
                    curr = root_ctrl = klass.new(ctx, nil, nil, true)
         | 
| 272 | 
            -
                     | 
| 276 | 
            +
                    route_parts.each do |route_part|
         | 
| 273 277 | 
             
                      curr.instance_variable_set :@_active, true
         | 
| 274 | 
            -
                      nxt = curr[ | 
| 278 | 
            +
                      nxt = curr[route_part[:route], *route_part[:args]]
         | 
| 279 | 
            +
                      curr.logical_child = nxt
         | 
| 280 | 
            +
                      curr = nxt
         | 
| 281 | 
            +
                    end
         | 
| 282 | 
            +
                    while route_part = curr.default_route
         | 
| 283 | 
            +
                      if route_part[:redirect]
         | 
| 284 | 
            +
                        klass = curr.child_class(route_part)
         | 
| 285 | 
            +
                        return {:type => :redirect, :location => grow_link(curr, route_part, klass.arg_defs)}
         | 
| 286 | 
            +
                      end
         | 
| 287 | 
            +
                      curr.instance_variable_set :@_active, true
         | 
| 288 | 
            +
                      nxt = curr[route_part[:route], *route_part[:args]]
         | 
| 275 289 | 
             
                      curr.logical_child = nxt
         | 
| 276 290 | 
             
                      curr = nxt
         | 
| 277 291 | 
             
                    end
         | 
| 278 292 | 
             
                    curr.instance_variable_set :@_active, true
         | 
| 279 293 | 
             
                    curr.instance_variable_set :@_current, true
         | 
| 280 | 
            -
                     | 
| 281 | 
            -
             | 
| 282 | 
            -
             | 
| 283 | 
            -
             | 
| 284 | 
            -
                      type = (curr.is_a? ctx.missing_page) ? :missing_page : :page
         | 
| 294 | 
            +
                    type = (curr.is_a? ctx.missing_page) ? :missing_page : :page
         | 
| 295 | 
            +
                    prev = curr
         | 
| 296 | 
            +
                    while curr = prev.visual_parent
         | 
| 297 | 
            +
                      curr.visual_child = prev
         | 
| 285 298 | 
             
                      prev = curr
         | 
| 286 | 
            -
                      while curr = prev.visual_parent
         | 
| 287 | 
            -
                        curr.visual_child = prev
         | 
| 288 | 
            -
                        prev = curr
         | 
| 289 | 
            -
                      end
         | 
| 290 | 
            -
                      {:type => type, :controller => prev}
         | 
| 291 299 | 
             
                    end
         | 
| 300 | 
            +
                    {:type => type, :controller => prev}
         | 
| 292 301 | 
             
                  end
         | 
| 293 302 |  | 
| 294 303 | 
             
                  # Extends the including module with Tanuki::ControllerBehavior::ClassMethods.
         | 
| @@ -1,13 +1,20 @@ | |
| 1 1 | 
             
            module Tanuki
         | 
| 2 2 |  | 
| 3 | 
            +
              # Tanuki::MetaModelBehavior contains all methods for the meta-model.
         | 
| 4 | 
            +
              # In is included in the meta-model class.
         | 
| 3 5 | 
             
              module MetaModelBehavior
         | 
| 4 6 |  | 
| 5 | 
            -
                   | 
| 7 | 
            +
                  # Creates new meta-model +name+ in +namespace+.
         | 
| 8 | 
            +
                  # Model schema is passed as +data+.
         | 
| 9 | 
            +
                  # Stucture +models+ contains all models being generated.
         | 
| 10 | 
            +
                  def initialize(namespace, name, data, models)
         | 
| 6 11 | 
             
                    @namespace = namespace
         | 
| 7 12 | 
             
                    @name = name
         | 
| 8 13 | 
             
                    @data = data
         | 
| 14 | 
            +
                    @models = models
         | 
| 9 15 | 
             
                  end
         | 
| 10 16 |  | 
| 17 | 
            +
                  # Returns class name for a given class type.
         | 
| 11 18 | 
             
                  def class_name_for(class_type)
         | 
| 12 19 | 
             
                    case class_type
         | 
| 13 20 | 
             
                    when :model, :model_base then "#{@namespace}_Model_#{@name}"
         | 
| @@ -15,24 +22,92 @@ module Tanuki | |
| 15 22 | 
             
                    end
         | 
| 16 23 | 
             
                  end
         | 
| 17 24 |  | 
| 25 | 
            +
                  # Returns an array of code for alias-column name pair.
         | 
| 18 26 | 
             
                  def key
         | 
| 19 | 
            -
                     | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 27 | 
            +
                    @key.inspect
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # Prepares data for template generation.
         | 
| 31 | 
            +
                  # Processes own keys, fields, etc.
         | 
| 32 | 
            +
                  def process!
         | 
| 33 | 
            +
                    process_source!
         | 
| 34 | 
            +
                    process_key!
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def process_key!
         | 
| 38 | 
            +
                    @key = @source['key'] || 'id'
         | 
| 39 | 
            +
                    @key = [@key] if @key.is_a? String
         | 
| 40 | 
            +
                    raise 'invalid key' unless @key.is_a? Array
         | 
| 41 | 
            +
                    @key.map! do |k|
         | 
| 42 | 
            +
                      parts = k.split('.').map {|p| p.to_sym }
         | 
| 43 | 
            +
                      raise "invalid key field #{k}" if parts.count > 2
         | 
| 44 | 
            +
                      if parts.count == 2
         | 
| 45 | 
            +
                        raise 'all key fields should belong to the first-source' if parts[0] != @first_source.to_s
         | 
| 46 | 
            +
                        parts
         | 
| 47 | 
            +
                      else
         | 
| 48 | 
            +
                        [@first_source, parts[0]]
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  # Extracts the model firts-source information form the YAML @data
         | 
| 54 | 
            +
                  # and performs
         | 
| 55 | 
            +
                  def process_source!
         | 
| 56 | 
            +
                    guess_table = @name.pluralize
         | 
| 57 | 
            +
                    @data ||= {}
         | 
| 58 | 
            +
                    @source = @data['source'] || guess_table
         | 
| 59 | 
            +
                    @source = {'table' => @source} if @source.is_a? String
         | 
| 60 | 
            +
                    @first_source = (@source['table'] || guess_table).downcase.to_sym
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def process_joins!
         | 
| 64 | 
            +
                    @joins = {}
         | 
| 65 | 
            +
                    @joins[@first_source] = nil
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  # Prepares data for template generation.
         | 
| 69 | 
            +
                  # Processes foreign keys, fields, etc.
         | 
| 70 | 
            +
                  def process_relations!
         | 
| 71 | 
            +
                    joins = @source['joins'] || {}
         | 
| 72 | 
            +
                    joins = [joins] if joins.is_a? String
         | 
| 73 | 
            +
                    joins = Hash[*joins.collect {|v| [v, nil] }.flatten] if joins.is_a? Array
         | 
| 74 | 
            +
                    if joins.is_a? Hash
         | 
| 75 | 
            +
                      joins.each_pair do |table_alias, join|
         | 
| 76 | 
            +
                        table_alias = table_alias.to_sym
         | 
| 77 | 
            +
                        raise "#{table_alias} is already in use" if @joins.include? table_alias
         | 
| 78 | 
            +
                        if join
         | 
| 79 | 
            +
                          if join['on'].is_a Hash
         | 
| 80 | 
            +
                            table_name = join['table'] || table_alias
         | 
| 81 | 
            +
                            on = join['on']
         | 
| 82 | 
            +
                          else
         | 
| 83 | 
            +
                            on = join
         | 
| 84 | 
            +
                            table_name = table_alias
         | 
| 85 | 
            +
                          end
         | 
| 86 | 
            +
                        else
         | 
| 87 | 
            +
                           on = nil
         | 
| 88 | 
            +
                           table_name = table_alias
         | 
| 89 | 
            +
                        end
         | 
| 90 | 
            +
                        if on
         | 
| 91 | 
            +
                        else
         | 
| 92 | 
            +
                          on = {}
         | 
| 93 | 
            +
                          @key.each do |k|
         | 
| 94 | 
            +
                            on[[table_alias, @first_source.to_s.singularize.to_sym]] = []
         | 
| 95 | 
            +
                            # TODO choose a right priciple
         | 
| 96 | 
            +
                          end
         | 
| 97 | 
            +
                        end
         | 
| 98 | 
            +
                      end
         | 
| 25 99 | 
             
                    else
         | 
| 26 | 
            -
                      raise " | 
| 100 | 
            +
                      raise "`joins' should be either nil or string or array or hash"
         | 
| 27 101 | 
             
                    end
         | 
| 28 102 | 
             
                  end
         | 
| 29 103 |  | 
| 104 | 
            +
                  # Returns code for alias-column name pair for field +field_name+.
         | 
| 30 105 | 
             
                  def qualified_name(field_name)
         | 
| 31 106 | 
             
                    parts = field_name.split('.')
         | 
| 32 107 | 
             
                    if parts.length == 1
         | 
| 33 | 
            -
                      ":#{field_name}"
         | 
| 108 | 
            +
                      "%w{:#{@first_source} :#{field_name}}"
         | 
| 34 109 | 
             
                    elsif parts.length == 2
         | 
| 35 | 
            -
                      ":#{parts[ | 
| 110 | 
            +
                      "%w{:#{parts[0]} :#{parts[1]}}"
         | 
| 36 111 | 
             
                    else
         | 
| 37 112 | 
             
                      raise "field name for model #{@namespace}.#{@name} is invalid"
         | 
| 38 113 | 
             
                    end
         | 
| @@ -1,17 +1,23 @@ | |
| 1 1 | 
             
            module Tanuki
         | 
| 2 2 |  | 
| 3 | 
            +
              # Tanuki::ModelBehavior contains basic methods for a framework model.
         | 
| 4 | 
            +
              # In is included in the base model class.
         | 
| 3 5 | 
             
              module ModelBehavior
         | 
| 4 6 |  | 
| 5 | 
            -
                 | 
| 7 | 
            +
                # Creates new model with dataset row +data+.
         | 
| 8 | 
            +
                # If the model is +lazy+, +data+ should contain only row keys.
         | 
| 9 | 
            +
                def initialize(data={}, lazy=false)
         | 
| 6 10 | 
             
                  @_data = data
         | 
| 7 11 | 
             
                  @_loaded = !lazy
         | 
| 8 12 | 
             
                end
         | 
| 9 13 |  | 
| 14 | 
            +
                # Returns the value of a given attribute, loading the model on demand.
         | 
| 10 15 | 
             
                def [](attribute)
         | 
| 11 | 
            -
                  ensure_loaded!
         | 
| 16 | 
            +
                  ensure_loaded! unless self.class[attribute].present_in(@_data)
         | 
| 12 17 | 
             
                  self.class[attribute].get(@_data)
         | 
| 13 18 | 
             
                end
         | 
| 14 19 |  | 
| 20 | 
            +
                # Sets the value of a given attribute.
         | 
| 15 21 | 
             
                def []=(attribute, value)
         | 
| 16 22 | 
             
                  @_errors ||= {}
         | 
| 17 23 | 
             
                  @_original ||= {}
         | 
| @@ -24,16 +30,27 @@ module Tanuki | |
| 24 30 | 
             
                  end
         | 
| 25 31 | 
             
                end
         | 
| 26 32 |  | 
| 33 | 
            +
                # Returns the modification errors hash.
         | 
| 34 | 
            +
                def errors
         | 
| 35 | 
            +
                  @_errors ||= {}
         | 
| 36 | 
            +
                  @_errors
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                # Returns transport representation of data.
         | 
| 27 40 | 
             
                def internals_get(attribute)
         | 
| 28 41 | 
             
                  self.class[attribute].internals_get(@_data)
         | 
| 29 42 | 
             
                end
         | 
| 30 43 |  | 
| 44 | 
            +
                # Sets transport representation of data.
         | 
| 31 45 | 
             
                def internals_set(attribute, internal_value)
         | 
| 32 46 | 
             
                  @_errors ||= {}
         | 
| 33 47 | 
             
                  internals_set(self.class[attribute], @_data)
         | 
| 34 48 | 
             
                end
         | 
| 35 49 |  | 
| 50 | 
            +
                # Returns model updates hash.
         | 
| 51 | 
            +
                # This method is used internally to generate a data source update.
         | 
| 36 52 | 
             
                def get_updates
         | 
| 53 | 
            +
                  # TODO Rewrite this properly
         | 
| 37 54 | 
             
                  @_original ||= {}
         | 
| 38 55 | 
             
                  original_data = {}
         | 
| 39 56 | 
             
                  self.class.attributes.each_pair do |name, attrib|
         | 
| @@ -46,30 +63,23 @@ module Tanuki | |
| 46 63 | 
             
                  updates
         | 
| 47 64 | 
             
                end
         | 
| 48 65 |  | 
| 49 | 
            -
                 | 
| 50 | 
            -
                  @_errors ||= {}
         | 
| 51 | 
            -
                  @_errors[attribute]
         | 
| 52 | 
            -
                end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                def invalid?(attribute)
         | 
| 55 | 
            -
                  @_errors.include? attribute
         | 
| 56 | 
            -
                end
         | 
| 57 | 
            -
             | 
| 66 | 
            +
                # Returns +true+ if there are any modification errors.
         | 
| 58 67 | 
             
                def has_errors?
         | 
| 59 68 | 
             
                  @_errors ||= {}
         | 
| 60 69 | 
             
                  @_errors == {}
         | 
| 61 70 | 
             
                end
         | 
| 62 71 |  | 
| 63 | 
            -
                def errors
         | 
| 64 | 
            -
                  @_errors ||= {}
         | 
| 65 | 
            -
                  @_errors
         | 
| 66 | 
            -
                end
         | 
| 67 | 
            -
             | 
| 68 72 | 
             
                module ClassMethods
         | 
| 69 73 |  | 
| 70 | 
            -
                   | 
| 74 | 
            +
                  # Returns meta-information for a given attribute.
         | 
| 75 | 
            +
                  def [](attribute)
         | 
| 76 | 
            +
                    @_attributes[attribute]
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  # Creates new model, or returns existing one.
         | 
| 80 | 
            +
                  def get(data, ctx, lazy=false) # IDENTITY TRACKING AND LAZY LOADING
         | 
| 71 81 | 
             
                    entity_key = extract_key(data)
         | 
| 72 | 
            -
                    key = [self, entity_key] #extract_key is generated ad hoc by model compiler!
         | 
| 82 | 
            +
                    key = [self, entity_key] # extract_key is generated ad hoc by model compiler!
         | 
| 73 83 | 
             
                    if cached = ctx.entity_cache[key]
         | 
| 74 84 | 
             
                      cached
         | 
| 75 85 | 
             
                    else
         | 
| @@ -77,30 +87,29 @@ module Tanuki | |
| 77 87 | 
             
                    end
         | 
| 78 88 | 
             
                  end
         | 
| 79 89 |  | 
| 90 | 
            +
                  # Assigns +attribute+ with definition +attr_def+ to model.
         | 
| 80 91 | 
             
                  def has_attribute(attribute, attr_def)
         | 
| 81 92 | 
             
                    @_attributes ||= superclass.instance_variable_get(:@_attributes).dup
         | 
| 82 93 | 
             
                    @_attributes[attribute] = attr_def
         | 
| 83 94 | 
             
                  end
         | 
| 84 95 |  | 
| 85 | 
            -
                   | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
                  def has_reference(attribute, reference_def)
         | 
| 90 | 
            -
                    @_references ||= superclass.instance_variable_get(:@_references).dup
         | 
| 91 | 
            -
                    @_references[attribute] = reference_def
         | 
| 96 | 
            +
                  # Adds a relation +name+ with definition +relation_def+ to model.
         | 
| 97 | 
            +
                  def has_relation(name, relation_def)
         | 
| 98 | 
            +
                    @_relations ||= superclass.instance_variable_get(:@_relations).dup
         | 
| 99 | 
            +
                    @_relations[name] = relation_def
         | 
| 92 100 | 
             
                  end
         | 
| 93 101 |  | 
| 94 102 | 
             
                  # Prepares the extended module.
         | 
| 95 103 | 
             
                  def self.extended(mod)
         | 
| 96 104 | 
             
                    mod.instance_variable_set(:@_attributes, {})
         | 
| 97 | 
            -
                    mod.instance_variable_set(:@ | 
| 105 | 
            +
                    mod.instance_variable_set(:@_relations, {})
         | 
| 98 106 | 
             
                  end
         | 
| 99 107 |  | 
| 100 108 | 
             
                end # end ClassMethods
         | 
| 101 109 |  | 
| 102 110 | 
             
                class << self
         | 
| 103 111 |  | 
| 112 | 
            +
                  # Extends the including module with Tanuki::ModelBehavior::ClassMethods.
         | 
| 104 113 | 
             
                  def included(mod)
         | 
| 105 114 | 
             
                    mod.extend ClassMethods
         | 
| 106 115 | 
             
                  end
         | 
    
        data/lib/tanuki/configurator.rb
    CHANGED
    
    | @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            module Tanuki
         | 
| 2 2 |  | 
| 3 3 | 
             
              # Tanuki::Configurator is a scope for evaluating a Tanuki application configuration block.
         | 
| 4 | 
            -
              # Use Tanuki::development_application and Tanuki::production_application to create such a block.
         | 
| 5 4 | 
             
              class Configurator
         | 
| 6 5 |  | 
| 7 6 | 
             
                # Configuration root.
         | 
| @@ -12,7 +11,6 @@ module Tanuki | |
| 12 11 | 
             
                def initialize(ctx, root, config_root=nil)
         | 
| 13 12 | 
             
                  @context = ctx
         | 
| 14 13 | 
             
                  set :root, root ? root : Dir.pwd
         | 
| 15 | 
            -
                  @config_root = config_root ? config_root : File.join(@context.root, 'config')
         | 
| 16 14 | 
             
                end
         | 
| 17 15 |  | 
| 18 16 | 
             
                # Loads and executes a given configuraion file with symbolic name +config+.
         | 
    
        data/lib/tanuki/context.rb
    CHANGED
    
    | @@ -7,41 +7,66 @@ module Tanuki | |
| 7 7 |  | 
| 8 8 | 
             
                @_defined = {}
         | 
| 9 9 |  | 
| 10 | 
            -
                 | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
                   | 
| 14 | 
            -
                  child | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
                   | 
| 22 | 
            -
                   | 
| 23 | 
            -
                  defined  | 
| 24 | 
            -
                   | 
| 25 | 
            -
             | 
| 26 | 
            -
                    if  | 
| 27 | 
            -
             | 
| 28 | 
            -
                     | 
| 29 | 
            -
                       | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 10 | 
            +
                class << self
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Creates and returns child context object.
         | 
| 13 | 
            +
                  # This object's superclass is going to be current context class.
         | 
| 14 | 
            +
                  def child
         | 
| 15 | 
            +
                    child = Class.new(self)
         | 
| 16 | 
            +
                    child.instance_variable_set(:@_defined, {})
         | 
| 17 | 
            +
                    child
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  # Returns a printable version of Tanuki::Context, represented as a +Hash+.
         | 
| 21 | 
            +
                  # Can be used during development for inspection purposes.
         | 
| 22 | 
            +
                  #--
         | 
| 23 | 
            +
                  # When changing this method, remember to update `#{__LINE__ + 12}' to `defined.inspect` line number.
         | 
| 24 | 
            +
                  # This is required to avoid infinite recursion.
         | 
| 25 | 
            +
                  def inspect
         | 
| 26 | 
            +
                    return to_s if caller.any? {|entry_point| entry_point =~ /\A#{__FILE__}:#{__LINE__ + 12}/}
         | 
| 27 | 
            +
                    defined = {}
         | 
| 28 | 
            +
                    ancestors.each do |ancestor|
         | 
| 29 | 
            +
                      ancestor.instance_variable_get(:@_defined).each_key do |key|
         | 
| 30 | 
            +
                        begin
         | 
| 31 | 
            +
                          defined[key] ||= send(key)
         | 
| 32 | 
            +
                        rescue ArgumentError
         | 
| 33 | 
            +
                          defined[key] ||= method(key)
         | 
| 34 | 
            +
                        end
         | 
| 35 | 
            +
                      end
         | 
| 36 | 
            +
                      break if ancestor.equal? Context
         | 
| 35 37 | 
             
                    end
         | 
| 36 | 
            -
                     | 
| 37 | 
            -
                  end | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 38 | 
            +
                    defined.inspect
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  # Allowes arbitary values to be assigned to context with a +key=+ method.
         | 
| 42 | 
            +
                  # A reader in context object class is created for each assigned value.
         | 
| 43 | 
            +
                  def method_missing(sym, arg=nil)
         | 
| 44 | 
            +
                    match = sym.to_s.match(/\A(?!(?:child|inspect|method_missing)=\Z)([^=]+)(=)?\Z/)
         | 
| 45 | 
            +
                    raise "`#{sym}' method cannot be called for Context and its descendants" unless match
         | 
| 46 | 
            +
                    defined = @_defined
         | 
| 47 | 
            +
                    class << self; self; end.instance_eval do
         | 
| 48 | 
            +
                      method_sym = match[1].to_sym
         | 
| 49 | 
            +
                      if defined.include? method_sym
         | 
| 50 | 
            +
                        undef_method method_sym
         | 
| 51 | 
            +
                      else
         | 
| 52 | 
            +
                        defined[method_sym] = nil
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                      if arg.is_a? Proc
         | 
| 55 | 
            +
                        define_method(method_sym, &arg)
         | 
| 56 | 
            +
                      else
         | 
| 57 | 
            +
                        define_method(method_sym) { arg }
         | 
| 58 | 
            +
                      end
         | 
| 59 | 
            +
                      return arg
         | 
| 60 | 
            +
                    end if match[2]
         | 
| 61 | 
            +
                    super
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  # Disallow context instantiation
         | 
| 65 | 
            +
                  def new
         | 
| 66 | 
            +
                    raise "contexts cannot be instantiated"
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                end # end class << self
         | 
| 45 70 |  | 
| 46 71 | 
             
              end # end Context
         | 
| 47 72 |  | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            module Rack
         | 
| 2 | 
            +
              class Builder
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                # Initializes application settings using configuration for environment +env+ and +rackup+ arguments.
         | 
| 5 | 
            +
                # Application is configured for development, if no environment is specified.
         | 
| 6 | 
            +
                # Returns Tanuki::Application::rack_app.
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                # This should be invoked from Rackup configuration files (e.g. +config.ru+):
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                #   #\ -p 3000
         | 
| 11 | 
            +
                #   require 'tanuki'
         | 
| 12 | 
            +
                #   run tanuki
         | 
| 13 | 
            +
                def tanuki(env=nil)
         | 
| 14 | 
            +
                  puts %{Calling for a Tanuki in "#{Dir.pwd}"}
         | 
| 15 | 
            +
                  at_exit { puts 'Tanuki ran away!' }
         | 
| 16 | 
            +
                  builder = self
         | 
| 17 | 
            +
                  Tanuki::Application.instance_eval do
         | 
| 18 | 
            +
                    configure(env = env ? env.to_sym : :development)
         | 
| 19 | 
            +
                    configure_middleware(builder)
         | 
| 20 | 
            +
                    puts "A racked #{env} Tanuki appears!"
         | 
| 21 | 
            +
                    rack_app
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              end # end
         | 
| 26 | 
            +
            end # end Rack
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module Rack
         | 
| 2 | 
            +
              class Server
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                # Wraps around Rack::Server#options to update application configuration accordingly.
         | 
| 5 | 
            +
                def options_with_tanuki(*args, &block)
         | 
| 6 | 
            +
                  rack_server = self
         | 
| 7 | 
            +
                  rackup_options = options_without_tanuki(*args, &block)
         | 
| 8 | 
            +
                  Tanuki::Application.instance_eval do
         | 
| 9 | 
            +
                    rackup_options.each_pair {|k, v| @context.send :"#{k.downcase}=", v }
         | 
| 10 | 
            +
                    @context.running_server = rack_server.server
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
                  rackup_options
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                alias_method_chain :options, :tanuki
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              end # end
         | 
| 18 | 
            +
            end # end Rack
         | 
    
        data/lib/tanuki/i18n.rb
    CHANGED
    
    | @@ -15,7 +15,7 @@ module Tanuki | |
| 15 15 | 
             
                # Returns default route according to default language.
         | 
| 16 16 | 
             
                def default_route
         | 
| 17 17 | 
             
                  raise 'default language is not configured' unless @_ctx.language
         | 
| 18 | 
            -
                  {:route => @_ctx.language | 
| 18 | 
            +
                  {:route => @_ctx.language, :args => {}, :redirect => @_ctx.i18n_redirect}
         | 
| 19 19 | 
             
                end
         | 
| 20 20 |  | 
| 21 21 | 
             
                # Calls default view of visual child.
         | 
    
        data/lib/tanuki/version.rb
    CHANGED
    
    
    
        data/lib/tanuki.rb
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            libdir = File.dirname(__FILE__)
         | 
| 2 2 | 
             
            $:.unshift(libdir) unless $:.include?(libdir)
         | 
| 3 3 |  | 
| 4 | 
            +
            require 'active_support/all'
         | 
| 4 5 | 
             
            require 'rack'
         | 
| 5 6 | 
             
            require 'fileutils'
         | 
| 6 7 | 
             
            require 'sequel'
         | 
| @@ -10,6 +11,8 @@ require 'escape_utils/url/rack' | |
| 10 11 | 
             
            require 'tanuki/version'
         | 
| 11 12 | 
             
            require 'tanuki/extensions/module'
         | 
| 12 13 | 
             
            require 'tanuki/extensions/object'
         | 
| 14 | 
            +
            require 'tanuki/extensions/rack/builder'
         | 
| 15 | 
            +
            require 'tanuki/extensions/rack/server'
         | 
| 13 16 | 
             
            require 'tanuki/extensions/rack/static_dir'
         | 
| 14 17 | 
             
            require 'tanuki/behavior/controller_behavior'
         | 
| 15 18 | 
             
            require 'tanuki/behavior/meta_model_behavior'
         | 
    
        metadata
    CHANGED
    
    | @@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version | |
| 4 4 | 
             
              prerelease: false
         | 
| 5 5 | 
             
              segments: 
         | 
| 6 6 | 
             
              - 0
         | 
| 7 | 
            -
              -  | 
| 8 | 
            -
              -  | 
| 9 | 
            -
              version: 0. | 
| 7 | 
            +
              - 3
         | 
| 8 | 
            +
              - 0
         | 
| 9 | 
            +
              version: 0.3.0
         | 
| 10 10 | 
             
            platform: ruby
         | 
| 11 11 | 
             
            authors: 
         | 
| 12 12 | 
             
            - Anatoly Ressin
         | 
| @@ -15,7 +15,7 @@ autorequire: | |
| 15 15 | 
             
            bindir: bin
         | 
| 16 16 | 
             
            cert_chain: []
         | 
| 17 17 |  | 
| 18 | 
            -
            date: 2010- | 
| 18 | 
            +
            date: 2010-09-10 00:00:00 +03:00
         | 
| 19 19 | 
             
            default_executable: 
         | 
| 20 20 | 
             
            dependencies: 
         | 
| 21 21 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -24,7 +24,7 @@ dependencies: | |
| 24 24 | 
             
              requirement: &id001 !ruby/object:Gem::Requirement 
         | 
| 25 25 | 
             
                none: false
         | 
| 26 26 | 
             
                requirements: 
         | 
| 27 | 
            -
                - -  | 
| 27 | 
            +
                - - ~>
         | 
| 28 28 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 29 29 | 
             
                    segments: 
         | 
| 30 30 | 
             
                    - 1
         | 
| @@ -38,13 +38,12 @@ dependencies: | |
| 38 38 | 
             
              requirement: &id002 !ruby/object:Gem::Requirement 
         | 
| 39 39 | 
             
                none: false
         | 
| 40 40 | 
             
                requirements: 
         | 
| 41 | 
            -
                - -  | 
| 41 | 
            +
                - - ~>
         | 
| 42 42 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 43 43 | 
             
                    segments: 
         | 
| 44 44 | 
             
                    - 3
         | 
| 45 45 | 
             
                    - 14
         | 
| 46 | 
            -
                     | 
| 47 | 
            -
                    version: 3.14.0
         | 
| 46 | 
            +
                    version: "3.14"
         | 
| 48 47 | 
             
              type: :runtime
         | 
| 49 48 | 
             
              version_requirements: *id002
         | 
| 50 49 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -53,30 +52,56 @@ dependencies: | |
| 53 52 | 
             
              requirement: &id003 !ruby/object:Gem::Requirement 
         | 
| 54 53 | 
             
                none: false
         | 
| 55 54 | 
             
                requirements: 
         | 
| 56 | 
            -
                - -  | 
| 55 | 
            +
                - - ~>
         | 
| 57 56 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 58 57 | 
             
                    segments: 
         | 
| 59 58 | 
             
                    - 0
         | 
| 60 59 | 
             
                    - 1
         | 
| 61 | 
            -
                     | 
| 62 | 
            -
                    version: 0.1.5
         | 
| 60 | 
            +
                    version: "0.1"
         | 
| 63 61 | 
             
              type: :runtime
         | 
| 64 62 | 
             
              version_requirements: *id003
         | 
| 65 63 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| 66 | 
            -
              name:  | 
| 64 | 
            +
              name: activesupport
         | 
| 67 65 | 
             
              prerelease: false
         | 
| 68 66 | 
             
              requirement: &id004 !ruby/object:Gem::Requirement 
         | 
| 69 67 | 
             
                none: false
         | 
| 70 68 | 
             
                requirements: 
         | 
| 71 | 
            -
                - -  | 
| 69 | 
            +
                - - ~>
         | 
| 72 70 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 73 71 | 
             
                    segments: 
         | 
| 74 | 
            -
                    - 1
         | 
| 75 72 | 
             
                    - 3
         | 
| 76 73 | 
             
                    - 0
         | 
| 77 | 
            -
                    version:  | 
| 78 | 
            -
              type: : | 
| 74 | 
            +
                    version: "3.0"
         | 
| 75 | 
            +
              type: :runtime
         | 
| 79 76 | 
             
              version_requirements: *id004
         | 
| 77 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 78 | 
            +
              name: i18n
         | 
| 79 | 
            +
              prerelease: false
         | 
| 80 | 
            +
              requirement: &id005 !ruby/object:Gem::Requirement 
         | 
| 81 | 
            +
                none: false
         | 
| 82 | 
            +
                requirements: 
         | 
| 83 | 
            +
                - - ~>
         | 
| 84 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 85 | 
            +
                    segments: 
         | 
| 86 | 
            +
                    - 0
         | 
| 87 | 
            +
                    - 4
         | 
| 88 | 
            +
                    version: "0.4"
         | 
| 89 | 
            +
              type: :runtime
         | 
| 90 | 
            +
              version_requirements: *id005
         | 
| 91 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 92 | 
            +
              name: rspec
         | 
| 93 | 
            +
              prerelease: false
         | 
| 94 | 
            +
              requirement: &id006 !ruby/object:Gem::Requirement 
         | 
| 95 | 
            +
                none: false
         | 
| 96 | 
            +
                requirements: 
         | 
| 97 | 
            +
                - - ~>
         | 
| 98 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 99 | 
            +
                    segments: 
         | 
| 100 | 
            +
                    - 1
         | 
| 101 | 
            +
                    - 3
         | 
| 102 | 
            +
                    version: "1.3"
         | 
| 103 | 
            +
              type: :development
         | 
| 104 | 
            +
              version_requirements: *id006
         | 
| 80 105 | 
             
            description: Tanuki is an MVVM-inspired web framework that fancies idiomatic Ruby, DRY and extensibility by its design.
         | 
| 81 106 | 
             
            email: tanuki@withballs.org
         | 
| 82 107 | 
             
            executables: 
         | 
| @@ -121,6 +146,8 @@ files: | |
| 121 146 | 
             
            - lib/tanuki/context.rb
         | 
| 122 147 | 
             
            - lib/tanuki/extensions/module.rb
         | 
| 123 148 | 
             
            - lib/tanuki/extensions/object.rb
         | 
| 149 | 
            +
            - lib/tanuki/extensions/rack/builder.rb
         | 
| 150 | 
            +
            - lib/tanuki/extensions/rack/server.rb
         | 
| 124 151 | 
             
            - lib/tanuki/extensions/rack/static_dir.rb
         | 
| 125 152 | 
             
            - lib/tanuki/i18n.rb
         | 
| 126 153 | 
             
            - lib/tanuki/launcher.rb
         | 
| @@ -148,7 +175,7 @@ require_paths: | |
| 148 175 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement 
         | 
| 149 176 | 
             
              none: false
         | 
| 150 177 | 
             
              requirements: 
         | 
| 151 | 
            -
              - -  | 
| 178 | 
            +
              - - ~>
         | 
| 152 179 | 
             
                - !ruby/object:Gem::Version 
         | 
| 153 180 | 
             
                  segments: 
         | 
| 154 181 | 
             
                  - 1
         |