sinatra-rest-api 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.rubocop.yml +34 -0
  4. data/Gemfile +20 -0
  5. data/Gemfile.lock +92 -0
  6. data/LICENSE +5 -0
  7. data/README.md +135 -0
  8. data/examples/active_record/Gemfile +15 -0
  9. data/examples/active_record/Gemfile.lock +75 -0
  10. data/examples/active_record/Rakefile +8 -0
  11. data/examples/active_record/app.rb +117 -0
  12. data/examples/active_record/config.ru +8 -0
  13. data/examples/active_record/database.yml +7 -0
  14. data/examples/active_record/db/migrate/001_create_authors.rb +11 -0
  15. data/examples/active_record/db/migrate/002_create_categories.rb +10 -0
  16. data/examples/active_record/db/migrate/003_create_books.rb +16 -0
  17. data/examples/active_record/db/migrate/004_create_tags.rb +10 -0
  18. data/examples/active_record/db/migrate/005_create_books_tags.rb +11 -0
  19. data/examples/active_record/db/migrate/006_create_chapters.rb +12 -0
  20. data/examples/active_record/db/schema.rb +61 -0
  21. data/examples/active_record/db/seeds.rb +0 -0
  22. data/examples/activeresource/Gemfile +10 -0
  23. data/examples/activeresource/Gemfile.lock +68 -0
  24. data/examples/activeresource/app.rb +125 -0
  25. data/examples/activeresource/config.ru +9 -0
  26. data/examples/activeresource/layout.slim +78 -0
  27. data/examples/misc/set_verb_options.rb +12 -0
  28. data/examples/mongoid/Gemfile +11 -0
  29. data/examples/mongoid/Gemfile.lock +68 -0
  30. data/examples/mongoid/Rakefile +3 -0
  31. data/examples/mongoid/app.rb +144 -0
  32. data/examples/mongoid/config.ru +8 -0
  33. data/examples/mongoid/database.yml +13 -0
  34. data/examples/mongoid/lib/tasks/db.rake +79 -0
  35. data/examples/ng-admin/index.html +168 -0
  36. data/examples/ng-admin/package.json +14 -0
  37. data/examples/sequel/Gemfile +14 -0
  38. data/examples/sequel/Gemfile.lock +54 -0
  39. data/examples/sequel/Rakefile +3 -0
  40. data/examples/sequel/app.rb +138 -0
  41. data/examples/sequel/config.ru +9 -0
  42. data/examples/sequel/database.yml +7 -0
  43. data/examples/sequel/db/migrate/001_create_authors.rb +15 -0
  44. data/examples/sequel/db/migrate/002_create_categories.rb +14 -0
  45. data/examples/sequel/db/migrate/003_create_books.rb +20 -0
  46. data/examples/sequel/db/migrate/004_create_tags.rb +14 -0
  47. data/examples/sequel/db/migrate/005_create_books_tags.rb +15 -0
  48. data/examples/sequel/db/migrate/006_create_chapters.rb +16 -0
  49. data/examples/sequel/lib/tasks/db.rake +44 -0
  50. data/lib/sinatra-rest-api/actions.rb +2 -1
  51. data/sinatra-rest-api.gemspec +24 -0
  52. data/spec/action_create_spec.rb +164 -0
  53. data/spec/action_delete_spec.rb +32 -0
  54. data/spec/action_list_spec.rb +31 -0
  55. data/spec/action_read_spec.rb +28 -0
  56. data/spec/action_update_spec.rb +98 -0
  57. data/spec/app_active_record_spec.rb +53 -0
  58. data/spec/app_mongoid_spec.rb +46 -0
  59. data/spec/app_sequel_spec.rb +45 -0
  60. data/spec/spec_helper.rb +59 -0
  61. metadata +70 -3
@@ -0,0 +1,12 @@
1
+ require 'sinatra/base'
2
+
3
+ class TestApp < Sinatra::Application
4
+ options '*' do
5
+ response.headers['Access-Control-Allow-Origin'] = '*'
6
+ response.headers['Access-Control-Allow-Methods'] = 'HEAD,GET,PUT,DELETE,OPTIONS'
7
+ response.headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Cache-Control, Accept'
8
+ halt 200
9
+ end
10
+
11
+ run! if app_file == $PROGRAM_NAME # = $0 ## starts the server if executed directly by ruby
12
+ end
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'pry'
4
+
5
+ gem 'sinatra'
6
+ gem 'sinatra-contrib'
7
+ gem 'thin'
8
+
9
+ gem 'mongoid'
10
+
11
+ gem 'sinatra-rest-api' #, path: '../../../sinatra-rest-api'
@@ -0,0 +1,68 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ activemodel (5.1.4)
5
+ activesupport (= 5.1.4)
6
+ activesupport (5.1.4)
7
+ concurrent-ruby (~> 1.0, >= 1.0.2)
8
+ i18n (~> 0.7)
9
+ minitest (~> 5.1)
10
+ tzinfo (~> 1.1)
11
+ backports (3.8.0)
12
+ bson (4.2.2)
13
+ coderay (1.1.2)
14
+ concurrent-ruby (1.0.5)
15
+ daemons (1.2.4)
16
+ eventmachine (1.2.5)
17
+ i18n (0.8.6)
18
+ method_source (0.9.0)
19
+ minitest (5.10.3)
20
+ mongo (2.4.3)
21
+ bson (>= 4.2.1, < 5.0.0)
22
+ mongoid (6.2.1)
23
+ activemodel (~> 5.1)
24
+ mongo (>= 2.4.1, < 3.0.0)
25
+ multi_json (1.12.2)
26
+ mustermann (1.0.1)
27
+ pry (0.11.1)
28
+ coderay (~> 1.1.0)
29
+ method_source (~> 0.9.0)
30
+ rack (2.0.3)
31
+ rack-protection (2.0.0)
32
+ rack
33
+ sinatra (2.0.0)
34
+ mustermann (~> 1.0)
35
+ rack (~> 2.0)
36
+ rack-protection (= 2.0.0)
37
+ tilt (~> 2.0)
38
+ sinatra-contrib (2.0.0)
39
+ backports (>= 2.0)
40
+ multi_json
41
+ mustermann (~> 1.0)
42
+ rack-protection (= 2.0.0)
43
+ sinatra (= 2.0.0)
44
+ tilt (>= 1.3, < 3)
45
+ sinatra-rest-api (0.1.2)
46
+ sinatra (~> 2.0, > 1.4.0)
47
+ thin (1.7.2)
48
+ daemons (~> 1.0, >= 1.0.9)
49
+ eventmachine (~> 1.0, >= 1.0.4)
50
+ rack (>= 1, < 3)
51
+ thread_safe (0.3.6)
52
+ tilt (2.0.8)
53
+ tzinfo (1.2.3)
54
+ thread_safe (~> 0.1)
55
+
56
+ PLATFORMS
57
+ ruby
58
+
59
+ DEPENDENCIES
60
+ mongoid
61
+ pry
62
+ sinatra
63
+ sinatra-contrib
64
+ sinatra-rest-api
65
+ thin
66
+
67
+ BUNDLED WITH
68
+ 1.15.4
@@ -0,0 +1,3 @@
1
+ # $VERBOSE = true
2
+
3
+ Dir.glob( 'lib/tasks/*.rake' ).each { |r| load r }
@@ -0,0 +1,144 @@
1
+ # require 'pry'
2
+
3
+ require 'json'
4
+ require 'yaml'
5
+
6
+ require 'sinatra/base'
7
+
8
+ require 'mongoid'
9
+
10
+ require 'sinatra-rest-api'
11
+
12
+ # MongoidTest App
13
+ module MongoidTest
14
+ env = ENV['RACK_ENV'] || 'development'
15
+
16
+ Mongoid.load!( File.expand_path( '../database.yml', __FILE__ ), env.to_sym )
17
+
18
+ # An author
19
+ class Author
20
+ include Mongoid::Document
21
+ include Mongoid::Timestamps
22
+
23
+ field :name, type: String
24
+ field :email, type: String
25
+
26
+ has_many :books
27
+ # has_many :books, inverse_of: :author
28
+
29
+ validates :name, presence: true
30
+ validates :email, presence: true, uniqueness: true
31
+ end
32
+
33
+ # A book
34
+ class Book
35
+ include Mongoid::Document
36
+ include Mongoid::Timestamps
37
+
38
+ # Fields
39
+ field :title, type: String
40
+ field :description, type: String
41
+ field :pages, type: Integer
42
+ field :price, type: Float
43
+ field :dt, type: DateTime
44
+
45
+ # Relations
46
+ belongs_to :category, optional: true # optional disables required relations
47
+ belongs_to :author, optional: true
48
+ has_and_belongs_to_many :tags
49
+ has_many :chapters
50
+
51
+ accepts_nested_attributes_for :category
52
+ accepts_nested_attributes_for :author
53
+ accepts_nested_attributes_for :tags
54
+
55
+ # has_one :author, inverse_of: :books
56
+ # has_one :category, inverse_of: :book
57
+
58
+ # embeds_one :author
59
+ # embeds_one :category
60
+
61
+ validates :title, presence: true
62
+ end
63
+
64
+ # A category
65
+ class Category
66
+ include Mongoid::Document
67
+ include Mongoid::Timestamps
68
+
69
+ field :name, type: String
70
+
71
+ has_many :books
72
+ # has_many :books, inverse_of: :category
73
+
74
+ # embedded_in :book
75
+ end
76
+
77
+ # A tag
78
+ class Tag
79
+ include Mongoid::Document
80
+ include Mongoid::Timestamps
81
+
82
+ field :name, type: String
83
+
84
+ has_and_belongs_to_many :books
85
+ end
86
+
87
+ # A chapter
88
+ class Chapter
89
+ include Mongoid::Document
90
+ include Mongoid::Timestamps
91
+
92
+ belongs_to :book
93
+
94
+ field :title, type: String
95
+
96
+ validates :title, presence: true
97
+ end
98
+
99
+ ##############################################################################
100
+
101
+ # App main class
102
+ class App < Sinatra::Application
103
+ register Sinatra::RestApi
104
+
105
+ # model Author, actions: { list: { verb: :post }, read: { verb: :post } }
106
+ # model Author, actions: { list: true, read: true }
107
+
108
+ # set :restapi_request_type, :json
109
+
110
+ resource Author, singular: 'writer' # , plural: 'writers'
111
+ resource Book
112
+ resource Category do
113
+ actions [ :list, :read ]
114
+ end
115
+ resource Chapter
116
+ resource Tag # , actions: [ :list, :read ]
117
+
118
+ before do
119
+ content_type :json
120
+ headers( 'Access-Control-Allow-Origin' => '*',
121
+ 'Access-Control-Allow-Methods' => Sinatra::RestApi::Router::VERBS.join(',').upcase,
122
+ 'Access-Control-Allow-Headers' => 'Origin, Content-Type, Accept, Authorization, Token',
123
+ 'Access-Control-Expose-Headers' => 'X-Total-Count' )
124
+ end
125
+
126
+ options '*' do
127
+ response.headers['Access-Control-Allow-Origin'] = '*'
128
+ response.headers['Access-Control-Allow-Methods'] = 'HEAD,GET,PUT,DELETE,OPTIONS'
129
+ response.headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Cache-Control, Accept'
130
+ halt 200
131
+ end
132
+
133
+ get '/' do
134
+ Sinatra::RestApi::Router.list_routes.to_json
135
+ end
136
+
137
+ not_found do
138
+ return if JSON.parse( body[0] ).include?( 'error' ) rescue nil # Error already defined
139
+ [ 404, { message: 'This is not the page you are looking for...' }.to_json ]
140
+ end
141
+
142
+ run! if app_file == $PROGRAM_NAME # = $0 ## starts the server if executed directly by ruby
143
+ end
144
+ end
@@ -0,0 +1,8 @@
1
+ # config.ru (run with rackup)
2
+
3
+ # Launch with:
4
+ # rackup -o 0.0.0.0 -p 4567
5
+
6
+ require './app'
7
+
8
+ run MongoidTest::App
@@ -0,0 +1,13 @@
1
+ development:
2
+ clients:
3
+ default:
4
+ database: sinatra-rest-api
5
+ hosts:
6
+ - localhost:27017
7
+
8
+ test:
9
+ clients:
10
+ default:
11
+ database: sinatra-rest-api_test
12
+ hosts:
13
+ - localhost:27017
@@ -0,0 +1,79 @@
1
+ # require 'mongoid'
2
+
3
+ load 'mongoid/tasks/database.rake'
4
+
5
+ # spec = Gem::Specification.find_by_name 'mongoid'
6
+ # load "#{spec.gem_dir}/lib/mongoid/railties/database.rake"
7
+
8
+ # Rake::Task.define_task( :environment )
9
+
10
+ namespace :db do
11
+ unless Rake::Task.task_defined?('db:drop')
12
+ desc 'Drops all the collections for the database for the current Rails.env'
13
+ task drop: 'mongoid:drop'
14
+ end
15
+
16
+ unless Rake::Task.task_defined?('db:purge')
17
+ desc 'Drop all collections except the system collections'
18
+ task purge: 'mongoid:purge'
19
+ end
20
+
21
+ unless Rake::Task.task_defined?('db:seed')
22
+ # if another ORM has defined db:seed, don't run it twice.
23
+ desc 'Load the seed data from db/seeds.rb'
24
+ task seed: :environment do
25
+ seed_file = File.join(Rails.root, 'db', 'seeds.rb')
26
+ load(seed_file) if File.exist?(seed_file)
27
+ end
28
+ end
29
+
30
+ unless Rake::Task.task_defined?('db:setup')
31
+ desc 'Create the database, and initialize with the seed data'
32
+ task setup: [ 'db:create', 'mongoid:create_indexes', 'db:seed' ]
33
+ end
34
+
35
+ unless Rake::Task.task_defined?('db:reset')
36
+ desc 'Delete data and loads the seeds'
37
+ task reset: [ 'db:drop', 'db:seed' ]
38
+ end
39
+
40
+ unless Rake::Task.task_defined?('db:create')
41
+ task create: :environment do
42
+ # noop
43
+ end
44
+ end
45
+
46
+ unless Rake::Task.task_defined?('db:migrate')
47
+ task migrate: :environment do
48
+ # noop
49
+ end
50
+ end
51
+
52
+ unless Rake::Task.task_defined?('db:schema:load')
53
+ namespace :schema do
54
+ task :load do
55
+ # noop
56
+ end
57
+ end
58
+ end
59
+
60
+ unless Rake::Task.task_defined?('db:test:prepare')
61
+ namespace :test do
62
+ task prepare: 'mongoid:create_indexes'
63
+ end
64
+ end
65
+
66
+ unless Rake::Task.task_defined?('db:create_indexes')
67
+ task create_indexes: 'mongoid:create_indexes'
68
+ end
69
+
70
+ unless Rake::Task.task_defined?('db:remove_indexes')
71
+ task remove_indexes: 'mongoid:remove_indexes'
72
+ end
73
+
74
+ namespace :mongoid do
75
+ task :load_models do
76
+ ::Rails.application.eager_load! if defined?(::Rails)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,168 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Test ng-admin</title>
5
+ <link rel="stylesheet" href="node_modules/ng-admin/build/ng-admin.min.css">
6
+ </head>
7
+ <body ng-app="myApp" ng-strict-di>
8
+ <!-- <script src="node_modules/angular/angular.js"></script>
9
+ <script src="ng-admin/src/javascripts/ng-admin.js"></script> -->
10
+ <script src="node_modules/ng-admin/build/ng-admin.min.js"></script>
11
+ <!-- <script src="node_modules/ng-admin/lib/javascripts/ng-admin.js"></script> -->
12
+ <div ui-view="ng-admin"></div>
13
+ <script type="text/javascript">
14
+ var LIMIT = 10;
15
+
16
+ var entities = {
17
+ 'writers': 'writer',
18
+ 'books': 'book',
19
+ 'categories': 'category',
20
+ 'chapters': 'chapter',
21
+ 'tags': 'tag'
22
+ };
23
+ var myApp = angular.module( 'myApp', [ 'ng-admin' ] );
24
+ myApp.config( [ 'NgAdminConfigurationProvider', function( NgAdminConfigurationProvider ) {
25
+ var nga = NgAdminConfigurationProvider;
26
+ // create an admin application
27
+ var admin = nga.application( 'My First Admin' ).baseApiUrl( 'http://127.0.0.1:3000/' );
28
+
29
+ var authors = nga.entity('writers');
30
+ var books = nga.entity('books');
31
+ var categories = nga.entity('categories' ).readOnly();
32
+ var chapters = nga.entity('chapters');
33
+ var tags = nga.entity('tags');
34
+
35
+ // Authors
36
+ authors.listView().fields([
37
+ nga.field( 'id' ),
38
+ nga.field( 'name' ).isDetailLink( true ),
39
+ nga.field( 'email' )
40
+ ]).listActions( [ 'show', 'delete' ] ).perPage( LIMIT );
41
+ authors.showView().fields([
42
+ nga.field( 'name' ),
43
+ nga.field( 'email' ),
44
+ nga.field( 'books', 'embedded_list' ).targetEntity( books ).targetFields( [ nga.field( 'title' ) ] )
45
+ ]);
46
+ authors.creationView().fields([
47
+ nga.field( 'name' ),
48
+ nga.field( 'email' )
49
+ ]);
50
+ authors.editionView().fields( authors.creationView().fields() );
51
+
52
+ // Books
53
+ books.listView().fields([
54
+ nga.field( 'id' ),
55
+ nga.field( 'title' ).isDetailLink(true),
56
+ // nga.field( 'description', 'text' ),
57
+ nga.field( 'pages', 'number' ),
58
+ nga.field( 'price', 'number' ).format( '$0,0.00' ),
59
+ // nga.field( 'dt', 'date' ),
60
+ nga.field( 'author_id', 'reference' ).targetEntity( authors ).targetField( nga.field( 'name' ) ),
61
+ nga.field( 'category_id', 'reference' ).targetEntity( categories ).targetField( nga.field( 'name' ) )
62
+ ]).listActions( [ 'show', 'delete' ] ).perPage( LIMIT );
63
+ books.showView().fields([
64
+ // nga.field( 'id' ),
65
+ nga.field( 'title' ).isDetailLink( true ),
66
+ nga.field( 'description', 'text' ),
67
+ nga.field( 'pages', 'number' ),
68
+ nga.field( 'price', 'number' ).format( '$0,0.00' ),
69
+ // nga.field( 'dt', 'date' ),
70
+ nga.field( 'author_id', 'reference' ).targetEntity( authors ).targetField( nga.field( 'name' ) ),
71
+ nga.field( 'category_id', 'reference' ).targetEntity( categories ).targetField( nga.field( 'name' ) ),
72
+ nga.field( 'chapters', 'embedded_list' ).targetEntity( chapters ).targetFields( [ nga.field( 'title' ) ] ),
73
+ nga.field( 'tags', 'embedded_list' ).targetEntity( tags ).targetFields( [ nga.field( 'name' ) ] )
74
+ ]);
75
+ books.creationView().fields([
76
+ // nga.field( 'id' ),
77
+ nga.field( 'title' ).isDetailLink( true ),
78
+ nga.field( 'description', 'text' ),
79
+ nga.field( 'pages', 'number' ),
80
+ nga.field( 'price', 'number' ).format( '$0,0.00' ),
81
+ // nga.field( 'dt', 'date' ),
82
+ nga.field( 'author_id', 'reference' ).targetEntity( authors ).targetField( nga.field( 'name' ) ),
83
+ nga.field( 'category_id', 'reference' ).targetEntity( categories ).targetField( nga.field( 'name' ) ),
84
+ // nga.field( 'chapters', 'referenced_list' ).targetEntity( chapters ).targetReferenceField( 'book_id' ).targetFields( [ nga.field( 'id' ), nga.field( 'title' ) ] ) // NO
85
+
86
+ // nga.field( 'chapters', 'embedded_list' ).targetEntity( chapters ).targetFields( [ nga.field( 'title' ) ] ) // NO
87
+
88
+ // nga.field( 'chapters', 'reference_many' ).targetEntity( chapters ).targetField( nga.field( 'title' ) )
89
+ // .singleApiCall( function( ids ) { return {'id': ids }; })
90
+ // nga.field( 'chapters', 'reference_many' ).targetEntity( chapters ).targetField( nga.field( 'title' ) )
91
+ // nga.field( 'chapters', 'reference_many' ).targetEntity( chapters ).targetField( nga.field( 'title' ) ).singleApiCall( function( tagIds ) {
92
+ // return { '_where': "['id IN ?',['1','2']]" };
93
+ // })
94
+ // _where=["id IN ?",["1","2"]]
95
+ ]);
96
+ books.editionView().fields( books.creationView().fields() );
97
+
98
+ // Categories
99
+ categories.listView().fields([
100
+ nga.field( 'id' ),
101
+ nga.field( 'name' ).isDetailLink( true )
102
+ ]).listActions( [ 'show' ] ).perPage( LIMIT );
103
+ categories.showView().fields([
104
+ nga.field( 'name' ),
105
+ nga.field( 'books', 'embedded_list' ).targetEntity( books ).targetFields( [ nga.field( 'title' ) ] )
106
+ ]);
107
+
108
+ // Chapters
109
+ chapters.listView().fields([
110
+ nga.field( 'id' ),
111
+ nga.field( 'title' ).isDetailLink( true )
112
+ ]).listActions( [ 'show', 'delete' ] ).perPage( LIMIT );
113
+ chapters.showView().fields([
114
+ nga.field( 'id' ),
115
+ nga.field( 'title' ),
116
+ nga.field( 'book_id', 'reference' ).targetEntity( books ).targetField( nga.field( 'title' ) )
117
+ ]);
118
+ chapters.creationView().fields([
119
+ nga.field( 'title' )
120
+ ]);
121
+ chapters.editionView().fields( chapters.creationView().fields() );
122
+
123
+ // Tags
124
+ tags.listView().fields([
125
+ nga.field( 'id' ),
126
+ nga.field( 'name' ).isDetailLink( true )
127
+ ]).listActions( [ 'show', 'delete' ] ).perPage( LIMIT );
128
+ tags.showView().fields([
129
+ nga.field( 'id' ),
130
+ nga.field( 'name' ),
131
+ nga.field( 'books', 'embedded_list' ).targetEntity( books ).targetFields( [ nga.field( 'title' ) ] )
132
+ ]);
133
+ tags.creationView().fields([
134
+ nga.field( 'name' )
135
+ ]);
136
+ tags.editionView().fields( tags.creationView().fields() );
137
+
138
+ admin.addEntity( authors );
139
+ admin.addEntity( books );
140
+ admin.addEntity( categories );
141
+ admin.addEntity( chapters );
142
+ admin.addEntity( tags );
143
+
144
+ nga.configure( admin );
145
+ }]);
146
+
147
+ myApp.config( [ 'RestangularProvider', function( RestangularProvider ) {
148
+ RestangularProvider.addFullRequestInterceptor( function( element, operation, what, url, headers, params, httpConfig ) {
149
+ if( operation == 'getList' ) {
150
+ if( params._page && params._perPage ) {
151
+ params.limit = params._perPage;
152
+ params.offset = ( params._page - 1 )* params.limit;
153
+ delete params._page;
154
+ delete params._perPage;
155
+ }
156
+ }
157
+ else if( operation == 'post' || operation == 'put' ) {
158
+ ret = {}
159
+ ret[entities[what]] = element;
160
+ return { element: ret }
161
+ }
162
+ // return { element: element };
163
+ return { params: params };
164
+ });
165
+ }]);
166
+ </script>
167
+ </body>
168
+ </html>