grape-entity 0.6.0 → 0.10.1

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 (54) hide show
  1. checksums.yaml +5 -5
  2. data/.coveralls.yml +1 -0
  3. data/.github/dependabot.yml +14 -0
  4. data/.github/workflows/rubocop.yml +26 -0
  5. data/.github/workflows/ruby.yml +26 -0
  6. data/.gitignore +5 -1
  7. data/.rspec +2 -1
  8. data/.rubocop.yml +82 -2
  9. data/.rubocop_todo.yml +16 -33
  10. data/CHANGELOG.md +120 -0
  11. data/Dangerfile +2 -0
  12. data/Gemfile +8 -8
  13. data/Guardfile +4 -2
  14. data/README.md +168 -7
  15. data/Rakefile +2 -2
  16. data/UPGRADING.md +19 -2
  17. data/bench/serializing.rb +7 -0
  18. data/grape-entity.gemspec +10 -8
  19. data/lib/grape-entity.rb +2 -0
  20. data/lib/grape_entity/condition/base.rb +3 -1
  21. data/lib/grape_entity/condition/block_condition.rb +3 -1
  22. data/lib/grape_entity/condition/hash_condition.rb +2 -0
  23. data/lib/grape_entity/condition/symbol_condition.rb +2 -0
  24. data/lib/grape_entity/condition.rb +20 -11
  25. data/lib/grape_entity/delegator/base.rb +7 -0
  26. data/lib/grape_entity/delegator/fetchable_object.rb +2 -0
  27. data/lib/grape_entity/delegator/hash_object.rb +4 -2
  28. data/lib/grape_entity/delegator/openstruct_object.rb +2 -0
  29. data/lib/grape_entity/delegator/plain_object.rb +2 -0
  30. data/lib/grape_entity/delegator.rb +14 -9
  31. data/lib/grape_entity/deprecated.rb +13 -0
  32. data/lib/grape_entity/entity.rb +115 -38
  33. data/lib/grape_entity/exposure/base.rb +27 -11
  34. data/lib/grape_entity/exposure/block_exposure.rb +2 -0
  35. data/lib/grape_entity/exposure/delegator_exposure.rb +2 -0
  36. data/lib/grape_entity/exposure/formatter_block_exposure.rb +2 -0
  37. data/lib/grape_entity/exposure/formatter_exposure.rb +2 -0
  38. data/lib/grape_entity/exposure/nesting_exposure/nested_exposures.rb +27 -15
  39. data/lib/grape_entity/exposure/nesting_exposure/output_builder.rb +8 -2
  40. data/lib/grape_entity/exposure/nesting_exposure.rb +36 -30
  41. data/lib/grape_entity/exposure/represent_exposure.rb +3 -1
  42. data/lib/grape_entity/exposure.rb +69 -41
  43. data/lib/grape_entity/options.rb +44 -58
  44. data/lib/grape_entity/version.rb +3 -1
  45. data/lib/grape_entity.rb +3 -0
  46. data/spec/grape_entity/entity_spec.rb +405 -48
  47. data/spec/grape_entity/exposure/nesting_exposure/nested_exposures_spec.rb +6 -4
  48. data/spec/grape_entity/exposure/represent_exposure_spec.rb +5 -3
  49. data/spec/grape_entity/exposure_spec.rb +14 -2
  50. data/spec/grape_entity/hash_spec.rb +52 -1
  51. data/spec/grape_entity/options_spec.rb +66 -0
  52. data/spec/spec_helper.rb +17 -0
  53. metadata +35 -45
  54. data/.travis.yml +0 -26
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape_entity/exposure/base'
2
4
  require 'grape_entity/exposure/represent_exposure'
3
5
  require 'grape_entity/exposure/block_exposure'
@@ -10,67 +12,93 @@ require 'grape_entity/condition'
10
12
  module Grape
11
13
  class Entity
12
14
  module Exposure
13
- def self.new(attribute, options)
14
- conditions = compile_conditions(options)
15
- base_args = [attribute, options, conditions]
16
-
17
- if options[:proc]
18
- block_exposure = BlockExposure.new(*base_args, &options[:proc])
19
- else
20
- delegator_exposure = DelegatorExposure.new(*base_args)
21
- end
15
+ class << self
16
+ def new(attribute, options)
17
+ conditions = compile_conditions(attribute, options)
18
+ base_args = [attribute, options, conditions]
22
19
 
23
- if options[:using]
20
+ passed_proc = options[:proc]
24
21
  using_class = options[:using]
22
+ format_with = options[:format_with]
25
23
 
26
- if options[:proc]
27
- RepresentExposure.new(*base_args, using_class, block_exposure)
24
+ if using_class
25
+ build_class_exposure(base_args, using_class, passed_proc)
26
+ elsif passed_proc
27
+ build_block_exposure(base_args, passed_proc)
28
+ elsif format_with
29
+ build_formatter_exposure(base_args, format_with)
30
+ elsif options[:nesting]
31
+ build_nesting_exposure(base_args)
28
32
  else
29
- RepresentExposure.new(*base_args, using_class, delegator_exposure)
33
+ build_delegator_exposure(base_args)
30
34
  end
35
+ end
31
36
 
32
- elsif options[:proc]
33
- block_exposure
37
+ private
34
38
 
35
- elsif options[:format_with]
36
- format_with = options[:format_with]
39
+ def compile_conditions(attribute, options)
40
+ if_conditions = [
41
+ options[:if_extras],
42
+ options[:if]
43
+ ].compact.flatten.map { |cond| Condition.new_if(cond) }
37
44
 
38
- if format_with.is_a? Symbol
39
- FormatterExposure.new(*base_args, format_with)
40
- elsif format_with.respond_to? :call
41
- FormatterBlockExposure.new(*base_args, &format_with)
42
- end
45
+ unless_conditions = [
46
+ options[:unless_extras],
47
+ options[:unless]
48
+ ].compact.flatten.map { |cond| Condition.new_unless(cond) }
43
49
 
44
- elsif options[:nesting]
45
- NestingExposure.new(*base_args)
50
+ unless_conditions << expose_nil_condition(attribute, options) if options[:expose_nil] == false
46
51
 
47
- else
48
- delegator_exposure
52
+ if_conditions + unless_conditions
49
53
  end
50
- end
51
54
 
52
- def self.compile_conditions(options)
53
- if_conditions = []
54
- unless options[:if_extras].nil?
55
- if_conditions.concat(options[:if_extras])
55
+ def expose_nil_condition(attribute, options)
56
+ Condition.new_unless(
57
+ proc do |object, _options|
58
+ if options[:proc].nil?
59
+ delegator = Delegator.new(object)
60
+ if is_a?(Grape::Entity) && delegator.accepts_options?
61
+ delegator.delegate(attribute, **self.class.delegation_opts).nil?
62
+ else
63
+ delegator.delegate(attribute).nil?
64
+ end
65
+ else
66
+ exec_with_object(options, &options[:proc]).nil?
67
+ end
68
+ end
69
+ )
56
70
  end
57
- if_conditions << options[:if] unless options[:if].nil?
58
71
 
59
- if_conditions.map! do |cond|
60
- Condition.new_if cond
72
+ def build_class_exposure(base_args, using_class, passed_proc)
73
+ exposure =
74
+ if passed_proc
75
+ build_block_exposure(base_args, passed_proc)
76
+ else
77
+ build_delegator_exposure(base_args)
78
+ end
79
+
80
+ RepresentExposure.new(*base_args, using_class, exposure)
81
+ end
82
+
83
+ def build_formatter_exposure(base_args, format_with)
84
+ if format_with.is_a? Symbol
85
+ FormatterExposure.new(*base_args, format_with)
86
+ elsif format_with.respond_to?(:call)
87
+ FormatterBlockExposure.new(*base_args, &format_with)
88
+ end
61
89
  end
62
90
 
63
- unless_conditions = []
64
- unless options[:unless_extras].nil?
65
- unless_conditions.concat(options[:unless_extras])
91
+ def build_nesting_exposure(base_args)
92
+ NestingExposure.new(*base_args)
66
93
  end
67
- unless_conditions << options[:unless] unless options[:unless].nil?
68
94
 
69
- unless_conditions.map! do |cond|
70
- Condition.new_unless cond
95
+ def build_block_exposure(base_args, passed_proc)
96
+ BlockExposure.new(*base_args, &passed_proc)
71
97
  end
72
98
 
73
- if_conditions + unless_conditions
99
+ def build_delegator_exposure(base_args)
100
+ DelegatorExposure.new(*base_args)
101
+ end
74
102
  end
75
103
  end
76
104
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
1
5
  module Grape
2
6
  class Entity
3
7
  class Options
8
+ extend Forwardable
9
+
4
10
  attr_reader :opts_hash
5
11
 
12
+ def_delegators :opts_hash, :dig, :key?, :fetch, :[], :empty
13
+
6
14
  def initialize(opts_hash = {})
7
15
  @opts_hash = opts_hash
8
16
  @has_only = !opts_hash[:only].nil?
@@ -11,54 +19,33 @@ module Grape
11
19
  @should_return_key_cache = {}
12
20
  end
13
21
 
14
- def [](key)
15
- @opts_hash[key]
16
- end
22
+ def merge(new_opts)
23
+ return self if new_opts.empty?
17
24
 
18
- def fetch(*args)
19
- @opts_hash.fetch(*args)
20
- end
25
+ merged = if new_opts.instance_of? Options
26
+ @opts_hash.merge(new_opts.opts_hash)
27
+ else
28
+ @opts_hash.merge(new_opts)
29
+ end
21
30
 
22
- def key?(key)
23
- @opts_hash.key? key
24
- end
25
-
26
- def merge(new_opts)
27
- if new_opts.empty?
28
- self
29
- else
30
- merged = if new_opts.instance_of? Options
31
- @opts_hash.merge(new_opts.opts_hash)
32
- else
33
- @opts_hash.merge(new_opts)
34
- end
35
- Options.new(merged)
36
- end
31
+ Options.new(merged)
37
32
  end
38
33
 
39
34
  def reverse_merge(new_opts)
40
- if new_opts.empty?
41
- self
42
- else
43
- merged = if new_opts.instance_of? Options
44
- new_opts.opts_hash.merge(@opts_hash)
45
- else
46
- new_opts.merge(@opts_hash)
47
- end
48
- Options.new(merged)
49
- end
50
- end
35
+ return self if new_opts.empty?
51
36
 
52
- def empty?
53
- @opts_hash.empty?
37
+ merged = if new_opts.instance_of? Options
38
+ new_opts.opts_hash.merge(@opts_hash)
39
+ else
40
+ new_opts.merge(@opts_hash)
41
+ end
42
+
43
+ Options.new(merged)
54
44
  end
55
45
 
56
46
  def ==(other)
57
- @opts_hash == if other.is_a? Options
58
- other.opts_hash
59
- else
60
- other
61
- end
47
+ other_hash = other.is_a?(Options) ? other.opts_hash : other
48
+ @opts_hash == other_hash
62
49
  end
63
50
 
64
51
  def should_return_key?(key)
@@ -66,7 +53,7 @@ module Grape
66
53
 
67
54
  only = only_fields.nil? ||
68
55
  only_fields.key?(key)
69
- except = except_fields && except_fields.key?(key) &&
56
+ except = except_fields&.key?(key) &&
70
57
  except_fields[key] == true
71
58
  only && !except
72
59
  end
@@ -96,36 +83,35 @@ module Grape
96
83
  end
97
84
 
98
85
  def with_attr_path(part)
86
+ return yield unless part
87
+
99
88
  stack = (opts_hash[:attr_path] ||= [])
100
- if part
101
- stack.push part
102
- result = yield
103
- stack.pop
104
- result
105
- else
106
- yield
107
- end
89
+ stack.push part
90
+ result = yield
91
+ stack.pop
92
+ result
108
93
  end
109
94
 
110
95
  private
111
96
 
112
97
  def build_for_nesting(key)
113
- new_opts_hash = opts_hash.dup
114
- new_opts_hash.delete(:collection)
115
- new_opts_hash[:root] = nil
116
- new_opts_hash[:only] = only_fields(key)
117
- new_opts_hash[:except] = except_fields(key)
118
- new_opts_hash[:attr_path] = opts_hash[:attr_path]
119
-
120
- Options.new(new_opts_hash)
98
+ Options.new(
99
+ opts_hash.dup.reject { |current_key| current_key == :collection }.merge(
100
+ root: nil,
101
+ only: only_fields(key),
102
+ except: except_fields(key),
103
+ attr_path: opts_hash[:attr_path]
104
+ )
105
+ )
121
106
  end
122
107
 
123
108
  def build_symbolized_hash(attribute, hash)
124
- if attribute.is_a?(Hash)
109
+ case attribute
110
+ when Hash
125
111
  attribute.each do |attr, nested_attrs|
126
112
  hash[attr.to_sym] = build_symbolized_hash(nested_attrs, {})
127
113
  end
128
- elsif attribute.is_a?(Array)
114
+ when Array
129
115
  return attribute.each { |x| build_symbolized_hash(x, {}) }
130
116
  else
131
117
  hash[attribute.to_sym] = true
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GrapeEntity
2
- VERSION = '0.6.0'.freeze
4
+ VERSION = '0.10.1'
3
5
  end
data/lib/grape_entity.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/version'
2
4
  require 'active_support/core_ext/string/inflections'
3
5
  require 'active_support/core_ext/hash/reverse_merge'
@@ -7,3 +9,4 @@ require 'grape_entity/entity'
7
9
  require 'grape_entity/delegator'
8
10
  require 'grape_entity/exposure'
9
11
  require 'grape_entity/options'
12
+ require 'grape_entity/deprecated'