representable 2.3.0 → 2.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +47 -0
  3. data/Gemfile +5 -0
  4. data/README.md +33 -0
  5. data/lib/representable.rb +60 -73
  6. data/lib/representable/binding.rb +37 -194
  7. data/lib/representable/cached.rb +10 -46
  8. data/lib/representable/coercion.rb +8 -8
  9. data/lib/representable/config.rb +15 -75
  10. data/lib/representable/debug.rb +41 -59
  11. data/lib/representable/declarative.rb +34 -53
  12. data/lib/representable/decorator.rb +11 -40
  13. data/lib/representable/definition.rb +14 -15
  14. data/lib/representable/deprecations.rb +90 -0
  15. data/lib/representable/deserializer.rb +87 -82
  16. data/lib/representable/for_collection.rb +5 -3
  17. data/lib/representable/hash.rb +5 -3
  18. data/lib/representable/hash/binding.rb +6 -15
  19. data/lib/representable/hash/collection.rb +10 -6
  20. data/lib/representable/hash_methods.rb +5 -5
  21. data/lib/representable/insert.rb +31 -0
  22. data/lib/representable/json.rb +7 -3
  23. data/lib/representable/json/hash.rb +1 -1
  24. data/lib/representable/object/binding.rb +5 -5
  25. data/lib/representable/parse_strategies.rb +37 -3
  26. data/lib/representable/pipeline.rb +37 -5
  27. data/lib/representable/pipeline_factories.rb +88 -0
  28. data/lib/representable/serializer.rb +38 -44
  29. data/lib/representable/version.rb +1 -1
  30. data/lib/representable/xml.rb +4 -0
  31. data/lib/representable/xml/binding.rb +25 -31
  32. data/lib/representable/xml/collection.rb +5 -3
  33. data/lib/representable/xml/hash.rb +7 -2
  34. data/lib/representable/yaml.rb +6 -3
  35. data/lib/representable/yaml/binding.rb +4 -4
  36. data/representable.gemspec +3 -3
  37. data/test/---deserialize-pipeline_test.rb +37 -0
  38. data/test/binding_test.rb +7 -7
  39. data/test/cached_test.rb +31 -19
  40. data/test/coercion_test.rb +2 -2
  41. data/test/config/inherit_test.rb +13 -12
  42. data/test/config_test.rb +12 -67
  43. data/test/decorator_test.rb +4 -5
  44. data/test/default_test.rb +34 -0
  45. data/test/defaults_options_test.rb +93 -0
  46. data/test/definition_test.rb +19 -39
  47. data/test/exec_context_test.rb +1 -1
  48. data/test/filter_test.rb +18 -20
  49. data/test/getter_setter_test.rb +1 -8
  50. data/test/hash_bindings_test.rb +13 -13
  51. data/test/heritage_test.rb +62 -0
  52. data/test/if_test.rb +1 -0
  53. data/test/inherit_test.rb +5 -3
  54. data/test/instance_test.rb +3 -4
  55. data/test/json_test.rb +3 -59
  56. data/test/lonely_test.rb +47 -3
  57. data/test/nested_test.rb +8 -2
  58. data/test/pipeline_test.rb +259 -0
  59. data/test/populator_test.rb +76 -0
  60. data/test/realistic_benchmark.rb +39 -7
  61. data/test/render_nil_test.rb +21 -0
  62. data/test/represent_test.rb +2 -2
  63. data/test/representable_test.rb +33 -103
  64. data/test/schema_test.rb +5 -15
  65. data/test/serialize_deserialize_test.rb +2 -2
  66. data/test/skip_test.rb +1 -1
  67. data/test/test_helper.rb +6 -0
  68. data/test/uncategorized_test.rb +67 -0
  69. data/test/xml_bindings_test.rb +6 -6
  70. data/test/xml_test.rb +6 -6
  71. metadata +33 -13
  72. data/lib/representable/apply.rb +0 -13
  73. data/lib/representable/inheritable.rb +0 -71
  74. data/lib/representable/mapper.rb +0 -83
  75. data/lib/representable/populator.rb +0 -56
  76. data/test/inheritable_test.rb +0 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 304ec8f66cc93beff15aba84d2d4b5d5214fa5d1
4
- data.tar.gz: 83a928ef451bd032509abb6c41d8ababda744a97
3
+ metadata.gz: a7d63369f6aa46d6611bd4f968a61ed24e91c1eb
4
+ data.tar.gz: f6708f841e1ada1e9e17cfd5a7c6bb43300d05d1
5
5
  SHA512:
6
- metadata.gz: 5eb021bc55145ff44e467db742d2842c8971413b44f084df9046f9eb6ee66e5250fa523fae0f8d0f3f0f3938c2e38339ca08876bcd04f6fb291ab8fb4368b0b7
7
- data.tar.gz: fc426cbb854f6aef32a99b5bdec3e0f364176cf9ece0d35f19c43200434db12a2d8f545430f139cc8f452baf3598183ba8bd1ab53b6ce6d944512a5ac31c1fa5
6
+ metadata.gz: 9acd1431d96131b220f03bd15296c94a77fc362959a46daa0b41847cc6ef2d375295e546664e3f4e911ae3118447497cf260b8cdd301f2e1662c916b940e152c
7
+ data.tar.gz: c45744211c992ab797ea4f6d0488930414b4587e3be8db98ae74973d135557b7f1108a913e440f8e1e8808957240b89cf0dff60b4ee463c8de86773820af3fad
data/CHANGES.md CHANGED
@@ -1,3 +1,50 @@
1
+ # 2.4.0
2
+
3
+ * Breaking API change: `:parse_filter` and `:render_filter` have no deprecation as all the other, they receive one options.
4
+ render_filter: val, doc, options
5
+ * `Decorator` always needs a format engine included, e.g. `Representable::JSON` to build bindings at compile-time.
6
+ * Removed `Representable::Apply`. This is now done via `Schema`.
7
+ representer_class.representable_attrs is definitions
8
+ * Removed `:use_decorator` option. Use a decorator instead.
9
+
10
+ TODO: DEPRECATE 4-args? also, allow options[:fragment] instead of options[:result] ?
11
+
12
+
13
+ TODO: :representable should be removing Deserialize.
14
+ deprecate Coercion, it sucks
15
+ deprecate parse/render filter
16
+
17
+ pipeline
18
+ less ifs, instead: simply remove the "middleware"
19
+ read_fragment
20
+ represented.set instance.prepare.from_json
21
+ Populator.(fragment)
22
+ not_found?.default.deserialize.parse_filter.set
23
+ Deserializer.(fragment)
24
+ instance.class.prepare.deserialize=from_json
25
+
26
+ OverwriteOnNil vs. StopOnNil
27
+ [Instance, ->(){..}, Bla]
28
+
29
+ did you ever overwrite #create_object in a binding? you can now simply pass a callable object into the pipeline.
30
+ TODO: deprecate parse strategies in favour of :populator (analogue to Reform)
31
+
32
+ elegant, easier to understand, and extend
33
+ you know, the whole deserialize stack (same for rendering): you pass in the json, it finds the fragments for each property, passes it to the binding, the binding deserializes, builds/finds a model, assigns values to it, and so on
34
+ you can partially hook in with :instance or :deserialize or :parse_filter, but it's really awkward. i am now changing the architecture to a pipeline, where you plug in the features you want (e.g. "do not call :prepare, :instance, :class, etc. but run my own :populator")
35
+ and it suddenly is super simple to understand
36
+
37
+ * Added `Representable.deprecations = false` to disable slow and weird deprecation code.
38
+
39
+ * Removed `Binding#user_options`. This is now available via `->(options[:user_options])`.
40
+ * Removed `Binding#as`.
41
+ * Removed `Binding#parent_decorator`.
42
+
43
+ ## Internal Changes
44
+ * Removed `Binding@represented` (which was never public anyway). Use `Binding#represented`.
45
+ * Changed signature: `Binding#get(represented:)`. In now needs a hash `{represented: ..}`.
46
+
47
+
1
48
  # 2.3.0
2
49
 
3
50
  * Remove dependency to Nokogiri and Multi_JSON. You have to add what you need to your `Gemfile`/`gemspec` now.
data/Gemfile CHANGED
@@ -11,4 +11,9 @@ end
11
11
  group :test do
12
12
  gem "nokogiri", require: false
13
13
  gem "multi_json", require: false
14
+ gem "minitest-line"
15
+ gem "pry"
14
16
  end
17
+
18
+ # gem "declarative", path: "../declarative"
19
+ # gem "declarative", github: "apotonick/declarative"
data/README.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  Representable maps Ruby objects to documents and back.
4
4
 
5
+ [![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat)
6
+ [![Build
7
+ Status](https://travis-ci.org/apotonick/representable.svg)](https://travis-ci.org/apotonick/representable)
8
+ [![Gem Version](https://badge.fury.io/rb/representable.svg)](http://badge.fury.io/rb/representable)
9
+
5
10
  In other words: Take an object and decorate it with a representer module. This will allow you to render a JSON, XML or YAML document from that object. But that's only half of it! You can also use representers to parse a document and create or populate an object.
6
11
 
7
12
  Representable is helpful for all kind of mappings, rendering and parsing workflows. However, it is mostly useful in API code. Are you planning to write a real REST API with representable? Then check out the [Roar](http://github.com/apotonick/roar) gem first, save work and time and make the world a better place instead.
@@ -1362,6 +1367,34 @@ end
1362
1367
 
1363
1368
  This is an implementation detail most people shouldn't worry about.
1364
1369
 
1370
+
1371
+ ### Defaults
1372
+
1373
+ Declaring default options for `property`, `nested`, `collection` and `hash` is
1374
+ easy as:
1375
+
1376
+ ```ruby
1377
+ defaults render_nil: true
1378
+ ```
1379
+
1380
+ You can also pass a block for dynamic default options based on the property
1381
+ name or on the property options.
1382
+
1383
+ ```ruby
1384
+ defaults render_nil: true do |name|
1385
+ { as: name.to_s.camelize }
1386
+ end
1387
+ ```
1388
+
1389
+ ```ruby
1390
+ defaults render_nil: true do |name, options|
1391
+ {
1392
+ as: name.to_s.camelize,
1393
+ embedded: options.fetch(:hash, false)
1394
+ }
1395
+ end
1396
+ ```
1397
+
1365
1398
  ## Symbol Keys vs. String Keys
1366
1399
 
1367
1400
  When parsing representable reads properties from hashes by using their string keys.
@@ -1,21 +1,18 @@
1
- require 'forwardable'
2
-
3
- require 'representable/inheritable'
4
- require 'representable/config'
5
- require 'representable/definition'
6
- require 'representable/mapper'
7
- require 'representable/for_collection'
8
- require 'representable/represent'
9
- require 'representable/declarative'
10
- require 'representable/apply'
11
- require "representable/populator"
12
- require "representable/deserializer"
13
- require "representable/serializer"
14
- require "representable/cached"
1
+ require "uber/delegates"
15
2
 
3
+ require "declarative/schema"
16
4
 
17
- require "uber/callable"
5
+ require "representable/config"
6
+ require "representable/definition"
7
+ require "representable/declarative"
8
+ require "representable/deserializer"
9
+ require "representable/serializer"
10
+ require "representable/binding"
18
11
  require "representable/pipeline"
12
+ require "representable/insert" # Pipeline::Insert
13
+ require "representable/cached"
14
+ require "representable/for_collection"
15
+ require "representable/represent"
19
16
 
20
17
  module Representable
21
18
  attr_writer :representable_attrs
@@ -23,86 +20,90 @@ module Representable
23
20
  def self.included(base)
24
21
  base.class_eval do
25
22
  extend Declarative
26
- extend ClassInclusions, ModuleExtensions
23
+ # make Representable horizontally and vertically inheritable.
24
+ extend ModuleExtensions, ::Declarative::Heritage::Inherited, ::Declarative::Heritage::Included
27
25
  extend ClassMethods
28
- extend Feature
29
26
  extend ForCollection
30
27
  extend Represent
31
- extend Apply
32
- # register_feature Representable # Representable gets included automatically when creating inline representer.
33
28
  end
34
29
  end
35
30
 
36
31
  private
37
32
  # Reads values from +doc+ and sets properties accordingly.
38
33
  def update_properties_from(doc, options, format)
39
- propagated_options, private_options = normalize_options!(options)
34
+ propagated_options = normalize_options(options)
40
35
 
41
- representable_mapper(format, propagated_options).deserialize(represented, doc, propagated_options, private_options)
36
+ representable_map!(doc, propagated_options, format, :uncompile_fragment)
37
+ represented
42
38
  end
43
39
 
44
40
  # Compiles the document going through all properties.
45
41
  def create_representation_with(doc, options, format)
46
- propagated_options, private_options = normalize_options!(options)
42
+ propagated_options = normalize_options(options) # {_private: {include: }, is_admin: true}
47
43
 
48
- representable_mapper(format, propagated_options).serialize(represented, doc, propagated_options, private_options)
44
+ representable_map!(doc, propagated_options, format, :compile_fragment)
45
+ doc
49
46
  end
50
47
 
51
- def representable_bindings_for(format, options)
52
- representable_attrs.collect {|definition| representable_binding_for(definition, format, options) }
48
+ class Binding::Map < Array
49
+ def call(method, options)
50
+ each do |bin|
51
+ options[:binding] = bin # this is so much faster than options.merge().
52
+ bin.send(method, options)
53
+ end
54
+ end
55
+
56
+ # TODO: Merge with Definitions.
57
+ def <<(binding) # can be slow. this is compile time code.
58
+ (existing = find { |bin| bin.name == binding.name }) ? self[index(existing)] = binding : super(binding)
59
+ end
60
+ end
61
+
62
+ def representable_map(options, format)
63
+ Binding::Map.new(representable_bindings_for(format, options))
64
+ end
65
+
66
+ def representable_map!(doc, propagated_options, format, method)
67
+ options = {doc: doc, _private: propagated_options[:_private], user_options: propagated_options, represented: represented, decorator: self}
68
+
69
+ representable_map(options, format).(method, options) # .(:uncompile_fragment, options)
53
70
  end
54
71
 
55
- def representable_binding_for(definition, format, options)
56
- format.build(definition, self)
72
+ def representable_bindings_for(format, options)
73
+ representable_attrs.collect {|definition| format.build(definition) }
57
74
  end
58
75
 
59
76
  # Make sure we do not change original options. However, private options like :include or :wrap are
60
77
  # not passed on to child representers.
61
- def normalize_options!(options)
78
+ def normalize_options(options)
62
79
  # here, we could also filter out local options e.g. like options[:band].
63
- private_options = {}
64
- return [options, private_options] if options.size == 0
80
+ return options unless options.any?
65
81
 
66
82
  propagated_options = options.dup
83
+ propagated_options.delete(:wrap) # FIXME.
84
+ propagated_options.delete(:_private)
67
85
 
86
+ private_options = {}
68
87
  private_options[:include] = propagated_options.delete(:include) if options[:include]
69
88
  private_options[:exclude] = propagated_options.delete(:exclude) if options[:exclude]
70
- propagated_options.delete(:wrap) # FIXME.
71
89
 
72
- [propagated_options, private_options]
73
- end
90
+ propagated_options[:_private] = private_options if private_options.any?
74
91
 
75
- def representable_attrs
76
- @representable_attrs ||= self.class.representable_attrs # DISCUSS: copy, or better not? what about "freezing"?
92
+ propagated_options
77
93
  end
78
94
 
79
- def representable_mapper(format, options)
80
- bindings = representable_bindings_for(format, options)
81
- Mapper.new(bindings)
95
+ def representable_attrs
96
+ @representable_attrs ||= self.class.definitions
82
97
  end
83
98
 
84
99
  def representation_wrap(*args)
85
- representable_attrs.wrap_for(nil, represented, *args) { self.class.name }
100
+ representable_attrs.wrap_for(represented, *args)
86
101
  end
87
102
 
88
103
  def represented
89
104
  self
90
105
  end
91
106
 
92
- module ClassInclusions
93
- def included(base)
94
- super
95
- base.inherit_module!(self)
96
- end
97
-
98
- def inherited(subclass) # DISCUSS: this could be in Decorator? but then we couldn't do B < A(include X) for non-decorators, right?
99
- super
100
- # FIXME: subclass.representable_attrs is ALWAYS empty at this point.
101
- subclass.representable_attrs.inherit!(representable_attrs) # this should be inherit_class!
102
- # DISCUSS: this could also just be: subclass.inheritable_attr :representable_attrs --> superclass.representable_attrs.clone
103
- end
104
- end
105
-
106
107
  module ModuleExtensions
107
108
  # Copies the representable_attrs reference to the extended object.
108
109
  # Note that changing attrs in the instance will affect the class configuration.
@@ -112,32 +113,18 @@ private
112
113
  end
113
114
  end
114
115
 
115
-
116
116
  module ClassMethods
117
- # Gets overridden by Decorator as inheriting representers via include in Decorator means a bit more work (manifesting).
118
- def inherit_module!(parent)
119
- representable_attrs.inherit!(parent.representable_attrs) # Module just inherits.
120
- end
121
-
122
117
  def prepare(represented)
123
118
  represented.extend(self)
124
119
  end
125
120
  end
126
121
 
127
-
128
- module Feature
129
- def feature(*mods)
130
- mods.each do |mod|
131
- include mod
132
- register_feature(mod)
133
- end
134
- end
135
-
136
- private
137
- def register_feature(mod)
138
- representable_attrs[:features][mod] = true
139
- end
122
+ require "representable/deprecations"
123
+ def self.deprecations=(value)
124
+ evaluator = value==false ? Binding::EvaluateOption : Binding::Deprecation::EvaluateOption
125
+ ::Representable::Binding.send :include, evaluator
140
126
  end
127
+ self.deprecations = true # TODO: change to false in 2.5 or remove entirely.
141
128
  end
142
129
 
143
- require 'representable/autoload'
130
+ require 'representable/autoload'
@@ -1,143 +1,57 @@
1
1
  module Representable
2
- # The Binding wraps the Definition instance for this property and provides methods to read/write fragments.
3
-
4
- # The flow when parsing is Binding#read_fragment -> Populator -> Deserializer.
5
- # Actual parsing the fragment from the document happens in Binding#read, everything after that is generic.
2
+ # The Binding provides methods to read/write the fragment for one property.
6
3
  #
7
- # Serialization: Serializer -> {frag}/[frag]/frag -> Binding#write
4
+ # Actually parsing the fragment from the document happens in Binding#read, everything after that is generic.
8
5
  class Binding
9
6
  class FragmentNotFound
10
7
  end
11
8
 
12
- def self.build(definition, *args)
13
- # DISCUSS: move #create_binding to this class?
14
- return definition.create_binding(*args) if definition[:binding]
15
- build_for(definition, *args)
9
+ def self.build(definition)
10
+ return definition.create_binding if definition[:binding]
11
+ build_for(definition)
16
12
  end
17
13
 
18
- def initialize(definition, parent_decorator)
14
+ def initialize(definition)
19
15
  @definition = definition
20
- @parent_decorator = parent_decorator # DISCUSS: where's this needed?
21
-
22
- # static options. do this once.
23
- @representable = @definition.representable?
24
16
  @name = @definition.name
25
- @skip_filters = self[:readable]==false || self[:writeable]==false || self[:if] # Does this binding contain :if, :readable or :writeable settings?
26
17
  @getter = @definition.getter
27
18
  @setter = @definition.setter
28
- @array = @definition.array?
29
- @typed = @definition.typed?
30
- @has_default = @definition.has_default?
31
- end
32
-
33
- attr_reader :user_options, :represented # TODO: make private/remove.
34
-
35
- # DISCUSS: an overall strategy to speed up option reads will come around 3.0.
36
- attr_reader :representable, :name, :getter, :setter, :array, :typed, :skip_filters, :has_default
37
- alias_method :representable?, :representable
38
- alias_method :array?, :array
39
- alias_method :typed?, :typed
40
- alias_method :skip_filters?, :skip_filters
41
- alias_method :has_default?, :has_default
42
19
 
43
- def as # DISCUSS: private?
44
- @as ||= evaluate_option(:as)
20
+ setup_exec_context!
45
21
  end
46
22
 
23
+ attr_reader :name, :getter, :setter
24
+ extend Uber::Delegates
25
+ delegates :@definition, :has_default?, :representable?, :array?, :typed?
26
+
47
27
  # Single entry points for rendering and parsing a property are #compile_fragment
48
28
  # and #uncompile_fragment in Mapper.
49
- #
50
- # DISCUSS:
51
- # currently, we need to call B#update! before compile_fragment/uncompile_fragment.
52
- # this will change to B#renderer(represented, options).call
53
- # B#parser (represented, options).call
54
- # goal is to have two objects for 2 entirely different tasks.
55
29
 
56
- # Retrieve value and write fragment to the doc.
57
- def compile_fragment(doc)
58
- evaluate_option(:writer, doc) do
59
- value = render_filter(get, doc)
60
- write_fragment(doc, value)
30
+ module Deprecatable
31
+ # Retrieve value and write fragment to the doc.
32
+ def compile_fragment(options)
33
+ render_pipeline(nil, options).(nil, options)
61
34
  end
62
- end
63
35
 
64
- # Parse value from doc and update the model property.
65
- def uncompile_fragment(doc)
66
- evaluate_option(:reader, doc) do
67
- read_fragment(doc)
36
+ # Parse value from doc and update the model property.
37
+ def uncompile_fragment(options)
38
+ parse_pipeline(options[:doc], options).(options[:doc], options)
68
39
  end
69
40
  end
41
+ include Deprecatable
70
42
 
71
- def write_fragment(doc, value)
72
- value = default_for(value)
73
-
74
- return if skipable_empty_value?(value)
75
-
76
- render_fragment(value, doc)
77
- end
78
-
79
- def render_fragment(value, doc)
80
- fragment = serialize(value) { return } # render fragments of hash, xml, yaml.
81
-
82
- write(doc, fragment)
83
- end
84
-
85
- def read_fragment(doc)
86
- fragment = read(doc) # scalar, Array, or Hash (abstract format) or un-deserialised fragment(s).
87
-
88
- populator.call(fragment, doc)
89
- end
90
-
91
- def render_filter(value, doc)
92
- evaluate_option(:render_filter, value, doc) { value }
93
- end
94
-
95
- def parse_filter(value, doc)
96
- evaluate_option(:parse_filter, value, doc) { value }
97
- end
98
-
99
- def get
100
- evaluate_option(:getter) do
101
- exec_context.send(getter)
43
+ module EvaluateOption
44
+ def evaluate_option(name, input=nil, options={})
45
+ proc = self[name]
46
+ proc.(send(:exec_context, options), options) # from Uber::Options::Value. # NOTE: this can also be the Proc object if it's not wrapped by Uber:::Value.
102
47
  end
103
48
  end
104
-
105
- def set(value)
106
- evaluate_option(:setter, value) do
107
- exec_context.send(setter, value)
108
- end
109
- end
110
-
111
- # DISCUSS: do we really need that?
112
- # 1.38 0.104 0.021 0.000 0.083 40001 Representable::Binding#representer_module_for
113
- # 1.13 0.044 0.017 0.000 0.027 40001 Representable::Binding#representer_module_for (with memoize).
114
- def representer_module_for(object, *args)
115
- # TODO: cache this!
116
- evaluate_option(:extend, object) # TODO: pass args? do we actually have args at the time this is called (compile-time)?
117
- end
118
-
119
- # Evaluate the option (either nil, static, a block or an instance method call) or
120
- # executes passed block when option not defined.
121
- def evaluate_option(name, *args)
122
- unless proc = @definition[name] # TODO: this could dispatch directly to the @definition using #send?
123
- return yield if block_given?
124
- return
125
- end
126
-
127
- # TODO: it would be better if user_options was nil per default and then we just don't pass it into lambdas.
128
- options = self[:pass_options] ? Options.new(self, user_options, represented, parent_decorator) : user_options
129
-
130
- proc.evaluate(exec_context, *(args<<options)) # from Uber::Options::Value.
131
- end
49
+ # include EvaluateOption
132
50
 
133
51
  def [](name)
134
52
  @definition[name]
135
53
  end
136
54
 
137
- # 1.55 0.031 0.022 0.000 0.009 60004 Representable::Binding#skipable_empty_value?
138
- # 1.51 0.030 0.022 0.000 0.008 60004 Representable::Binding#skipable_empty_value?
139
- # after polymorphism:
140
- # 1.44 0.031 0.022 0.000 0.009 60002 Representable::Binding#skipable_empty_value?
141
55
  def skipable_empty_value?(value)
142
56
  value.nil? and not self[:render_nil]
143
57
  end
@@ -147,112 +61,41 @@ module Representable
147
61
  value
148
62
  end
149
63
 
150
- # Note: this method is experimental.
151
- def update!(represented, user_options)
152
- @represented = represented
153
-
154
- setup_user_options!(user_options)
155
- setup_exec_context!
156
- end
157
-
158
64
  attr_accessor :cached_representer
159
65
 
160
- private
161
- def setup_user_options!(user_options)
162
- @user_options = user_options
163
- # this is the propagated_options.
164
- @user_options = user_options.merge(wrap: false) if self[:wrap] == false
66
+ require "representable/pipeline_factories"
67
+ include Factories
165
68
 
166
- # @user_options = user_options.merge(wrap: self[:wrap]) if self[:wrap]
167
- end
69
+ private
168
70
 
169
- # 1.80 0.066 0.027 0.000 0.039 30002 Representable::Binding#setup_exec_context!
170
- # 0.98 0.034 0.014 0.000 0.020 30002 Representable::Binding#setup_exec_context!
171
71
  def setup_exec_context!
172
- return @exec_context = @represented unless self[:exec_context]
173
- @exec_context = self if self[:exec_context] == :binding
174
- @exec_context = parent_decorator if self[:exec_context] == :decorator
72
+ @exec_context = ->(options) { options[:represented] } unless self[:exec_context]
73
+ @exec_context = ->(options) { self } if self[:exec_context] == :binding
74
+ @exec_context = ->(options) { options[:decorator] } if self[:exec_context] == :decorator
175
75
  end
176
76
 
177
- attr_reader :exec_context, :parent_decorator
178
-
179
- def serialize(object, &block)
180
- serializer.call(object, &block)
77
+ def exec_context(options)
78
+ @exec_context.(options)
181
79
  end
182
80
 
183
- module Factories
184
- def serializer_class
185
- Serializer
186
- end
187
-
188
- def serializer
189
- @serializer ||= serializer_class.new(self)
190
- end
191
-
192
- def populator
193
- @populator ||= populator_class.new(self)
194
- end
195
-
196
- def populator_class
197
- Populator
198
- end
199
-
200
- def deserializer_class
201
- Deserializer
202
- end
203
-
204
- def deserializer
205
- @deserializer ||= deserializer_class.new(self)
206
- end
81
+ def parse_pipeline(input, options)
82
+ @parse_pipeline ||= pipeline_for(:parse_pipeline, input, options) { Pipeline[*parse_functions] }
207
83
  end
208
- include Factories
209
-
210
-
211
- # Options instance gets passed to lambdas when pass_options: true.
212
- # This is considered the new standard way and should be used everywhere for forward-compat.
213
- Options = Struct.new(:binding, :user_options, :represented, :decorator)
214
84
 
85
+ def render_pipeline(input, options)
86
+ @render_pipeline ||= pipeline_for(:render_pipeline, input, options) { Pipeline[*render_functions] }
87
+ end
215
88
 
216
89
  # generics for collection bindings.
217
90
  module Collection
218
- private
219
- def populator_class
220
- Populator::Collection
221
- end
222
-
223
- def serializer_class
224
- Serializer::Collection
225
- end
226
-
227
- def deserializer_class
228
- Deserializer::Collection
229
- end
230
-
231
91
  def skipable_empty_value?(value)
232
92
  # TODO: this can be optimized, again.
233
93
  return true if value.nil? and not self[:render_nil] # FIXME: test this without the "and"
234
94
  return true if self[:render_empty] == false and value and value.size == 0 # TODO: change in 2.0, don't render emtpy.
235
95
  end
236
96
  end
237
-
238
- # and the same for hashes.
239
- module Hash
240
- private
241
- def populator_class
242
- Populator::Hash
243
- end
244
-
245
- def serializer_class
246
- Serializer::Hash
247
- end
248
-
249
- def deserializer_class
250
- Deserializer::Hash
251
- end
252
- end
253
97
  end
254
98
 
255
-
256
99
  class DeserializeError < RuntimeError
257
100
  end
258
101
  end