introspective_grape 0.2.7 → 0.2.8

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.
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