grape-entity 0.6.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
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'