rack-scaffold_aim 0.1.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.
- checksums.yaml +15 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +64 -0
- data/Rakefile +10 -0
- data/lib/rack/scaffold.rb +184 -0
- data/lib/rack/scaffold/adapters.rb +94 -0
- data/lib/rack/scaffold/adapters/active_record.rb +48 -0
- data/lib/rack/scaffold/adapters/core_data.rb +137 -0
- data/lib/rack/scaffold/adapters/sequel.rb +43 -0
- data/rack-scaffold_aim.gemspec +28 -0
- metadata +151 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            !binary "U0hBMQ==":
         | 
| 3 | 
            +
              metadata.gz: !binary |-
         | 
| 4 | 
            +
                ODg1ZDZhOTFkMzY3NDM2OTNlNGVhZDFlNTZkNmIyNjJkMzQ1ZTdhYw==
         | 
| 5 | 
            +
              data.tar.gz: !binary |-
         | 
| 6 | 
            +
                NjQ1YTIxYTAzN2NiMzA1NDA0ZWI1Nzk5MjY2YmJkYTI2YWI3NzI2Mg==
         | 
| 7 | 
            +
            SHA512:
         | 
| 8 | 
            +
              metadata.gz: !binary |-
         | 
| 9 | 
            +
                MzAzYzE0M2MwYjVkOWI0NjhlZTRlNzUyNzk2N2FhMDg3NzE5NGJiZDU4Y2Iz
         | 
| 10 | 
            +
                MDk1NWI4ZjA5NTg3N2ZiNzc4ZTBlZjYyZDlkNTYwNzZmZWQyNTU0NWJiZmU4
         | 
| 11 | 
            +
                M2UwMmEwZDJlODNmMjEzODZmZDlhYTQ2MWM0YzA0MWRmMDcyNmE=
         | 
| 12 | 
            +
              data.tar.gz: !binary |-
         | 
| 13 | 
            +
                MmRjNThmMDM0YzhjMzIyZDQxYzA0ODJmMTI3MDJkNzhkZGZiZjRkNDU0N2I0
         | 
| 14 | 
            +
                ZjVjMzVjMzIwZTllYTFiODA4YjA2YzQ4ZmY4ZmY4NGRmNGRkYWViNWE0YTNj
         | 
| 15 | 
            +
                YzlmMWYzZGMzZGUyM2FlMTIyZjM5MDkwYjVhMjRjNDJjZDM4ZDU=
         | 
    
        data/Gemfile
    ADDED
    
    
    
        data/LICENSE
    ADDED
    
    | @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            Copyright (c) 2013 Mattt Thompson (http://mattt.me/)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| 4 | 
            +
            of this software and associated documentation files (the "Software"), to deal
         | 
| 5 | 
            +
            in the Software without restriction, including without limitation the rights
         | 
| 6 | 
            +
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         | 
| 7 | 
            +
            copies of the Software, and to permit persons to whom the Software is
         | 
| 8 | 
            +
            furnished to do so, subject to the following conditions:
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            The above copyright notice and this permission notice shall be included in
         | 
| 11 | 
            +
            all copies or substantial portions of the Software.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         | 
| 14 | 
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         | 
| 15 | 
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         | 
| 16 | 
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         | 
| 17 | 
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         | 
| 18 | 
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         | 
| 19 | 
            +
            THE SOFTWARE.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,64 @@ | |
| 1 | 
            +
            # Rack::Scaffold
         | 
| 2 | 
            +
            **Automatically generate RESTful CRUD services**
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            > This project generalizes the webservice auto-generation functionality of [Rack::CoreData](https://github.com/mattt/rack-core-data) with a plugin architecture that can adapt to any data model format. It is currently used in the latest release of [Helios](https://github.com/helios-framework/helios)
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            ### Supported Data Models
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            - [Core Data Model](https://github.com/mattt/core_data/) (`.xcdatamodeld`)
         | 
| 9 | 
            +
            - [Sequel](https://github.com/jeremyevans/sequel)
         | 
| 10 | 
            +
            - [ActiveRecord](https://github.com/rails/rails)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            ## Usage
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            ### Gemfile
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            ```ruby
         | 
| 17 | 
            +
            source :rubygems
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            gem 'rack-scaffold', require: 'rack/scaffold'
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            gem 'sequel'
         | 
| 22 | 
            +
            gem 'core_data'
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            gem 'unicorn'
         | 
| 25 | 
            +
            gem 'pg'
         | 
| 26 | 
            +
            ```
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            ### config.ru
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            ```ruby
         | 
| 31 | 
            +
            require 'sequel'
         | 
| 32 | 
            +
            require 'core_data'
         | 
| 33 | 
            +
            require 'rack/scaffold'
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            DB = Sequel.connect(ENV['DATABASE_URL'])
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            run Rack::Scaffold model: './Example.xcdatamodeld', only: [:create, :read]
         | 
| 38 | 
            +
            ```
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            ## Available Actions
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            By default, `Rack::Scaffold` will enable all of the actions described below. Actions can be whitelisted or blacklisted by passing either the `only` or `except` options, respectively.
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            - `create` (`POST /resources`): Creates a new resource with the fields in a `www-form-urlencoded` or `application/json` encoded HTTP request body.
         | 
| 45 | 
            +
            - `read` (`GET /resources` & `GET /resources/123`): Reads a collection of resources or an individual resource at the specified URI. Supports pagination by passing either `page` & `per_page` or `limit` & `offset` parameters.
         | 
| 46 | 
            +
            - `update` (`PUT` OR `PATCH /resources/123`): Updates the specified resource with the fields in a `www-form-urlencoded` or `application/json` encoded HTTP request body.
         | 
| 47 | 
            +
            - `delete` (`DELETE /resources/123`): Deletes the specified resource.
         | 
| 48 | 
            +
            - `susbscribe` (`SUBSCRIBE` or `GET /resources` with `Accept: text/event-stream`): Subscribes to create, update, and delete actions performed, streaming corresponding JSON Patch diffs. You can read more about the Rocket technique for streaming REST resources at http://rocket.github.io.
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            ## Examples
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            An example web API using a Core Data model can be found the `/example` directory.
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            ## Contact
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            Mattt Thompson
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            - http://github.com/mattt
         | 
| 59 | 
            +
            - http://twitter.com/mattt
         | 
| 60 | 
            +
            - m@mattt.me
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            ## License
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            Rack::Scaffold is available under the MIT license. See the LICENSE file for more info.
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,10 @@ | |
| 1 | 
            +
            require "bundler"
         | 
| 2 | 
            +
            Bundler.setup
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            gemspec = eval(File.read("rack-scaffold_aim.gemspec"))
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            task :build => "#{gemspec.full_name}.gem"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            file "#{gemspec.full_name}.gem" => gemspec.files + ["rack-scaffold_aim.gemspec"] do
         | 
| 9 | 
            +
              system "gem build rack-scaffold_aim.gemspec"
         | 
| 10 | 
            +
            end
         | 
| @@ -0,0 +1,184 @@ | |
| 1 | 
            +
            require 'rack'
         | 
| 2 | 
            +
            require 'rack/contrib'
         | 
| 3 | 
            +
            require 'sinatra/base'
         | 
| 4 | 
            +
            require 'sinatra/param'
         | 
| 5 | 
            +
            require 'sinatra/multi_route'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require 'rack/scaffold/adapters'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            require 'pathname'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            module Rack
         | 
| 12 | 
            +
              class Scaffold
         | 
| 13 | 
            +
                ACTIONS = [:subscribe, :create, :read, :update, :destroy]
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def initialize(options = {})
         | 
| 16 | 
            +
                  raise ArgumentError, "Missing option: :model or :models" unless options[:model] or options[:models]
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  if options[:models] and options[:models].kind_of?(Array)
         | 
| 19 | 
            +
                    @app = Rack::Cascade.new(options.delete(:models).collect{|model| self.class.new(options.dup.merge({model: model}))}) and return
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  @app = Class.new(Sinatra::Base) do
         | 
| 23 | 
            +
                    use Rack::PostBodyContentTypeParser
         | 
| 24 | 
            +
                    register Sinatra::MultiRoute
         | 
| 25 | 
            +
                    helpers Sinatra::Param
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    before do
         | 
| 28 | 
            +
                      content_type :json
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    disable :raise_errors, :show_exceptions
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    def last_modified_time(resource, resources)
         | 
| 34 | 
            +
                      update_timestamp_field = resource.update_timestamp_field.to_sym
         | 
| 35 | 
            +
                      most_recently_updated = resources.class.include?(Enumerable) ? resources.max_by(&update_timestamp_field) : resources
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      timestamp = request.env['HTTP_IF_MODIFIED_SINCE']
         | 
| 38 | 
            +
                      timestamp = most_recently_updated.send(update_timestamp_field) if most_recently_updated
         | 
| 39 | 
            +
                      timestamp
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    def notify!(record)
         | 
| 43 | 
            +
                      return unless @@connections
         | 
| 44 | 
            +
                      
         | 
| 45 | 
            +
                      pathname = Pathname.new(request.path)
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                      lines = []
         | 
| 48 | 
            +
                      lines << "event: patch"
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                      op = case status
         | 
| 51 | 
            +
                           when 201 then :add
         | 
| 52 | 
            +
                           when 204 then :remove
         | 
| 53 | 
            +
                           else
         | 
| 54 | 
            +
                             :update
         | 
| 55 | 
            +
                           end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                      data = [{op: op, path: record.url, value: record}].to_json
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                      @@connections[pathname.dirname].each do |out|
         | 
| 60 | 
            +
                        out << "event: patch\ndata: #{data}\n\n"
         | 
| 61 | 
            +
                      end
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  @actions = (options[:only] || ACTIONS) - (options[:except] || [])
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  @adapter = Rack::Scaffold.adapters.detect{|adapter| adapter === options[:model]}
         | 
| 68 | 
            +
                  raise "No suitable adapters found for #{options[:model]} in #{Rack::Scaffold.adapters}" unless @adapter
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  resources = Array(@adapter.resources(options[:model], options))
         | 
| 71 | 
            +
                  resources.each do |resource|
         | 
| 72 | 
            +
                    @app.instance_eval do
         | 
| 73 | 
            +
                      @@connections = Hash.new([])
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                      route :get, :subscribe, "/#{resource.plural}/?" do
         | 
| 76 | 
            +
                        pass unless request.accept? 'text/event-stream'
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                        content_type 'text/event-stream'
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                        stream :keep_open do |out|
         | 
| 81 | 
            +
                          @@connections[request.path] << out
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                          out.callback do
         | 
| 84 | 
            +
                            @@connections[request.path].delete(out)
         | 
| 85 | 
            +
                          end
         | 
| 86 | 
            +
                        end
         | 
| 87 | 
            +
                      end
         | 
| 88 | 
            +
                    end if @actions.include?(:subscribe)
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    @app.instance_eval do
         | 
| 91 | 
            +
                      post "/#{resource.plural}/?" do
         | 
| 92 | 
            +
                        if record = resource.create!(params)
         | 
| 93 | 
            +
                          status 201
         | 
| 94 | 
            +
                          notify!(record)
         | 
| 95 | 
            +
                          {"#{resource.singular}" => record}.to_json
         | 
| 96 | 
            +
                        else
         | 
| 97 | 
            +
                          status 406
         | 
| 98 | 
            +
                          {errors: record.errors}.to_json
         | 
| 99 | 
            +
                        end
         | 
| 100 | 
            +
                      end
         | 
| 101 | 
            +
                    end if @actions.include?(:create)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    @app.instance_eval do
         | 
| 104 | 
            +
                      get "/#{resource.plural}/?" do
         | 
| 105 | 
            +
                        if params[:page] or params[:per_page]
         | 
| 106 | 
            +
                          param :page, Integer, default: 1, min: 1
         | 
| 107 | 
            +
                          param :per_page, Integer, default: 500, in: (1..500)
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                          resources = resource.paginate(params[:per_page], (params[:page] - 1) * params[:per_page])
         | 
| 110 | 
            +
                          last_modified(last_modified_time(resource, resources)) if resource.timestamps?
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                          {
         | 
| 113 | 
            +
                            "#{resource.plural}" => resources,
         | 
| 114 | 
            +
                            page: params[:page],
         | 
| 115 | 
            +
                            total: resource.count
         | 
| 116 | 
            +
                          }.to_json
         | 
| 117 | 
            +
                        else
         | 
| 118 | 
            +
                          param :limit, Integer, default: 500, in: (1..500)
         | 
| 119 | 
            +
                          param :offset, Integer, default: 0, min: 0
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                          resources = resource.paginate(params[:limit], params[:offset])
         | 
| 122 | 
            +
                          last_modified(last_modified_time(resource, resources)) if resource.timestamps?
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                          {
         | 
| 125 | 
            +
                            "#{resource.plural}" => resources
         | 
| 126 | 
            +
                          }.to_json
         | 
| 127 | 
            +
                        end
         | 
| 128 | 
            +
                      end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                      get "/#{resource.plural}/:id/?" do
         | 
| 131 | 
            +
                        record = resource[params[:id]] or halt 404
         | 
| 132 | 
            +
                        last_modified(last_modified_time(resource, record)) if resource.timestamps?
         | 
| 133 | 
            +
                        {"#{resource.singular}" => record}.to_json
         | 
| 134 | 
            +
                      end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                      resource.one_to_many_associations.each do |association|
         | 
| 137 | 
            +
                        get "/#{resource.plural}/:id/#{association}/?" do
         | 
| 138 | 
            +
                          record = resource[params[:id]] or halt 404
         | 
| 139 | 
            +
                          associations = record.send(association)
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                          {
         | 
| 142 | 
            +
                            "#{association}" => associations
         | 
| 143 | 
            +
                          }.to_json
         | 
| 144 | 
            +
                        end
         | 
| 145 | 
            +
                      end          
         | 
| 146 | 
            +
                    end if @actions.include?(:read)
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                    @app.instance_eval do
         | 
| 149 | 
            +
                      route :put, :patch, "/#{resource.plural}/:id/?" do
         | 
| 150 | 
            +
                        record = resource[params[:id]] or halt 404
         | 
| 151 | 
            +
                        if record.update!(params)
         | 
| 152 | 
            +
                          status 200
         | 
| 153 | 
            +
                          notify!(record)
         | 
| 154 | 
            +
                          {"#{resource.singular}" => record}.to_json
         | 
| 155 | 
            +
                        else
         | 
| 156 | 
            +
                          status 406
         | 
| 157 | 
            +
                          {errors: record.errors}.to_json
         | 
| 158 | 
            +
                        end
         | 
| 159 | 
            +
                      end
         | 
| 160 | 
            +
                    end if @actions.include?(:update)
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                    @app.instance_eval do
         | 
| 163 | 
            +
                      delete "/#{resource.plural}/:id/?" do
         | 
| 164 | 
            +
                        record = resource[params[:id]] or halt 404
         | 
| 165 | 
            +
                        if record.destroy
         | 
| 166 | 
            +
                          status 204
         | 
| 167 | 
            +
                          notify!(record)
         | 
| 168 | 
            +
                        else
         | 
| 169 | 
            +
                          status 406
         | 
| 170 | 
            +
                          {errors: record.errors}.to_json
         | 
| 171 | 
            +
                        end
         | 
| 172 | 
            +
                      end
         | 
| 173 | 
            +
                    end if @actions.include?(:destroy)
         | 
| 174 | 
            +
                  end
         | 
| 175 | 
            +
                end
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                def call(env)
         | 
| 178 | 
            +
                  @app.call(env)
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
              end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
              module Models
         | 
| 183 | 
            +
              end
         | 
| 184 | 
            +
            end
         | 
| @@ -0,0 +1,94 @@ | |
| 1 | 
            +
            module Rack
         | 
| 2 | 
            +
              class Scaffold
         | 
| 3 | 
            +
                def self.adapters
         | 
| 4 | 
            +
                  @@adapters ||= []
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                module Adapters
         | 
| 8 | 
            +
                  class NotImplementedError < StandardError; end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  class Base
         | 
| 11 | 
            +
                    attr_reader :klass
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    class << self
         | 
| 14 | 
            +
                      def inherited(adapter)
         | 
| 15 | 
            +
                        ::Rack::Scaffold.adapters << adapter
         | 
| 16 | 
            +
                        super
         | 
| 17 | 
            +
                      end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      def ===(model)
         | 
| 20 | 
            +
                        raise NotImplementedError
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      def resources(model, options = {})
         | 
| 24 | 
            +
                        raise NotImplementedError
         | 
| 25 | 
            +
                      end
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    def initialize(klass)
         | 
| 29 | 
            +
                      @klass = klass
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    def singular
         | 
| 33 | 
            +
                      raise NotImplementedError
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    def plural
         | 
| 37 | 
            +
                      raise NotImplementedError
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    def count
         | 
| 41 | 
            +
                      raise NotImplementedError
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    def all
         | 
| 45 | 
            +
                      raise NotImplementedError
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    def paginate(offset, limit)
         | 
| 49 | 
            +
                      raise NotImplementedError
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    def [](id)
         | 
| 53 | 
            +
                      raise NotImplementedError
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    def one_to_many_associations
         | 
| 57 | 
            +
                      raise NotImplementedError
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    def find(options = {})
         | 
| 61 | 
            +
                      raise NotImplementedError
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    def create!(attributes = {})
         | 
| 65 | 
            +
                      raise NotImplementedError
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    def update!(attributes = {})
         | 
| 69 | 
            +
                      raise NotImplementedError
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    def destroy!
         | 
| 73 | 
            +
                      raise NotImplementedError
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    def timestamps?
         | 
| 77 | 
            +
                      raise NotImplementedError
         | 
| 78 | 
            +
                    end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    def update_timestamp_field
         | 
| 81 | 
            +
                      raise NotImplementedError
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    def method_missing(method, *args, &block)
         | 
| 85 | 
            +
                      @klass.send(method)
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
            end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            require 'rack/scaffold/adapters/active_record' if defined?(ActiveRecord::Base)
         | 
| 93 | 
            +
            require 'rack/scaffold/adapters/sequel' if defined?(Sequel)
         | 
| 94 | 
            +
            require 'rack/scaffold/adapters/core_data' if defined?(Sequel) and defined?(CoreData)
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            require 'active_record'
         | 
| 2 | 
            +
            require 'forwardable'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Rack::Scaffold::Adapters
         | 
| 5 | 
            +
              class ActiveRecord < Base
         | 
| 6 | 
            +
                extend Forwardable
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def_delegators :@klass, :count, :all, :find, :create!, :update!, :destroy!
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                class << self
         | 
| 11 | 
            +
                  def ===(model)
         | 
| 12 | 
            +
                    ::ActiveRecord::Base === model
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def resources(model, options = {})
         | 
| 16 | 
            +
                    model
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def timestamps?
         | 
| 20 | 
            +
                    record_timestamps?
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def singular
         | 
| 25 | 
            +
                  @klass.name.downcase
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def plural
         | 
| 29 | 
            +
                  @klass.table_name
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def paginate(limit, offset)
         | 
| 33 | 
            +
                  @klass.limit(limit).offset(offset)
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def [](id)
         | 
| 37 | 
            +
                  self.find(id)
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def one_to_many_associations
         | 
| 41 | 
            +
                  @klass.reflect_on_all_associations(:has_many).collect(&:name)
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def update_timestamp_field
         | 
| 45 | 
            +
                  self.attribute_names.include?("updated_at") ? "updated_at" : "updated_on"
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         | 
| @@ -0,0 +1,137 @@ | |
| 1 | 
            +
            require 'core_data'
         | 
| 2 | 
            +
            require 'sequel'
         | 
| 3 | 
            +
            require 'active_support/inflector'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Rack::Scaffold::Adapters
         | 
| 6 | 
            +
              class CoreData < Sequel
         | 
| 7 | 
            +
                class << self
         | 
| 8 | 
            +
                  def ===(model)
         | 
| 9 | 
            +
                    return true if ::CoreData::DataModel === model
         | 
| 10 | 
            +
                    !! ::CoreData::DataModel.new(model) rescue false
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def resources(xcdatamodel, options = {})
         | 
| 14 | 
            +
                    model = ::CoreData::DataModel.new(xcdatamodel)
         | 
| 15 | 
            +
                    resources = model.entities.collect{|entity| resource = new(entity, options)}
         | 
| 16 | 
            +
                    model.entities.each do |entity|
         | 
| 17 | 
            +
                      resources.each do |resource|
         | 
| 18 | 
            +
                        resource.establish_associations!(entity)
         | 
| 19 | 
            +
                      end
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    return resources
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def initialize(entity, options = {})
         | 
| 27 | 
            +
                  klass = Class.new(::Sequel::Model)
         | 
| 28 | 
            +
                  klass.dataset = entity.name.downcase.pluralize.to_sym
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  klass.class_eval do
         | 
| 31 | 
            +
                    alias :update! :update
         | 
| 32 | 
            +
                    alias :destroy! :destroy
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    self.strict_param_setting = false
         | 
| 35 | 
            +
                    self.raise_on_save_failure = false
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    plugin :json_serializer, naked: true, include: [:url]
         | 
| 38 | 
            +
                    plugin :schema
         | 
| 39 | 
            +
                    plugin :validation_helpers
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    if options[:timestamps]
         | 
| 42 | 
            +
                      if options[:timestamps].instance_of? Hash
         | 
| 43 | 
            +
                        plugin :timestamps, options[:timestamps]
         | 
| 44 | 
            +
                      else
         | 
| 45 | 
            +
                        plugin :timestamps
         | 
| 46 | 
            +
                      end
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    def url
         | 
| 50 | 
            +
                      "/#{self.class.table_name}/#{self[primary_key]}"
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    set_schema do
         | 
| 54 | 
            +
                      primary_key :id
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      entity.attributes.each do |attribute|
         | 
| 57 | 
            +
                        next if attribute.transient?
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                        options = {
         | 
| 60 | 
            +
                          :null => attribute.optional?,
         | 
| 61 | 
            +
                          :index => attribute.indexed?,
         | 
| 62 | 
            +
                          :default => attribute.default_value
         | 
| 63 | 
            +
                        }
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                        type = case attribute.type
         | 
| 66 | 
            +
                               when "Integer 16" then :int2
         | 
| 67 | 
            +
                               when "Integer 32" then :int4
         | 
| 68 | 
            +
                               when "Integer 64" then :int8
         | 
| 69 | 
            +
                               when "Float" then :float4
         | 
| 70 | 
            +
                               when "Double" then :float8
         | 
| 71 | 
            +
                               when "Decimal" then :float8
         | 
| 72 | 
            +
                               when "Date" then :timestamp
         | 
| 73 | 
            +
                               when "Boolean" then :boolean
         | 
| 74 | 
            +
                               when "Binary" then :bytea
         | 
| 75 | 
            +
                               else :varchar
         | 
| 76 | 
            +
                               end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                        column attribute.name.to_sym, type, options
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                      entity.relationships.each do |relationship|
         | 
| 82 | 
            +
                        options = {
         | 
| 83 | 
            +
                          :index => true,
         | 
| 84 | 
            +
                          :null => relationship.optional?
         | 
| 85 | 
            +
                        }
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                        if not relationship.to_many?
         | 
| 88 | 
            +
                          column "#{relationship.name}_id".to_sym, :integer, options
         | 
| 89 | 
            +
                        end
         | 
| 90 | 
            +
                      end
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    if table_exists?
         | 
| 94 | 
            +
                      missing_columns = schema.columns.reject{|c| columns.include?(c[:name])}
         | 
| 95 | 
            +
                      db.alter_table table_name do
         | 
| 96 | 
            +
                        missing_columns.each do |options|
         | 
| 97 | 
            +
                          add_column options.delete(:name), options.delete(:type), options
         | 
| 98 | 
            +
                        end
         | 
| 99 | 
            +
                      end
         | 
| 100 | 
            +
                    else
         | 
| 101 | 
            +
                      create_table
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  klass.send :define_method, :validate do
         | 
| 106 | 
            +
                    entity.attributes.each do |attribute|
         | 
| 107 | 
            +
                      case attribute.type
         | 
| 108 | 
            +
                      when "Integer 16", "Integer 32", "Integer 64"
         | 
| 109 | 
            +
                        validates_integer attribute.name
         | 
| 110 | 
            +
                      when "Float", "Double", "Decimal"
         | 
| 111 | 
            +
                        validates_numeric attribute.name
         | 
| 112 | 
            +
                      when "String"
         | 
| 113 | 
            +
                        validates_min_length attribute.minimum_value, attribute.name if attribute.minimum_value
         | 
| 114 | 
            +
                        validates_max_length attribute.maximum_value, attribute.name if attribute.maximum_value
         | 
| 115 | 
            +
                      end
         | 
| 116 | 
            +
                    end
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                  super(CoreData.const_set(entity.name, klass))
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def establish_associations!(entity)
         | 
| 123 | 
            +
                  klass.class_eval do
         | 
| 124 | 
            +
                    entity.relationships.each do |relationship|
         | 
| 125 | 
            +
                      options = {:class => CoreData.const_get(relationship.destination.capitalize)}
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                      options = {}
         | 
| 128 | 
            +
                      if relationship.to_many?
         | 
| 129 | 
            +
                        one_to_many relationship.name.to_sym, options
         | 
| 130 | 
            +
                      else
         | 
| 131 | 
            +
                        many_to_one relationship.name.to_sym, options
         | 
| 132 | 
            +
                      end
         | 
| 133 | 
            +
                    end
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
              end
         | 
| 137 | 
            +
            end
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            require 'sequel'
         | 
| 2 | 
            +
            require 'forwardable'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Rack::Scaffold::Adapters
         | 
| 5 | 
            +
              class Sequel < Base
         | 
| 6 | 
            +
                extend Forwardable
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def_delegators :@klass, :count, :all, :find, :[], :update_timestamp_field
         | 
| 9 | 
            +
                def_delegator :@klass, :create, :create!
         | 
| 10 | 
            +
                def_delegator :@klass, :update, :update!
         | 
| 11 | 
            +
                def_delegator :@klass, :destroy, :destroy!
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                class << self
         | 
| 14 | 
            +
                  def ===(model)
         | 
| 15 | 
            +
                    ::Sequel::Model === model
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def resources(model, options = {})
         | 
| 19 | 
            +
                    model
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def singular
         | 
| 24 | 
            +
                  @klass.name.demodulize.downcase
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def plural
         | 
| 28 | 
            +
                  @klass.table_name
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def paginate(limit, offset)
         | 
| 32 | 
            +
                  @klass.limit(limit, offset)
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def one_to_many_associations
         | 
| 36 | 
            +
                  @klass.all_association_reflections.select{|association| association[:type] == :one_to_many}.collect{|association| association[:name]}
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def timestamps?
         | 
| 40 | 
            +
                  defined?(::Sequel::Plugins::Timestamps) and @klass.plugins.include?(::Sequel::Plugins::Timestamps)
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
            end
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            # -*- encoding: utf-8 -*-
         | 
| 2 | 
            +
            $:.push File.expand_path("../lib", __FILE__)
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Gem::Specification.new do |s|
         | 
| 5 | 
            +
              s.name        = "rack-scaffold_aim"
         | 
| 6 | 
            +
              s.authors     = ["Mattt Thompson"]
         | 
| 7 | 
            +
              s.email       = "m@mattt.me"
         | 
| 8 | 
            +
              s.homepage    = "http://mattt.me"
         | 
| 9 | 
            +
              s.license     = "MIT"
         | 
| 10 | 
            +
              s.version     = "0.1.0"
         | 
| 11 | 
            +
              s.platform    = Gem::Platform::RUBY
         | 
| 12 | 
            +
              s.summary     = "Rack::Scaffold"
         | 
| 13 | 
            +
              s.description = "Automatically generate RESTful CRUD services"
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              s.add_dependency "rack", "~> 1.4"
         | 
| 16 | 
            +
              s.add_dependency "rack-contrib", "~> 1.1"
         | 
| 17 | 
            +
              s.add_dependency "sinatra", "~> 1.4"
         | 
| 18 | 
            +
              s.add_dependency "sinatra-contrib", "~> 1.4"
         | 
| 19 | 
            +
              s.add_dependency "sinatra-param", "~> 0.1"
         | 
| 20 | 
            +
              s.add_dependency "activesupport", ">= 3.0"
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              s.add_development_dependency "rake"
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              s.files         = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|example|log|pkg|script|spec|test|vendor)/ }
         | 
| 25 | 
            +
              s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
         | 
| 26 | 
            +
              s.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
         | 
| 27 | 
            +
              s.require_paths = ["lib"]
         | 
| 28 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,151 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: rack-scaffold_aim
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Mattt Thompson
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2013-11-19 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: rack
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - ~>
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '1.4'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - ~>
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '1.4'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: rack-contrib
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - ~>
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '1.1'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - ~>
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '1.1'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: sinatra
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - ~>
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '1.4'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - ~>
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '1.4'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: sinatra-contrib
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - ~>
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '1.4'
         | 
| 62 | 
            +
              type: :runtime
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - ~>
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '1.4'
         | 
| 69 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            +
              name: sinatra-param
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
                requirements:
         | 
| 73 | 
            +
                - - ~>
         | 
| 74 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                    version: '0.1'
         | 
| 76 | 
            +
              type: :runtime
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - ~>
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: '0.1'
         | 
| 83 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            +
              name: activesupport
         | 
| 85 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - ! '>='
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: '3.0'
         | 
| 90 | 
            +
              type: :runtime
         | 
| 91 | 
            +
              prerelease: false
         | 
| 92 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
                requirements:
         | 
| 94 | 
            +
                - - ! '>='
         | 
| 95 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            +
                    version: '3.0'
         | 
| 97 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 98 | 
            +
              name: rake
         | 
| 99 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 100 | 
            +
                requirements:
         | 
| 101 | 
            +
                - - ! '>='
         | 
| 102 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 103 | 
            +
                    version: '0'
         | 
| 104 | 
            +
              type: :development
         | 
| 105 | 
            +
              prerelease: false
         | 
| 106 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 107 | 
            +
                requirements:
         | 
| 108 | 
            +
                - - ! '>='
         | 
| 109 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            +
                    version: '0'
         | 
| 111 | 
            +
            description: Automatically generate RESTful CRUD services
         | 
| 112 | 
            +
            email: m@mattt.me
         | 
| 113 | 
            +
            executables: []
         | 
| 114 | 
            +
            extensions: []
         | 
| 115 | 
            +
            extra_rdoc_files: []
         | 
| 116 | 
            +
            files:
         | 
| 117 | 
            +
            - ./Gemfile
         | 
| 118 | 
            +
            - ./lib/rack/scaffold/adapters/active_record.rb
         | 
| 119 | 
            +
            - ./lib/rack/scaffold/adapters/core_data.rb
         | 
| 120 | 
            +
            - ./lib/rack/scaffold/adapters/sequel.rb
         | 
| 121 | 
            +
            - ./lib/rack/scaffold/adapters.rb
         | 
| 122 | 
            +
            - ./lib/rack/scaffold.rb
         | 
| 123 | 
            +
            - ./LICENSE
         | 
| 124 | 
            +
            - ./rack-scaffold_aim.gemspec
         | 
| 125 | 
            +
            - ./Rakefile
         | 
| 126 | 
            +
            - ./README.md
         | 
| 127 | 
            +
            homepage: http://mattt.me
         | 
| 128 | 
            +
            licenses:
         | 
| 129 | 
            +
            - MIT
         | 
| 130 | 
            +
            metadata: {}
         | 
| 131 | 
            +
            post_install_message: 
         | 
| 132 | 
            +
            rdoc_options: []
         | 
| 133 | 
            +
            require_paths:
         | 
| 134 | 
            +
            - lib
         | 
| 135 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 136 | 
            +
              requirements:
         | 
| 137 | 
            +
              - - ! '>='
         | 
| 138 | 
            +
                - !ruby/object:Gem::Version
         | 
| 139 | 
            +
                  version: '0'
         | 
| 140 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 141 | 
            +
              requirements:
         | 
| 142 | 
            +
              - - ! '>='
         | 
| 143 | 
            +
                - !ruby/object:Gem::Version
         | 
| 144 | 
            +
                  version: '0'
         | 
| 145 | 
            +
            requirements: []
         | 
| 146 | 
            +
            rubyforge_project: 
         | 
| 147 | 
            +
            rubygems_version: 2.1.11
         | 
| 148 | 
            +
            signing_key: 
         | 
| 149 | 
            +
            specification_version: 4
         | 
| 150 | 
            +
            summary: Rack::Scaffold
         | 
| 151 | 
            +
            test_files: []
         |