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 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: []