rack-scaffold 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 62eaf4d70bb8f5ffe5b174b33713b1ebeae39bc5
4
+ data.tar.gz: 60452a102b8b505f53dfdc1a6ef2b32a56931ccf
5
+ SHA512:
6
+ metadata.gz: 5f9b7caa071e3813f3b0115e68db9ada8c116f57d5283594d3c6811a84faed2781a745ea1866dea66d722d3d9121058cd6f4f78c16c8dcf5009c3990fd51604d
7
+ data.tar.gz: 36a22a42bbd975c11cfec13ca44f239774061b8a8a7115eeda608bf96031b84ec74813a7eddc113d72cbac3d1ec125cb1f1e43ee2171c4c578dae97ac351bc09
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rack-scaffold (0.0.1)
5
+ activesupport (>= 3.0)
6
+ rack (~> 1.4)
7
+ rack-contrib (~> 1.1)
8
+ sinatra (~> 1.3)
9
+ sinatra-param (~> 0.1)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ activesupport (3.2.13)
15
+ i18n (= 0.6.1)
16
+ multi_json (~> 1.0)
17
+ i18n (0.6.1)
18
+ multi_json (1.7.2)
19
+ rack (1.5.2)
20
+ rack-contrib (1.1.0)
21
+ rack (>= 0.9.1)
22
+ rack-protection (1.5.0)
23
+ rack
24
+ rake (10.0.4)
25
+ sinatra (1.4.2)
26
+ rack (~> 1.5, >= 1.5.2)
27
+ rack-protection (~> 1.4)
28
+ tilt (~> 1.3, >= 1.3.4)
29
+ sinatra-param (0.1.2)
30
+ sinatra (~> 1.3)
31
+ tilt (1.3.7)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ rack-scaffold!
38
+ rake
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.
@@ -0,0 +1,53 @@
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 being actively developed for inclusion in the next 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
+ gem 'sequel'
21
+ gem 'core_data'
22
+
23
+ gem 'unicorn'
24
+ gem 'pg'
25
+ ```
26
+
27
+ ### config.ru
28
+
29
+ ```ruby
30
+ require 'sequel'
31
+ require 'core_data'
32
+ require 'rack/scaffold'
33
+
34
+ DB = Sequel.connect(ENV['DATABASE_URL'])
35
+
36
+ run Rack::Scaffold model: './Example.xcdatamodeld', only: [:create, :read]
37
+ ```
38
+
39
+ ## Examples
40
+
41
+ An example web API using a Core Data model can be found the `/example` directory.
42
+
43
+ ## Contact
44
+
45
+ Mattt Thompson
46
+
47
+ - http://github.com/mattt
48
+ - http://twitter.com/mattt
49
+ - m@mattt.me
50
+
51
+ ## License
52
+
53
+ Rack::Scaffold is available under the MIT license. See the LICENSE file for more info.
@@ -0,0 +1,10 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ gemspec = eval(File.read("rack-scaffold.gemspec"))
5
+
6
+ task :build => "#{gemspec.full_name}.gem"
7
+
8
+ file "#{gemspec.full_name}.gem" => gemspec.files + ["rack-scaffold.gemspec"] do
9
+ system "gem build rack-scaffold.gemspec"
10
+ end
@@ -0,0 +1,121 @@
1
+ require 'rack'
2
+ require 'rack/contrib'
3
+ require 'sinatra/base'
4
+ require 'sinatra/param'
5
+
6
+ require 'rack/scaffold/version'
7
+ require 'rack/scaffold/adapters'
8
+
9
+ module Rack
10
+ class Scaffold
11
+ ACTIONS = [:create, :read, :update, :destroy]
12
+
13
+ def initialize(options = {})
14
+ raise ArgumentError, "Missing option: :model or :models" unless options[:model] or options[:models]
15
+
16
+ if options[:models] and options[:models].kind_of?(Array)
17
+ @app = Rack::Cascade.new(options.delete(:models).collect{|model| self.class.new(options.dup.merge({model: model}))}) and return
18
+ end
19
+
20
+ @app = Class.new(Sinatra::Base) do
21
+ use Rack::PostBodyContentTypeParser
22
+ helpers Sinatra::Param
23
+
24
+ before do
25
+ content_type :json
26
+ end
27
+
28
+ disable :raise_errors, :show_exceptions
29
+ end
30
+
31
+ @actions = (options[:only] || ACTIONS) - (options[:except] || [])
32
+
33
+ @adapter = Rack::Scaffold.adapters.detect{|adapter| adapter === options[:model]}
34
+ raise "No suitable adapters found for #{options[:model]} in #{Rack::Scaffold.adapters}" unless @adapter
35
+
36
+ resources = Array(@adapter.resources(options[:model]))
37
+ resources.each do |resource|
38
+ @app.instance_eval do
39
+ post "/#{resource.plural}/?" do
40
+ if record = resource.create!(params)
41
+ status 201
42
+ {entity.name.downcase => record}.to_json
43
+ else
44
+ status 406
45
+ {errors: record.errors}.to_json
46
+ end
47
+ end
48
+ end if @actions.include?(:create)
49
+
50
+ @app.instance_eval do
51
+ get "/#{resource.plural}/?" do
52
+ if params[:page] or params[:per_page]
53
+ param :page, Integer, default: 1, min: 1
54
+ param :per_page, Integer, default: 100, in: (1..100)
55
+
56
+ {
57
+ "#{resource.plural}" => resource.paginate(params[:per_page], (params[:page] - 1) * params[:per_page]),
58
+ page: params[:page],
59
+ total: resource.count
60
+ }.to_json
61
+ else
62
+ param :limit, Integer, default: 100, in: (1..100)
63
+ param :offset, Integer, default: 0, min: 0
64
+
65
+ {
66
+ "#{resource.plural}" => resource.paginate(params[:limit], params[:offset])
67
+ }.to_json
68
+ end
69
+ end
70
+
71
+ get "/#{resource.plural}/:id/?" do
72
+ record = resource[params[:id]] or halt 404
73
+ {entity.name.downcase => record}.to_json
74
+ end
75
+ end if @actions.include?(:read)
76
+
77
+ @app.instance_eval do
78
+ put "/#{resource.plural}/:id/?" do
79
+ record = resource[params[:id]] or halt 404
80
+ if record.update!(params)
81
+ status 200
82
+ {entity.name.downcase => record}.to_json
83
+ else
84
+ status 406
85
+ {errors: record.errors}.to_json
86
+ end
87
+ end
88
+ end if @actions.include?(:update)
89
+
90
+ @app.instance_eval do
91
+ delete "/#{resource.plural}/:id/?" do
92
+ record = resource[params[:id]] or halt 404
93
+ if record.destroy
94
+ status 200
95
+ else
96
+ status 406
97
+ {errors: record.errors}.to_json
98
+ end
99
+ end
100
+ end if @actions.include?(:delete)
101
+
102
+ # @app.instance_eval do
103
+ # entity.relationships.each do |relationship|
104
+ # next unless relationship.to_many?
105
+
106
+ # get "/#{resource.plural}/:id/#{relationship.name}/?" do
107
+ # {relationship.name => resource[params[:id]].send(relationship.name)}.to_json
108
+ # end
109
+ # end
110
+ # end
111
+ end
112
+ end
113
+
114
+ def call(env)
115
+ @app.call(env)
116
+ end
117
+ end
118
+
119
+ module Models
120
+ end
121
+ end
@@ -0,0 +1,78 @@
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)
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 find(options = {})
57
+ raise NotImplementedError
58
+ end
59
+
60
+ def create!(attributes = {})
61
+ raise NotImplementedError
62
+ end
63
+
64
+ def update!(attributes = {})
65
+ raise NotImplementedError
66
+ end
67
+
68
+ def destroy!
69
+ raise NotImplementedError
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ require 'rack/scaffold/adapters/active_record' if defined?(ActiveRecord::Base)
77
+ require 'rack/scaffold/adapters/sequel' if defined?(Sequel)
78
+ require 'rack/scaffold/adapters/core_data' if defined?(Sequel) and defined?(CoreData)
@@ -0,0 +1,36 @@
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)
16
+ model
17
+ end
18
+ end
19
+
20
+ def singular
21
+ @klass.name.downcase
22
+ end
23
+
24
+ def plural
25
+ @klass.table_name
26
+ end
27
+
28
+ def paginate(limit, offset)
29
+ @klass.limit(limit).offset(offset)
30
+ end
31
+
32
+ def [](id)
33
+ self.find(id)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,114 @@
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)
14
+ model = ::CoreData::DataModel.new(xcdatamodel)
15
+ model.entities.collect{|entity| new(entity)}
16
+ end
17
+ end
18
+
19
+ def initialize(entity)
20
+ klass = Class.new(::Sequel::Model)
21
+ klass.dataset = entity.name.downcase.pluralize.to_sym
22
+
23
+ klass.class_eval do
24
+ self.strict_param_setting = false
25
+ self.raise_on_save_failure = false
26
+
27
+ plugin :json_serializer, naked: true, include: [:url]
28
+ plugin :schema
29
+ plugin :validation_helpers
30
+
31
+ def url
32
+ "/#{self.class.table_name}/#{self[primary_key]}"
33
+ end
34
+
35
+ # entity.relationships.each do |relationship|
36
+ # options = {:class => Rack::Scaffold::Models.const_get(relationship.destination.capitalize)}
37
+
38
+ # if relationship.to_many?
39
+ # one_to_many relationship.name.to_sym, options
40
+ # else
41
+ # many_to_one relationship.name.to_sym, options
42
+ # end
43
+ # end
44
+
45
+ set_schema do
46
+ primary_key :id
47
+
48
+ entity.attributes.each do |attribute|
49
+ next if attribute.transient?
50
+
51
+ options = {
52
+ :null => attribute.optional?,
53
+ :index => attribute.indexed?,
54
+ :default => attribute.default_value
55
+ }
56
+
57
+ type = case attribute.type
58
+ when "Integer 16" then :int2
59
+ when "Integer 32" then :int4
60
+ when "Integer 64" then :int8
61
+ when "Float" then :float4
62
+ when "Double" then :float8
63
+ when "Decimal" then :float8
64
+ when "Date" then :timestamp
65
+ when "Boolean" then :boolean
66
+ when "Binary" then :bytea
67
+ else :varchar
68
+ end
69
+
70
+ column attribute.name.to_sym, type, options
71
+ end
72
+
73
+ entity.relationships.each do |relationship|
74
+ options = {
75
+ :index => true,
76
+ :null => relationship.optional?
77
+ }
78
+
79
+ if not relationship.to_many?
80
+ column "#{relationship.name}_id".to_sym, :integer, options
81
+ end
82
+ end
83
+ end
84
+
85
+ if table_exists?
86
+ missing_columns = schema.columns.reject{|c| columns.include?(c[:name])}
87
+ db.alter_table table_name do
88
+ missing_columns.each do |options|
89
+ add_column options.delete(:name), options.delete(:type), options
90
+ end
91
+ end
92
+ else
93
+ create_table
94
+ end
95
+ end
96
+
97
+ klass.send :define_method, :validate do
98
+ entity.attributes.each do |attribute|
99
+ case attribute.type
100
+ when "Integer 16", "Integer 32", "Integer 64"
101
+ validates_integer attribute.name
102
+ when "Float", "Double", "Decimal"
103
+ validates_numeric attribute.name
104
+ when "String"
105
+ validates_min_length attribute.minimum_value, attribute.name if attribute.minimum_value
106
+ validates_max_length attribute.maximum_value, attribute.name if attribute.maximum_value
107
+ end
108
+ end
109
+ end
110
+
111
+ super(klass)
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,32 @@
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, :[], :create!, :update!, :destroy!
9
+
10
+ class << self
11
+ def ===(model)
12
+ ::Sequel::Model === model
13
+ end
14
+
15
+ def resources(model)
16
+ model
17
+ end
18
+ end
19
+
20
+ def singular
21
+ @klass.name
22
+ end
23
+
24
+ def plural
25
+ @klass.table_name
26
+ end
27
+
28
+ def paginate(limit, offset)
29
+ @klass.limit(limit, offset)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class Scaffold
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rack/scaffold/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rack-scaffold"
7
+ s.authors = ["Mattt Thompson"]
8
+ s.email = "m@mattt.me"
9
+ s.homepage = "http://mattt.me"
10
+ s.license = "MIT"
11
+ s.version = Rack::Scaffold::VERSION
12
+ s.platform = Gem::Platform::RUBY
13
+ s.summary = "Rack::Scaffold"
14
+ s.description = "Automatically generate RESTful CRUD services"
15
+
16
+ s.add_dependency "rack", "~> 1.4"
17
+ s.add_dependency "rack-contrib", "~> 1.1"
18
+ s.add_dependency "sinatra", "~> 1.3"
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,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-scaffold
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mattt Thompson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-23 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.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sinatra-param
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Automatically generate RESTful CRUD services
98
+ email: m@mattt.me
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - ./Gemfile
104
+ - ./Gemfile.lock
105
+ - ./lib/rack/scaffold/adapters/active_record.rb
106
+ - ./lib/rack/scaffold/adapters/core_data.rb
107
+ - ./lib/rack/scaffold/adapters/sequel.rb
108
+ - ./lib/rack/scaffold/adapters.rb
109
+ - ./lib/rack/scaffold/version.rb
110
+ - ./lib/rack/scaffold.rb
111
+ - ./LICENSE
112
+ - ./rack-scaffold.gemspec
113
+ - ./Rakefile
114
+ - ./README.md
115
+ homepage: http://mattt.me
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.0.3
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Rack::Scaffold
139
+ test_files: []