introspective_grape 0.2.7 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +14 -5
  3. data/.travis.yml +38 -12
  4. data/CHANGELOG.md +5 -0
  5. data/Gemfile +1 -1
  6. data/gemfiles/2.0.0-Gemfile +3 -3
  7. data/gemfiles/2.2.0-Gemfile +21 -0
  8. data/gemfiles/Gemfile.rails.3.2.22 +13 -0
  9. data/gemfiles/Gemfile.rails.4.1.13 +13 -0
  10. data/gemfiles/Gemfile.rails.4.2.7.1 +13 -0
  11. data/gemfiles/Gemfile.rails.4.2.7.1.new.swagger +12 -0
  12. data/gemfiles/Gemfile.rails.5.0.1 +13 -0
  13. data/gemfiles/Gemfile.rails.master +13 -0
  14. data/introspective_grape.gemspec +2 -3
  15. data/lib/introspective_grape/api.rb +20 -23
  16. data/lib/introspective_grape/camel_snake.rb +1 -1
  17. data/lib/introspective_grape/create_helpers.rb +22 -0
  18. data/lib/introspective_grape/filters.rb +17 -12
  19. data/lib/introspective_grape/traversal.rb +1 -1
  20. data/lib/introspective_grape/version.rb +1 -1
  21. data/lib/introspective_grape.rb +7 -6
  22. data/spec/dummy/Gemfile +6 -7
  23. data/spec/dummy/app/api/dummy/location_api.rb +2 -1
  24. data/spec/dummy/app/api/dummy/user_api.rb +3 -2
  25. data/spec/dummy/app/api/dummy_api.rb +0 -1
  26. data/spec/dummy/app/models/image.rb +3 -0
  27. data/spec/dummy/app/models/role.rb +2 -7
  28. data/spec/dummy/app/models/user.rb +2 -2
  29. data/spec/dummy/config/application.rb +2 -3
  30. data/spec/dummy/config/environments/development.rb +1 -0
  31. data/spec/dummy/config/environments/production.rb +1 -1
  32. data/spec/dummy/config/environments/test.rb +1 -0
  33. data/spec/dummy/config/initializers/assets.rb +1 -1
  34. data/spec/dummy/config/initializers/devise.rb +1 -1
  35. data/spec/dummy/config/initializers/devise_async.rb +1 -1
  36. data/spec/dummy/db/migrate/20141002205024_devise_create_users.rb +4 -2
  37. data/spec/dummy/db/migrate/20141002211055_devise_create_admin_users.rb +2 -2
  38. data/spec/dummy/db/schema.rb +1 -0
  39. data/spec/models/role_spec.rb +1 -7
  40. data/spec/models/user_spec.rb +4 -4
  41. data/spec/requests/project_api_spec.rb +2 -2
  42. data/spec/requests/role_api_spec.rb +6 -5
  43. data/spec/requests/user_api_spec.rb +12 -2
  44. metadata +10 -38
  45. data/spec/dummy/app/models/super_user.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d8dc4cc268e2946259654e5b44eb1841afb3a2a1
4
- data.tar.gz: 4110a9992a24fb544a58c3185abce336694499f0
3
+ metadata.gz: c0478e58629bddd63190fa37d11c63c07872b59b
4
+ data.tar.gz: 5176e6bfb35d31a20ad541a8e7069c365fb18f8b
5
5
  SHA512:
6
- metadata.gz: 9ed73bf254659358fba6e9635ed8bf4d38fbba2349e42a9636bd7a99b38d87f854ee05c6fb3430b94c899f641ebd62f31189a957d86ab1b55de60b3e2fff1872
7
- data.tar.gz: aa5b91683e4f66eb1de93cb08fb537127aa3bc109c4388d6c60b266edbf1ca079527fbcbaa2d024e834e2d1cb3cf66bef6a9757bcd0f1a44e56b69609a9c5734
6
+ metadata.gz: b45bc43f714cf140f1f2903fb075290e209b08f99f32ed656639c49440949c7de5b58ae9dded52af1aff3087690bf8e6e9591e65c2edd122914c82dd1113320f
7
+ data.tar.gz: 9ec43d28db3b0b6a79970270508c636629626800d3438fe2deb56b180cfd2f8ab0a59a7b52f0d86b73a1e62559716d56f1a72a8557eea779066a69589d4d68c7
data/.rubocop.yml CHANGED
@@ -536,10 +536,10 @@ Style/DefWithParentheses:
536
536
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens'
537
537
  Enabled: false
538
538
 
539
- Style/DeprecatedHashMethods:
540
- Description: 'Checks for use of deprecated Hash methods.'
541
- StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key'
542
- Enabled: false
539
+ #Style/DeprecatedHashMethods:
540
+ # Description: 'Checks for use of deprecated Hash methods.'
541
+ # StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key'
542
+ # Enabled: false
543
543
 
544
544
  Style/Documentation:
545
545
  Description: 'Document classes and non-namespace modules.'
@@ -724,7 +724,7 @@ Style/LineEndConcatenation:
724
724
  line end.
725
725
  Enabled: false
726
726
 
727
- Style/MethodCallParentheses:
727
+ Style/MethodCallWithoutArgsParentheses:
728
728
  Description: 'Do not use parentheses for method calls with no arguments.'
729
729
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens'
730
730
  Enabled: false
@@ -810,6 +810,10 @@ Style/Not:
810
810
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not'
811
811
  Enabled: false
812
812
 
813
+ Style/NumericPredicate:
814
+ Exclude:
815
+ - 'spec/**/*'
816
+
813
817
  Style/NumericLiterals:
814
818
  Description: >-
815
819
  Add underscores to large numeric literals to improve their
@@ -1162,3 +1166,8 @@ Style/WordArray:
1162
1166
  Description: 'Use %w or %W for arrays of words.'
1163
1167
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w'
1164
1168
  Enabled: false
1169
+
1170
+ Style/BlockLength:
1171
+ Exclude:
1172
+ - '*.gemspec'
1173
+ - 'spec/**/*'
data/.travis.yml CHANGED
@@ -7,33 +7,59 @@ script:
7
7
  - bundle exec rspec
8
8
 
9
9
  rvm:
10
+ - 2.0.0
11
+ - 2.1.0
12
+ - 2.2.0
10
13
  - 2.3.0
11
14
  - 2.2.6
12
15
  - ruby-head
13
16
  - jruby-head
17
+ gemfile:
18
+ # - gemfiles/Gemfile.rails.3.2.22
19
+ #- gemfiles/Gemfile.rails.4.1.13
20
+ - gemfiles/Gemfile.rails.4.2.7.1
21
+ - gemfiles/Gemfile.rails.4.2.7.1.new.swagger
22
+ - gemfiles/Gemfile.rails.5.0.1
23
+ - gemfiles/Gemfile.rails.master
24
+
14
25
  matrix:
15
- include:
26
+ exclude:
16
27
  # We'll have to back up to grape-swagger 0.11 for earlier rubies.
17
28
  - rvm: 2.0.0
18
- gemfile: gemfiles/2.0.0-Gemfile
29
+ gemfile: gemfiles/Gemfile.rails.4.2.7.1.new.swagger
30
+ - rvm: 2.0.0
31
+ gemfile: gemfiles/Gemfile.rails.5.0.1
32
+ - rvm: 2.0.0
33
+ gemfile: gemfiles/Gemfile.rails.master
34
+ - rvm: 2.1.0
35
+ gemfile: gemfiles/Gemfile.rails.4.2.7.1.new.swagger
36
+ - rvm: 2.1.0
37
+ gemfile: gemfiles/Gemfile.rails.5.0.1
19
38
  - rvm: 2.1.0
20
- gemfile: gemfiles/2.0.0-Gemfile
39
+ gemfile: gemfiles/Gemfile.rails.master
21
40
  - rvm: 2.2.0
22
- gemfile: gemfiles/2.0.0-Gemfile
41
+ gemfile: gemfiles/Gemfile.rails.4.2.7.1.new.swagger
42
+ - rvm: 2.2.0
43
+ gemfile: gemfiles/Gemfile.rails.5.0.1
44
+ - rvm: 2.2.0
45
+ gemfile: gemfiles/Gemfile.rails.master
23
46
  - rvm: jruby-9.0.4.0
24
- gemfile: gemfiles/2.0.0-Gemfile
25
-
47
+ gemfile: gemfiles/Gemfile.rails.4.2.7.1.new.swagger
48
+ - rvm: jruby-9.0.4.0
49
+ gemfile: gemfiles/Gemfile.rails.5.0.1
50
+ - rvm: jruby-9.0.4.0
51
+ gemfile: gemfiles/Gemfile.rails.master
26
52
  allow_failures:
27
53
  - rvm: ruby-head
28
54
  - rvm: jruby-head
29
55
 
30
56
  env:
31
- matrix:
32
- - RAILS=3.2.22
33
- - RAILS=4.1.13
34
- - RAILS=4.2.5.1
35
- - RAILS=5.0.1
36
- - RAILS=master
57
+ #matrix:
58
+ # - RAILS=3.2.22
59
+ # - RAILS=4.1.13
60
+ # - RAILS=4.2.5.1
61
+ # - RAILS=5.0.1
62
+ # - RAILS=master
37
63
  global:
38
64
  - JRUBY_OPTS="-J-Xmx1024m --debug"
39
65
 
data/CHANGELOG.md CHANGED
@@ -1,4 +1,9 @@
1
1
 
2
+ 0.2.8 01/22/2017
3
+ ==============
4
+
5
+ Nested endpoints for creating new associations will now return only the newly created records.
6
+
2
7
  0.2.7 01/18/2017
3
8
  ==============
4
9
 
data/Gemfile CHANGED
@@ -13,5 +13,5 @@ gem 'coveralls', require: false
13
13
  # your gem to rubygems.org.
14
14
 
15
15
  group :development do
16
- # gem 'byebug'
16
+ gem 'byebug'
17
17
  end
@@ -6,9 +6,9 @@ source 'https://rubygems.org'
6
6
 
7
7
  gemspec :path => '../'
8
8
 
9
- gem 'grape', '0.16.2'
10
- gem 'grape-swagger', '~> 0.11.0'
11
- gem 'nokogiri', '~> 1.6.8'
9
+ gem 'grape', '< 0.17'
10
+ gem 'grape-swagger', '< 0.12.0'
11
+ gem 'nokogiri', '< 1.6.9'
12
12
  gem 'coveralls', require: false
13
13
 
14
14
  # Declare any dependencies that are still in development here instead of in
@@ -0,0 +1,21 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Declare your gem's dependencies in introspective_grape.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+
7
+ gemspec :path => '../'
8
+
9
+ gem 'grape'
10
+ gem 'grape-swagger'
11
+ gem 'coveralls', require: false
12
+
13
+ #
14
+ # Declare any dependencies that are still in development here instead of in
15
+ # your gemspec. These might include edge Rails or gems from your path or
16
+ # Git. Remember to move these dependencies to your gemspec before releasing
17
+ # your gem to rubygems.org.
18
+
19
+ group :development do
20
+ # gem 'byebug'
21
+ end
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '../'
4
+
5
+ gem 'rails', '3.2.22'
6
+ gem 'grape', '< 0.17'
7
+ gem 'grape-swagger', '< 0.12.0'
8
+ gem 'nokogiri', '< 1.6.9'
9
+ gem 'coveralls', require: false
10
+
11
+ group :development,:test do
12
+ gem 'rspec'
13
+ end
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '../'
4
+
5
+ gem 'rails', '4.1.13'
6
+ gem 'grape', '< 0.17'
7
+ gem 'grape-swagger', '< 0.12.0'
8
+ gem 'nokogiri', '< 1.6.9'
9
+ gem 'coveralls', require: false
10
+
11
+ group :development,:test do
12
+ gem 'rspec'
13
+ end
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '../'
4
+
5
+ gem 'rails', '4.2.7.1'
6
+ gem 'grape', '< 0.17'
7
+ gem 'grape-swagger', '< 0.12.0'
8
+ gem 'nokogiri', '< 1.6.9'
9
+ gem 'coveralls', require: false
10
+
11
+ group :development,:test do
12
+ gem 'rspec'
13
+ end
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '../'
4
+
5
+ gem 'rails', '4.2.7.1'
6
+ gem 'grape', '> 0.17'
7
+ gem 'grape-swagger', '> 0.12.0'
8
+ gem 'coveralls', require: false
9
+
10
+ group :development,:test do
11
+ gem 'rspec'
12
+ end
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '../'
4
+
5
+ gem 'rails', '5.0.1'
6
+ gem 'grape'
7
+ gem 'grape-swagger'
8
+ gem 'nokogiri'
9
+ gem 'coveralls', require: false
10
+
11
+ group :development,:test do
12
+ gem 'rspec'
13
+ end
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '../'
4
+
5
+ gem 'rails'
6
+ gem 'grape'
7
+ gem 'grape-swagger'
8
+ gem 'nokogiri'
9
+ gem 'coveralls', require: false
10
+
11
+ group :development,:test do
12
+ gem 'rspec'
13
+ end
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
 
21
21
  s.required_ruby_version = '~> 2.0'
22
22
 
23
- s.add_dependency "rails", '>= 3.0.0', '< 5.0.0'
23
+ s.add_dependency "rails", '>= 3.0.0'
24
24
 
25
25
  s.add_dependency 'grape' #, '~> 0.16.2'
26
26
  s.add_dependency 'grape-entity' #, '< 0.5.0'
@@ -40,11 +40,10 @@ Gem::Specification.new do |s|
40
40
  #s.add_development_dependency "byebug"
41
41
  s.add_development_dependency "rspec-rails", '>= 3.0'
42
42
  s.add_development_dependency 'devise'
43
- s.add_development_dependency 'devise-async'
43
+ #s.add_development_dependency 'devise-async'
44
44
  s.add_development_dependency 'paperclip', '< 5.0'
45
45
  s.add_development_dependency 'machinist'
46
46
  s.add_development_dependency 'simplecov'
47
47
  s.add_development_dependency 'rufus-mnemo'
48
- s.add_development_dependency "activerecord-tableless", "~> 1.0"
49
48
 
50
49
  end
@@ -8,6 +8,7 @@ end
8
8
  module IntrospectiveGrape
9
9
  class API < Grape::API
10
10
  extend IntrospectiveGrape::Helpers
11
+ extend IntrospectiveGrape::CreateHelpers
11
12
  extend IntrospectiveGrape::Filters
12
13
  extend IntrospectiveGrape::Traversal
13
14
  extend IntrospectiveGrape::Doc
@@ -56,16 +57,18 @@ module IntrospectiveGrape
56
57
  self.send(IntrospectiveGrape::API.authentication_method(self))
57
58
  end
58
59
 
59
- child.before_validation do
60
- # We have to snake case the Rack params then re-assign @params to the
61
- # request.params, because of the I-think-very-goofy-and-inexplicable
62
- # way Grape interacts with both independently of each other
63
- (params.try(:with_snake_keys)||{}).each do |k,v|
64
- request.delete_param(k.camelize(:lower))
65
- request.update_param(k, v)
60
+ if IntrospectiveGrape.config.camelize_parameters
61
+ child.before_validation do
62
+ # We have to snake case the Rack params then re-assign @params to the
63
+ # request.params, because of the I-think-very-goofy-and-inexplicable
64
+ # way Grape interacts with both independently of each other
65
+ (params.try(:with_snake_keys)||{}).each do |k,v|
66
+ request.delete_param(k.camelize(:lower))
67
+ request.update_param(k, v)
68
+ end
69
+ @params = request.params
66
70
  end
67
- @params = request.params
68
- end if IntrospectiveGrape.config.camelize_parameters
71
+ end
69
72
  end
70
73
 
71
74
  # We will probably need before and after hooks eventually, but haven't yet...
@@ -183,8 +186,9 @@ module IntrospectiveGrape
183
186
  records = policy_scope( root.model.includes(klass.default_includes(root.model)) )
184
187
 
185
188
  records = klass.apply_filter_params(klass, model, api_params, params, records)
186
- records = records.where( JSON.parse(params[:query]) ) if params[:query].present?
189
+
187
190
  records = records.map{|r| klass.find_leaves( routes, r, params ) }.flatten.compact.uniq
191
+
188
192
  # paginate the records using Kaminari
189
193
  records = paginate(Kaminari.paginate_array(records)) if klass.pagination
190
194
  present records, with: "#{klass}::#{model}Entity".constantize
@@ -206,7 +210,6 @@ module IntrospectiveGrape
206
210
  def define_create(dsl, routes, model, api_params)
207
211
  name = routes.last.name.singularize
208
212
  klass = routes.first.klass
209
- root = routes.first
210
213
  dsl.desc "create a #{name}" do
211
214
  detail klass.create_documentation || "creates a new #{name} record"
212
215
  end
@@ -214,14 +217,8 @@ module IntrospectiveGrape
214
217
  klass.generate_params(self, :create, model, api_params, true)
215
218
  end
216
219
  dsl.post do
217
- if @model
218
- @model.update_attributes( safe_params(params).permit(klass.whitelist) )
219
- else
220
- @model = root.model.new( safe_params(params).permit(klass.whitelist) )
221
- end
222
- authorize @model, :create?
223
- @model.save!
224
- present klass.find_leaves(routes, @model.reload, params), with: "#{klass}::#{model}Entity".constantize
220
+ representation = @model ? klass.add_new_records_to_root_record(self, routes, params, @model) : klass.create_new_record(self, routes, params)
221
+ present representation, with: "#{klass}::#{model}Entity".constantize
225
222
  end
226
223
  end
227
224
 
@@ -333,7 +330,7 @@ module IntrospectiveGrape
333
330
  # model : The ActiveRecord model class
334
331
  # fields: The whitelisted data structure for Rails' strong params, from which we
335
332
  # infer Grape's parameters
336
-
333
+
337
334
  # skip the ID param at the root level endpoint, so we don't duplicate the URL parameter (api/v#/model/modelId)
338
335
  fields -= [:id] if is_root_endpoint
339
336
 
@@ -403,7 +400,7 @@ module IntrospectiveGrape
403
400
  # Check if it's a file attachment, look for an override class from the model,
404
401
  # check Pg2Ruby, use the database type, or fail over to a String:
405
402
  ( is_file_attachment?(model,f) && Rack::Multipart::UploadedFile ) ||
406
- (model.try(:grape_param_types)||{}).with_indifferent_access[f] ||
403
+ (model.try(:grape_param_types)||{}).with_indifferent_access[f] ||
407
404
  Pg2Ruby[db_type] ||
408
405
  begin db_type.to_s.camelize.constantize rescue nil end ||
409
406
  String
@@ -411,9 +408,9 @@ module IntrospectiveGrape
411
408
 
412
409
  def param_required?(model,f)
413
410
  # Detect if the field is a required field for the create action
414
- return false if skip_presence_validations.include? f
411
+ return false if skip_presence_validations.include?(f)
415
412
 
416
- validated_field = (f =~ /_id/) ? f.to_s.sub(/_id\z/,'').to_sym : f.to_sym
413
+ validated_field = f =~ /_id/ ? f.to_s.sub(/_id\z/,'').to_sym : f.to_sym
417
414
 
418
415
  model.validators_on(validated_field).any? {|v| v.kind_of? ActiveRecord::Validations::PresenceValidator }
419
416
  end
@@ -29,7 +29,7 @@ if IntrospectiveGrape.config.camelize_parameters
29
29
  alias_method_chain :create_documentation_class, :camelized
30
30
  end
31
31
  end
32
- else Gem::Version.new( GrapeSwagger::VERSION ) > Gem::Version.new('0.11.0')
32
+ else
33
33
  module GrapeSwagger
34
34
  module DocMethods
35
35
  def self.extended(mod)
@@ -0,0 +1,22 @@
1
+ module IntrospectiveGrape
2
+ module CreateHelpers
3
+
4
+ def add_new_records_to_root_record(dsl, routes, params, model)
5
+ dsl.authorize model, :create?
6
+ ActiveRecord::Base.transaction do
7
+ old = find_leaves(routes, model, params)
8
+ model.update_attributes( dsl.send(:safe_params,params).permit(whitelist) )
9
+ new = find_leaves(routes, model, params)
10
+ old.respond_to?(:size) ? new-old : new
11
+ end
12
+ end
13
+
14
+ def create_new_record(dsl, routes, params)
15
+ model = routes.first.model.new( dsl.send(:safe_params,params).permit(whitelist) )
16
+ dsl.authorize model, :create?
17
+ model.save!
18
+ find_leaves(routes, model.reload, params)
19
+ end
20
+
21
+ end
22
+ end
@@ -72,30 +72,33 @@ module IntrospectiveGrape::Filters
72
72
  end
73
73
 
74
74
  dsl.optional :filter, type: String, description: "JSON of conditions for query. If you're familiar with ActiveRecord's query conventions you can build more complex filters, e.g. against included child associations, e.g. {\"&lt;association_name&gt;_&lt;parent&gt;\":{\"field\":\"value\"}}" if filters.include?(:all) || filters.include?(:filter)
75
+ end
76
+
77
+
78
+ def apply_simple_filter(klass, model, params, records, field)
79
+ return records if params[field].blank?
75
80
 
81
+ if timestamp_filter(klass,model,field)
82
+ op = field.ends_with?("_start") ? ">=" : "<="
83
+ records.where("#{timestamp_filter(klass,model,field)} #{op} ?", Time.zone.parse(params[field]))
84
+ elsif model.respond_to?("#{field}=")
85
+ records.send("#{field}=", params[field])
86
+ else
87
+ records.where(field => params[field])
88
+ end
76
89
  end
77
90
 
78
91
  def apply_filter_params(klass, model, api_params, params, records)
79
92
  records = records.order(default_sort) if default_sort.present?
80
93
 
81
94
  simple_filters(klass, model, api_params).each do |field|
82
- next if params[field].blank?
83
-
84
- if timestamp_filter(klass,model,field)
85
- op = field.ends_with?("_start") ? ">=" : "<="
86
- records = records.where("#{timestamp_filter(klass,model,field)} #{op} ?", Time.zone.parse(params[field]))
87
- elsif model.respond_to?("#{field}=")
88
- records = records.send("#{field}=", params[field])
89
- else
90
- records = records.where(field => params[field])
91
- end
95
+ records = apply_simple_filter(klass, model, params, records, field)
92
96
  end
93
97
 
94
- klass.custom_filters.each do |filter,details|
98
+ klass.custom_filters.each do |filter,_details|
95
99
  records = records.send(filter, params[filter])
96
100
  end
97
101
 
98
-
99
102
  if params[:filter].present?
100
103
  filters = JSON.parse( params[:filter].delete('\\') )
101
104
  filters.each do |key, value|
@@ -103,6 +106,8 @@ module IntrospectiveGrape::Filters
103
106
  end
104
107
  end
105
108
 
109
+ records.where( JSON.parse(params[:query]) ) if params[:query].present?
110
+
106
111
  records
107
112
  end
108
113
  end
@@ -33,7 +33,7 @@ module IntrospectiveGrape::Traversal
33
33
  return record unless routes.size > 1
34
34
  # For deeply nested routes we need to search from the root of the API to the leaf
35
35
  # of its nested associations in order to guarantee the validity of the relationship,
36
- # the authorization on the parent model, and the sanity of passed parameters.
36
+ # the authorization on the parent model, and the sanity of passed parameters.
37
37
  routes[1..-1].each_with_index do |r|
38
38
  if record && params[r.key]
39
39
  ref = r.reflection
@@ -1,3 +1,3 @@
1
1
  module IntrospectiveGrape
2
- VERSION = "0.2.7".freeze
2
+ VERSION = "0.2.8".freeze
3
3
  end
@@ -1,10 +1,11 @@
1
1
  module IntrospectiveGrape
2
- autoload :API, 'introspective_grape/api'
3
- autoload :CamelSnake, 'introspective_grape/camel_snake'
4
- autoload :Doc, 'introspective_grape/doc'
5
- autoload :Filters, 'introspective_grape/filters'
6
- autoload :Helpers, 'introspective_grape/helpers'
7
- autoload :Traversal, 'introspective_grape/traversal'
2
+ autoload :API, 'introspective_grape/api'
3
+ autoload :CamelSnake, 'introspective_grape/camel_snake'
4
+ autoload :CreateHelpers, 'introspective_grape/create_helpers'
5
+ autoload :Doc, 'introspective_grape/doc'
6
+ autoload :Filters, 'introspective_grape/filters'
7
+ autoload :Helpers, 'introspective_grape/helpers'
8
+ autoload :Traversal, 'introspective_grape/traversal'
8
9
 
9
10
  module Formatter
10
11
  autoload :CamelJson, 'introspective_grape/formatter/camel_json'
data/spec/dummy/Gemfile CHANGED
@@ -2,18 +2,17 @@ source 'https://rubygems.org'
2
2
  gem 'rails', '<5.0'
3
3
 
4
4
  #gem 'byebug'
5
+ gem 'camel_snake_keys'
6
+
7
+ gem 'delayed_paperclip'
8
+ #gem 'devise-async'
5
9
 
6
10
  gem 'grape'
7
11
  gem 'grape-entity'
8
- gem 'grape-swagger'
9
12
  gem 'grape-kaminari'
13
+ gem 'grape-swagger'
10
14
 
11
- gem 'pundit'
12
15
  gem 'paperclip'
13
- gem 'delayed_paperclip'
14
- gem 'devise-async'
15
- gem 'activerecord-tableless'
16
-
17
- gem 'camel_snake_keys'
16
+ gem 'pundit'
18
17
 
19
18
  gem 'sqlite3'
@@ -7,7 +7,8 @@ class Dummy::LocationAPI < IntrospectiveGrape::API
7
7
 
8
8
  filter_on :name, :filter
9
9
 
10
- restful Location, [:name, :kind,
10
+ restful Location, [
11
+ :name, :kind,
11
12
  {gps_attributes: [:id, :lat, :lng, :alt, :_destroy]},
12
13
  {beacons_attributes: [:id, :company_id, :mac_address, :uuid, :major, :minor, :_destroy]},
13
14
  ]
@@ -8,7 +8,8 @@ class Dummy::UserAPI < IntrospectiveGrape::API
8
8
 
9
9
  filter_on :all
10
10
 
11
- restful User, [:id, :email, :password, :first_name, :last_name, :skip_confirmation_email,
11
+ restful User, [
12
+ :id, :email, :password, :first_name, :last_name, :skip_confirmation_email,
12
13
  :created_at, :updated_at,
13
14
  user_project_jobs_attributes: [:id, :job_id, :project_id, :_destroy],
14
15
  roles_attributes: [:id, :ownable_type, :ownable_id, :_destroy],
@@ -20,7 +21,7 @@ class Dummy::UserAPI < IntrospectiveGrape::API
20
21
  end
21
22
 
22
23
  class ImageEntity < Grape::Entity
23
- expose :id, :file_processing #, 'file.url'
24
+ expose :id, :file_processing, :medium_url
24
25
  end
25
26
 
26
27
  class UserProjectJobEntity < Grape::Entity
@@ -44,7 +44,6 @@ class DummyAPI < Grape::API
44
44
  end
45
45
 
46
46
  # configure grape-swagger to auto-generate swagger docs
47
- protocol = Rails.application.config.force_ssl ? 'https' : 'http'
48
47
  add_swagger_documentation({
49
48
  base_path: "/api",
50
49
  doc_version: 'v1',
@@ -9,6 +9,9 @@ class Image < ActiveRecord::Base
9
9
  validates_attachment :file, content_type: {content_type: ["image/jpeg", "image/png", "image/gif"]}
10
10
  validates_attachment_size :file, :less_than => 2.megabytes
11
11
 
12
+ def medium_url
13
+ file.url(:medium)
14
+ end
12
15
  #process_in_background :file, processing_image_url: 'empty_avatar.png'
13
16
 
14
17
  Paperclip.interpolates :imageable_type do |attachment, _style|
@@ -3,7 +3,7 @@ class Role < AbstractAdapter
3
3
  belongs_to :ownable, polymorphic: true
4
4
 
5
5
  validates_uniqueness_of :user_id, scope: [:ownable_type,:ownable_id], unless: "user_id.nil?", message: "user has already been assigned that role"
6
- OWNABLE_TYPES = %w(SuperUser Company Project).freeze
6
+ OWNABLE_TYPES = %w(Company Project).freeze
7
7
  validates_inclusion_of :ownable_type, in: OWNABLE_TYPES
8
8
 
9
9
  delegate :email, to: :user, allow_nil: true
@@ -15,13 +15,8 @@ class Role < AbstractAdapter
15
15
  { ownable_type: { values: OWNABLE_TYPES } }
16
16
  end
17
17
 
18
- def ownable
19
- # return the SuperUser null object
20
- ownable_type == 'SuperUser' ? SuperUser.new : super
21
- end
22
-
23
18
  def self.ownable_assign_options(_model=nil)
24
- ([SuperUser.new] + Company.all + Project.all).map { |i| [ "#{i.class}: #{i.name}", "#{i.class}-#{i.id}"] }
19
+ (Company.all + Project.all).map { |i| [ "#{i.class}: #{i.name}", "#{i.class}-#{i.id}"] }
25
20
  end
26
21
 
27
22
  def ownable_assign
@@ -2,7 +2,7 @@
2
2
  class User < AbstractAdapter
3
3
  # Include default devise modules. Others available are:
4
4
  # :confirmable, :lockable, :timeoutable and :omniauthable
5
- devise :database_authenticatable, :registerable, :async, :confirmable,
5
+ devise :database_authenticatable, :registerable, :confirmable,
6
6
  :recoverable, :rememberable, :trackable, :validatable, :lockable
7
7
 
8
8
  scope :active, -> { where(:locked_at => nil) }
@@ -41,7 +41,7 @@ class User < AbstractAdapter
41
41
  end
42
42
 
43
43
  def superuser?
44
- roles && roles.detect{|r| r.ownable_type == 'SuperUser' }.present?
44
+ superuser
45
45
  end
46
46
 
47
47
  def admin?(record)
@@ -6,9 +6,8 @@ require "action_controller/railtie"
6
6
  require "action_mailer/railtie"
7
7
  require "action_view/railtie"
8
8
  #require "sprockets/railtie"
9
- require 'activerecord-tableless'
10
9
  require 'devise'
11
- require 'devise/async'
10
+ #require 'devise/async'
12
11
  require 'grape-swagger'
13
12
  require 'grape-entity'
14
13
  # require "rails/test_unit/railtie"
@@ -32,7 +31,7 @@ module Dummy
32
31
  # config.i18n.default_locale = :de
33
32
 
34
33
  # Do not swallow errors in after_commit/after_rollback callbacks.
35
- config.active_record.raise_in_transactional_callbacks = true
34
+ #config.active_record.raise_in_transactional_callbacks = true
36
35
 
37
36
  end
38
37
  end
@@ -15,6 +15,7 @@ Rails.application.configure do
15
15
 
16
16
  # Don't care if the mailer can't send.
17
17
  config.action_mailer.raise_delivery_errors = false
18
+ config.action_mailer.default_url_options = { :host => 'localhost:3000' }
18
19
 
19
20
  # Print deprecation notices to the Rails logger.
20
21
  config.active_support.deprecation = :log
@@ -63,7 +63,7 @@ Rails.application.configure do
63
63
  # Ignore bad email addresses and do not raise email delivery errors.
64
64
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
65
65
  # config.action_mailer.raise_delivery_errors = false
66
-
66
+ config.action_mailer.default_url_options = { :host => 'localhost:3000' }
67
67
  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
68
68
  # the I18n.default_locale when a translation cannot be found).
69
69
  config.i18n.fallbacks = true
@@ -31,6 +31,7 @@ Dummy::Application.configure do
31
31
  # ActionMailer::Base.deliveries array.
32
32
  config.action_mailer.perform_deliveries = false
33
33
  config.action_mailer.delivery_method = :test
34
+ config.action_mailer.default_url_options = { :host => 'localhost:3000' }
34
35
 
35
36
  # Randomize the order test cases are executed.
36
37
  config.active_support.test_order = :random
@@ -1,7 +1,7 @@
1
1
  # Be sure to restart your server when you modify this file.
2
2
 
3
3
  # Version of your assets, change this if you want to expire all your assets.
4
- Rails.application.config.assets.version = '1.0'
4
+ #Rails.application.config.assets.version = '1.0'
5
5
 
6
6
  # Add additional assets to the asset load path
7
7
  # Rails.application.config.assets.paths << Emoji.images_path
@@ -6,7 +6,7 @@ Devise.setup do |config|
6
6
  # confirmation, reset password and unlock tokens in the database.
7
7
  # Devise will use the `secret_key_base` on Rails 4+ applications as its `secret_key`
8
8
  # by default. You can change it below and use your own secret key.
9
- # config.secret_key = 'c84d39010264fc71de6cbf2897952c90f92c1b6c7a38cd83c020e84ee343d2aff10b2aa91c24e2911cdb01bf0039cd89e45ff636f73a50dc449a0c814958f6e3'
9
+ config.secret_key = 'c84d39010264fc71de6cbf2897952c90f92c1b6c7a38cd83c020e84ee343d2aff10b2aa91c24e2911cdb01bf0039cd89e45ff636f73a50dc449a0c814958f6e3'
10
10
 
11
11
  # ==> Mailer Configuration
12
12
  # Configure the e-mail address which will be shown in Devise::Mailer,
@@ -1,2 +1,2 @@
1
1
  #require 'devise/async'
2
- Devise::Async.backend = :sidekiq
2
+ #Devise::Async.backend = :sidekiq
@@ -9,6 +9,8 @@ class DeviseCreateUsers < ActiveRecord::Migration
9
9
  t.string :reset_password_token
10
10
  t.datetime :reset_password_sent_at
11
11
 
12
+ t.boolean :superuser, default: false
13
+
12
14
  ## Rememberable
13
15
  t.datetime :remember_created_at
14
16
 
@@ -16,8 +18,8 @@ class DeviseCreateUsers < ActiveRecord::Migration
16
18
  t.integer :sign_in_count, default: 0, null: false
17
19
  t.datetime :current_sign_in_at
18
20
  t.datetime :last_sign_in_at
19
- t.inet :current_sign_in_ip
20
- t.inet :last_sign_in_ip
21
+ #t.inet :current_sign_in_ip
22
+ #t.inet :last_sign_in_ip
21
23
 
22
24
  ## Confirmable
23
25
  # t.string :confirmation_token
@@ -22,8 +22,8 @@ class DeviseCreateAdminUsers < ActiveRecord::Migration
22
22
  t.integer :sign_in_count, default: 0, null: false
23
23
  t.datetime :current_sign_in_at
24
24
  t.datetime :last_sign_in_at
25
- t.inet :current_sign_in_ip
26
- t.inet :last_sign_in_ip
25
+ #t.inet :current_sign_in_ip
26
+ #t.inet :last_sign_in_ip
27
27
 
28
28
  ## Confirmable
29
29
  # t.string :confirmation_token
@@ -261,6 +261,7 @@ ActiveRecord::Schema.define(version: 20150909225019) do
261
261
  t.integer "failed_attempts", default: 0, null: false
262
262
  t.string "unlock_token"
263
263
  t.datetime "locked_at"
264
+ t.boolean "superuser", default: false, null: false
264
265
  t.string "authentication_token"
265
266
  t.string "confirmation_token"
266
267
  t.datetime "confirmed_at"
@@ -7,11 +7,6 @@ RSpec.describe Role, type: :model do
7
7
  let(:company) { Company.make! }
8
8
  let(:project) { Project.make! }
9
9
 
10
- it "allows user assignment to super user" do
11
- ur = Role.new(user: user, ownable: SuperUser.new )
12
- ur.valid?.should be_truthy
13
- end
14
-
15
10
  it "allows user assignment to company admin" do
16
11
  ur = Role.create(user:user, ownable: company)
17
12
  ur.valid?.should be_truthy
@@ -25,8 +20,7 @@ RSpec.describe Role, type: :model do
25
20
  context "User helper methods" do
26
21
  it "should register a user as a super user" do
27
22
  user.superuser?.should == false
28
- Role.create!(user:user, ownable: SuperUser.new)
29
- user.reload
23
+ user.superuser = true
30
24
  user.superuser?.should == true
31
25
  end
32
26
 
@@ -113,11 +113,11 @@ RSpec.describe User, type: :model do
113
113
  end
114
114
 
115
115
  it "should cascade deletes to chat_user, messages, and read logs" do
116
- ChatUser.where(discussion.id).size.should == 3
117
- ChatMessage.where(discussion.id).size.should == 2
116
+ ChatUser.where(chat_id: discussion.id).size.should == 3
117
+ ChatMessage.where(chat_id: discussion.id).size.should == 2
118
118
  discussion.destroy
119
- ChatUser.where(discussion.id).size.should == 0
120
- ChatMessage.where(discussion.id).size.should == 0
119
+ ChatUser.where(chat_id: discussion.id).size.should == 0
120
+ ChatMessage.where(chat_id: discussion.id).size.should == 0
121
121
  end
122
122
  end
123
123
  end
@@ -53,8 +53,8 @@ describe Dummy::ProjectAPI, type: :request do
53
53
 
54
54
  context "via nested attributes" do
55
55
  it "should create a team with users" do
56
- p = { name: 'New Team',
57
- team_users_attributes: [{ user_id: @u1.id }, { user_id: @u2.id }]
56
+ p = {
57
+ name: 'New Team', team_users_attributes: [{ user_id: @u1.id }, { user_id: @u2.id }]
58
58
  }
59
59
  post "/api/v1/projects/#{project.id}/teams", p
60
60
  response.should be_success
@@ -1,13 +1,14 @@
1
1
  require 'rails_helper'
2
-
3
2
  describe Dummy::RoleAPI, type: :request do
4
3
  let(:role) { Role.last }
5
4
  let(:user) { User.last }
5
+ let(:company) { Company.last }
6
6
 
7
7
  before :all do
8
+ c = Company.make!
8
9
  Role.destroy_all
9
- User.make!
10
- Role.make!(user_id: User.last.id, ownable_type: 'SuperUser')
10
+ User.make!(superuser: true)
11
+ Role.make!(user_id: User.last.id, ownable_id: c.id, ownable_type: c.class)
11
12
  end
12
13
 
13
14
  it 'should return a list of user roles' do
@@ -29,13 +30,13 @@ describe Dummy::RoleAPI, type: :request do
29
30
  end
30
31
 
31
32
  it 'should not duplicate user roles' do
32
- post '/api/v1/roles', { user_id: user.id, ownable_type: 'SuperUser' }
33
+ post '/api/v1/roles', { user_id: user.id, ownable_type: 'Company', ownable_id: company.id }
33
34
  response.code.should == '400'
34
35
  json['error'].should =~ /user has already been assigned that role/
35
36
  end
36
37
 
37
38
  it 'validates ownable type value specified in grape_validations' do
38
- post '/api/v1/roles', { user_id: user.id, ownable_type: 'NotSuperUser' }
39
+ post '/api/v1/roles', { user_id: user.id, ownable_type: 'NotCompany' }
39
40
  response.code.should == '400'
40
41
  json['error'].should eq "ownable_type does not have a valid value"
41
42
  end
@@ -98,6 +98,15 @@ describe Dummy::UserAPI, type: :request do
98
98
  json['error'].should == "Email: is invalid, Password: can't be blank"
99
99
  end
100
100
 
101
+ it "should return only the records that were created at a nested endpoint" do
102
+ new_company = Company.make!
103
+ post "/api/v1/users/#{user.id}/roles", {ownable_type: 'Company', ownable_id: new_company.id}
104
+ response.should be_success
105
+ json.size.should eq 1
106
+ json.first['ownable_id'].should eq new_company.id
107
+ end
108
+
109
+
101
110
  let(:params) do
102
111
  { email: 'test@test.com', password: 'abc12345', roles_attributes:[] }
103
112
  end
@@ -148,8 +157,9 @@ describe Dummy::UserAPI, type: :request do
148
157
  put "/api/v1/users/#{user.id}", params
149
158
 
150
159
  response.should be_success
151
- user.avatar.should == Image.last
152
- user.avatar_url.should == Image.last.file.url(:medium)
160
+ json['avatar_url'].should eq Image.last.file.url(:medium)
161
+ user.avatar.should eq Image.last
162
+ user.avatar_url.should eq Image.last.file.url(:medium)
153
163
  end
154
164
 
155
165
  it "should upload a user avatar via the nested route, to test the restful api's handling of has_one associations" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: introspective_grape
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Buermann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-19 00:00:00.000000000 Z
11
+ date: 2017-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -17,9 +17,6 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 3.0.0
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: 5.0.0
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +24,6 @@ dependencies:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: 3.0.0
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: 5.0.0
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: grape
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -170,20 +164,6 @@ dependencies:
170
164
  - - ">="
171
165
  - !ruby/object:Gem::Version
172
166
  version: '0'
173
- - !ruby/object:Gem::Dependency
174
- name: devise-async
175
- requirement: !ruby/object:Gem::Requirement
176
- requirements:
177
- - - ">="
178
- - !ruby/object:Gem::Version
179
- version: '0'
180
- type: :development
181
- prerelease: false
182
- version_requirements: !ruby/object:Gem::Requirement
183
- requirements:
184
- - - ">="
185
- - !ruby/object:Gem::Version
186
- version: '0'
187
167
  - !ruby/object:Gem::Dependency
188
168
  name: paperclip
189
169
  requirement: !ruby/object:Gem::Requirement
@@ -240,20 +220,6 @@ dependencies:
240
220
  - - ">="
241
221
  - !ruby/object:Gem::Version
242
222
  version: '0'
243
- - !ruby/object:Gem::Dependency
244
- name: activerecord-tableless
245
- requirement: !ruby/object:Gem::Requirement
246
- requirements:
247
- - - "~>"
248
- - !ruby/object:Gem::Version
249
- version: '1.0'
250
- type: :development
251
- prerelease: false
252
- version_requirements: !ruby/object:Gem::Requirement
253
- requirements:
254
- - - "~>"
255
- - !ruby/object:Gem::Version
256
- version: '1.0'
257
223
  description: Introspectively configure deeply nested RESTful Grape APIs for ActiveRecord
258
224
  models.
259
225
  email:
@@ -281,11 +247,19 @@ files:
281
247
  - app/views/.keep
282
248
  - bin/rails
283
249
  - gemfiles/2.0.0-Gemfile
250
+ - gemfiles/2.2.0-Gemfile
251
+ - gemfiles/Gemfile.rails.3.2.22
252
+ - gemfiles/Gemfile.rails.4.1.13
253
+ - gemfiles/Gemfile.rails.4.2.7.1
254
+ - gemfiles/Gemfile.rails.4.2.7.1.new.swagger
255
+ - gemfiles/Gemfile.rails.5.0.1
256
+ - gemfiles/Gemfile.rails.master
284
257
  - introspective_grape.gemspec
285
258
  - lib/.DS_Store
286
259
  - lib/introspective_grape.rb
287
260
  - lib/introspective_grape/api.rb
288
261
  - lib/introspective_grape/camel_snake.rb
262
+ - lib/introspective_grape/create_helpers.rb
289
263
  - lib/introspective_grape/doc.rb
290
264
  - lib/introspective_grape/filters.rb
291
265
  - lib/introspective_grape/formatter/camel_json.rb
@@ -336,7 +310,6 @@ files:
336
310
  - spec/dummy/app/models/project.rb
337
311
  - spec/dummy/app/models/project_job.rb
338
312
  - spec/dummy/app/models/role.rb
339
- - spec/dummy/app/models/super_user.rb
340
313
  - spec/dummy/app/models/team.rb
341
314
  - spec/dummy/app/models/team_user.rb
342
315
  - spec/dummy/app/models/user.rb
@@ -506,7 +479,6 @@ test_files:
506
479
  - spec/dummy/app/models/project.rb
507
480
  - spec/dummy/app/models/project_job.rb
508
481
  - spec/dummy/app/models/role.rb
509
- - spec/dummy/app/models/super_user.rb
510
482
  - spec/dummy/app/models/team.rb
511
483
  - spec/dummy/app/models/team_user.rb
512
484
  - spec/dummy/app/models/user.rb
@@ -1,11 +0,0 @@
1
- #require 'activerecord-tableless'
2
- class SuperUser < AbstractAdapter
3
- # An empty ActiveRecord association for the polymorphic identity on Role.
4
- # We could also just create a SuperUser table with one record to do this...
5
- has_no_table :database => :pretend_success
6
-
7
- def name
8
- 'Admin'
9
- end
10
-
11
- end