rack-scaffold_aim 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
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: []