trax_core 0.0.84 → 0.0.85

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +2 -2
  4. data/lib/trax/core/definitions.rb +4 -4
  5. data/lib/trax/core/ext/hash.rb +15 -5
  6. data/lib/trax/core/ext/method.rb +95 -0
  7. data/lib/trax/core/ext/object.rb +7 -2
  8. data/lib/trax/core/fields.rb +4 -0
  9. data/lib/trax/core/has_mixins.rb +5 -1
  10. data/lib/trax/core/transformer.rb +244 -0
  11. data/lib/trax/core/types/boolean.rb +1 -0
  12. data/lib/trax/core/types/enum.rb +7 -8
  13. data/lib/trax/core/types/enum_value.rb +4 -10
  14. data/lib/trax/core/types/json.rb +8 -0
  15. data/lib/trax/core/types/struct.rb +45 -4
  16. data/lib/trax/core/types/value_object.rb +7 -1
  17. data/lib/trax/core.rb +13 -0
  18. data/lib/trax_core/version.rb +1 -1
  19. data/spec/support/defs.rb +29 -1
  20. data/spec/support/storefront/product.rb +2 -0
  21. data/spec/trax/array_spec.rb +2 -6
  22. data/spec/trax/core/definitions_spec.rb +76 -2
  23. data/spec/trax/core/eager_autoload_namespace_spec.rb +4 -4
  24. data/spec/trax/core/errors_spec.rb +10 -15
  25. data/spec/trax/core/ext/array_spec.rb +2 -2
  26. data/spec/trax/core/ext/class_spec.rb +3 -3
  27. data/spec/trax/core/ext/hash_spec.rb +10 -0
  28. data/spec/trax/core/ext/method_spec.rb +104 -0
  29. data/spec/trax/core/ext/module_spec.rb +5 -5
  30. data/spec/trax/core/ext/object_spec.rb +5 -5
  31. data/spec/trax/core/transformer_spec.rb +170 -0
  32. data/spec/trax/core/types/array_spec.rb +4 -4
  33. data/spec/trax/core/types/enum_spec.rb +23 -18
  34. data/spec/trax/core/types/struct_spec.rb +50 -7
  35. data/spec/trax/core/types/value_object_spec.rb +6 -0
  36. data/spec/trax/core_spec.rb +1 -3
  37. data/spec/trax/hash_spec.rb +13 -15
  38. data/trax_core.gemspec +7 -6
  39. metadata +97 -5
  40. data/spec/trax/core/inheritance_spec.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cc61a40b6e173df7ff14e66550fd7c5fa558ab4c
4
- data.tar.gz: 9b5f6b14a3b4c714b094f8020b66f3d532a79855
3
+ metadata.gz: 607a79a7c4082da1e2a834475a6d4f9cdd0ae5e5
4
+ data.tar.gz: 77f3a354d9981ac41cdbbc16c5786f9788c62ab7
5
5
  SHA512:
6
- metadata.gz: 18b3e5d1d9dc44a315bc88480c76b68ecb81a00d07dd41d14b831040ea3e62c7c696477ba9965c34f449a5f168aa35529f94ef3619f7ef2ebc99af8a5457bab7
7
- data.tar.gz: 1013d42d2b1900af67bd2e46edf65c0e28a59c301c400ae60423d945acf7d06ae2656b260a95d5baec813671323b50adabcfefb4438fd40102be00f349e47a47
6
+ metadata.gz: 10c480758ec396d9175a51272457c24fbd8d1da41a354a93bce0163760e60add4d177213d83e8faf780e9f0cc76177912453f7472d424c664a4bde73bde83743
7
+ data.tar.gz: aa5dadf5ea48099d9b44b7da1f0b54bd2520e2c98b80b3eed7106da6b465f3f3d490dda21bb0c240f2d489459e6645b8421ea562a9f2044a224b3e1475f16a6d
data/.gitignore CHANGED
@@ -8,6 +8,7 @@ InstalledFiles
8
8
  _yardoc
9
9
  coverage
10
10
  doc/
11
+ vendor/
11
12
  lib/bundler/man
12
13
  pkg
13
14
  rdoc
data/.travis.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  language: ruby
2
2
  script: bundle exec rspec
3
3
  rvm:
4
- - 2.2
5
- - 2.1
4
+ - 2.3.1
5
+ - 2.2.5
6
6
  notifications:
7
7
  email: false
@@ -6,8 +6,8 @@ module Trax
6
6
  end
7
7
 
8
8
  def enum(klass_name, **options, &block)
9
- attribute_klass = if options.key?(:extend)
10
- _klass_prototype = options[:extend].constantize.clone
9
+ attribute_klass = if options.key?(:extends)
10
+ _klass_prototype = options[:extends].constantize.clone
11
11
  ::Trax::Core::NamedClass.new("#{self.name}::#{klass_name}", _klass_prototype, :parent_definition => self, &block)
12
12
  else
13
13
  ::Trax::Core::NamedClass.new("#{self.name}::#{klass_name}", ::Trax::Core::Types::Enum, :parent_definition => self, &block)
@@ -17,8 +17,8 @@ module Trax
17
17
  end
18
18
 
19
19
  def struct(klass_name, **options, &block)
20
- attribute_klass = if options.key?(:extend)
21
- _klass_prototype = options[:extend].constantize.clone
20
+ attribute_klass = if options.key?(:extends)
21
+ _klass_prototype = options[:extends].constantize.clone
22
22
  ::Trax::Core::NamedClass.new("#{self.name}::#{klass_name}", _klass_prototype, :parent_definition => self, &block)
23
23
  else
24
24
  ::Trax::Core::NamedClass.new("#{self.name}::#{klass_name}", ::Trax::Core::Types::Struct, :parent_definition => self, &block)
@@ -1,21 +1,27 @@
1
- class Hash
1
+ module HashExtensions
2
+ def assert_required_keys(*args)
3
+ missing_args = args.reject{|arg| self.key?(arg) }
4
+ raise ArgumentError.new("Missing keys: #{missing_args.join(', ')}") if missing_args.any?
5
+ self
6
+ end
7
+
2
8
  ## Returns selected keys, named or renamed as specified
3
9
  # myproduct = {:name => "something", :price => "20"}
4
- # liability = myproduct.tap(&{:cost => :price})
10
+ # liability = myproduct.tap(&{:cost => :price}.to_transformer)
5
11
  # liability[:cost] == 20
6
12
  ## Note: Tap only works where source is a hash object, so use as otherwise
7
13
  # (because tap always returns the object you are tapping)
8
14
  # myproduct = ::OpenStruct.new({:name => "something", :price => "20"})
9
- # liability.as!(&{:cost => :price})
15
+ # liability.as!({:cost => :price})
10
16
  # liability[:cost] == 20
11
17
  #
12
18
  # Transforming values:
13
19
  # Pass a hash as the value with the key being the source key/method
14
20
  # myproduct = ::OpenStruct.new({:name => "something", :price => "20"})
15
- # my_sale_product = myproduct.as!(&{:sale_price => {:price => ->(val){ val / 2 } } })
21
+ # my_sale_product = myproduct.as!({:sale_price => {:price => ->(val){ val / 2 } } })
16
22
  # my_sale_product[:sale_price] == 10
17
23
 
18
- def to_proc
24
+ def to_transformer
19
25
  ->(hash_or_object) {
20
26
  new_hash = {}
21
27
 
@@ -48,3 +54,7 @@ class Hash
48
54
  }
49
55
  end
50
56
  end
57
+
58
+ class Hash
59
+ include HashExtensions
60
+ end
@@ -0,0 +1,95 @@
1
+ module MethodExtensions
2
+ # method(method_name).parameters returns an array of the parameters it accepts as well as the signature type
3
+ # :req = required, ordinal argument, i.e. def foo(one)
4
+ # :opt = optional ordinal argument, i.e. def foo(one=nil)
5
+ # :keyreq = required keyword argument i.e. def foo(req:)
6
+ # :key = optional keyword argument, i.e. def foo(one:nil)
7
+ # :rest = optional arguments splat, i.e. def foo(*args)
8
+ # :keyrest = optional keyword arguments splat, i.e. def foo(**args)
9
+
10
+ STRATEGIES_FOR_SEND_WHEN_METHOD = {
11
+ :accepts_nothing? => :strategy_for_method_without_arguments,
12
+ :accepts_arguments_and_keywords? => :strategy_for_method_with_arguments_and_keywords,
13
+ :accepts_arguments? => :strategy_for_method_with_arguments,
14
+ :accepts_keywords? => :strategy_for_method_with_keywords
15
+ }.freeze
16
+
17
+ def accepted_argument_signatures
18
+ @accepted_argument_signatures ||= self.parameters.any? ? self.parameters.map(&:first).uniq : []
19
+ end
20
+
21
+ def accepts_something?
22
+ @accepts_something ||= arity != 0
23
+ end
24
+
25
+ def accepts_nothing?
26
+ !accepts_something?
27
+ end
28
+
29
+ def accepts_arguments?
30
+ @accepts_arguments ||= requires_arguments? || accepts_optional_arguments? || accepts_arguments_splat?
31
+ end
32
+
33
+ def accepts_keywords?
34
+ @accepts_keywords ||= requires_keywords? || accepts_optional_keywords? || accepts_keywords_splat?
35
+ end
36
+
37
+ def accepts_arguments_and_keywords?
38
+ @accepts_arguments_and_keywords ||= accepts_arguments? && accepts_keywords?
39
+ end
40
+
41
+ def accepts_arguments_splat?
42
+ @accepts_arguments_splat ||= accepted_argument_signatures.include?(:rest)
43
+ end
44
+
45
+ def accepts_keywords_splat?
46
+ @accepts_keywords_splat ||= accepted_argument_signatures.include?(:keyrest)
47
+ end
48
+
49
+ def accepts_optional_arguments?
50
+ @accepts_optional_arguments ||= accepted_argument_signatures.include?(:opt)
51
+ end
52
+
53
+ def accepts_optional_keywords?
54
+ @accepts_optional_keywords ||= accepted_argument_signatures.include?(:key)
55
+ end
56
+
57
+ def execute_call_strategy(*args, **options)
58
+ __send__(strategy_for_call)
59
+ end
60
+
61
+ def requires_arguments?
62
+ @requires_arguments ||= accepted_argument_signatures.include?(:req)
63
+ end
64
+
65
+ def requires_keywords?
66
+ @requires_keywords ||= accepted_argument_signatures.include?(:keyreq)
67
+ end
68
+
69
+ def strategy_for_method_without_arguments(*args, **options)
70
+ call()
71
+ end
72
+
73
+ def strategy_for_method_with_keywords(*args, **options)
74
+ call(**options)
75
+ end
76
+
77
+ def strategy_for_method_with_arguments(*args, **options)
78
+ call(*args)
79
+ end
80
+
81
+ def strategy_for_method_with_arguments_and_keywords(*args, **options)
82
+ call(*args, **options)
83
+ end
84
+
85
+ def strategy_for_call
86
+ @strategy_for_call ||= begin
87
+ first_matching_question = STRATEGIES_FOR_SEND_WHEN_METHOD.keys.detect{ |k| send(k) }
88
+ STRATEGIES_FOR_SEND_WHEN_METHOD[first_matching_question]
89
+ end
90
+ end
91
+ end
92
+
93
+ class Method
94
+ include MethodExtensions
95
+ end
@@ -1,7 +1,12 @@
1
1
  require "active_support/core_ext/object/try"
2
2
  class Object
3
- def as!
4
- yield self
3
+ def __smartsend__(method_name, *args, **options)
4
+ target = method(method_name)
5
+ target.execute_call_strategy(*args, **options)
6
+ end
7
+
8
+ def as!(h)
9
+ h.to_transformer.call(self)
5
10
  end
6
11
 
7
12
  # Defines a Configuration Class within a target module namespace, or nested class
@@ -38,6 +38,10 @@ module Trax
38
38
  @enums ||= by_type(:enum)
39
39
  end
40
40
 
41
+ def key?(k)
42
+ all.key?(k)
43
+ end
44
+
41
45
  def structs
42
46
  @structs ||= by_type(:struct)
43
47
  end
@@ -21,7 +21,11 @@ module Trax
21
21
 
22
22
  mixin_module = base.const_set("Mixin", ::Module.new)
23
23
  mixin_module.module_attribute(:mixin_namespace) { base }
24
- mixin_module.extend(::Trax::Core::Mixin)
24
+
25
+ # NOTE: This line causes specs to fail, because it loads before
26
+ # ::Trax::Core::Definitions. It's currently not being used by any other
27
+ # Trax gems, so we'll have to revisit this whenever they start using it
28
+ #mixin_module.extend(::Trax::Core::Mixin)
25
29
 
26
30
  mixin_module.module_eval do
27
31
  def self.extended(base)
@@ -0,0 +1,244 @@
1
+ module Trax
2
+ module Core
3
+ class Transformer < SimpleDelegator
4
+ attr_reader :input, :parent, :output
5
+
6
+ def self.inherited(subklass)
7
+ subklass.class_attribute :properties
8
+ subklass.properties = {}.with_indifferent_access
9
+ subklass.class_attribute :after_initialize_callbacks
10
+ subklass.after_initialize_callbacks = ::Set.new
11
+ subklass.class_attribute :after_transform_callbacks
12
+ subklass.after_transform_callbacks = ::Set.new
13
+ end
14
+
15
+ def self.after_initialize(&block)
16
+ after_initialize_callbacks << block
17
+ end
18
+
19
+ def self.after_transform(&block)
20
+ after_transform_callbacks << block
21
+ end
22
+
23
+ def self.properties_with_default_values
24
+ @properties_with_default_values ||= properties.values.select{ |prop| prop.try(:default) }
25
+ end
26
+
27
+ def self.nested_properties
28
+ @nested_properties ||= properties.values.select{|prop| prop.is_nested? }
29
+ end
30
+
31
+ def self.transformer_properties
32
+ @transformer_properties ||= properties.values.select{|prop| prop.ancestors.include?(::Trax::Core::Transformer) }
33
+ end
34
+
35
+ def self.transformer_properties_with_after_transform_callbacks
36
+ @transformer_properties_with_after_transform_callbacks ||= transformer_properties.select{|prop| prop.after_transform_callbacks.any? }
37
+ end
38
+
39
+ def self.is_nested?
40
+ !!self.try(:parent_definition)
41
+ end
42
+
43
+ def self.from_parent?
44
+ false
45
+ end
46
+
47
+ def self.property(_property_name, **options, &block)
48
+ options[:parent_definition] = self
49
+ options[:property_name] = _property_name
50
+ options[:with] = block if block_given?
51
+ transformer_klass_name = "#{name}::#{_property_name.camelize}"
52
+ transformer_klass = ::Trax::Core::NamedClass.new(transformer_klass_name, Property, **options)
53
+ self.properties[_property_name] = transformer_klass
54
+ end
55
+
56
+ def self.transformer(_property_name, **options, &block)
57
+ options[:parent_definition] = self
58
+ options[:property_name] = _property_name
59
+ options[:default] = ->(){ {}.with_indifferent_access } unless options.key?(:default)
60
+ options[:with] = block if block_given?
61
+ transformer_klass_name = "#{name}::#{_property_name.camelize}"
62
+ transformer_klass = ::Trax::Core::NamedClass.new(transformer_klass_name, Transformer, **options, &block)
63
+ self.properties[_property_name] = transformer_klass
64
+ end
65
+
66
+ def self.nested(*args, **options, &block)
67
+ transformer(*args, **options, &block)
68
+ end
69
+
70
+ def self.fetch_property_from_object(_property, obj)
71
+ if _property.include?('/')
72
+ property_chain = _property.split('/')
73
+ obj.dig(*property_chain)
74
+ else
75
+ obj[_property]
76
+ end
77
+ end
78
+
79
+ def initialize(obj={}, parent=nil)
80
+ @input = obj.dup
81
+ @output = {}.with_indifferent_access
82
+ @parent = parent if parent
83
+
84
+ initialize_output_properties
85
+ initialize_default_values
86
+ run_after_initialize_callbacks if run_after_initialize_callbacks?
87
+ run_after_transform_callbacks if run_after_transform_callbacks?
88
+ end
89
+
90
+ def [](_property)
91
+ if _property.include?('/')
92
+ property_chain = _property.split('/')
93
+ self.dig(*property_chain)
94
+ else
95
+ super(_property)
96
+ end
97
+ end
98
+
99
+ def key?(_property)
100
+ if _property.include?('/')
101
+ property_chain = _property.split('/')
102
+ !!self.dig(*property_chain)
103
+ else
104
+ super(_property)
105
+ end
106
+ end
107
+
108
+ def has_parent?
109
+ !!parent
110
+ end
111
+
112
+ def parent_key?(k)
113
+ return false unless has_parent?
114
+
115
+ parent.key?(k)
116
+ end
117
+
118
+ def __getobj__
119
+ @output
120
+ end
121
+
122
+ def to_hash
123
+ @to_hash ||= begin
124
+ duplicate_hash = self.__getobj__.dup
125
+
126
+ duplicate_hash.each_pair do |k, v|
127
+ if v.is_a?(::Trax::Core::Transformer)
128
+ duplicate_hash[k] = v.__getobj__
129
+ elsif v.is_a?(Property)
130
+ duplicate_hash[k] = v.__getobj__
131
+ end
132
+ end
133
+
134
+ duplicate_hash
135
+ end
136
+ end
137
+
138
+ def to_recursive_hash
139
+ @to_recursive_hash ||= begin
140
+ duplicate_hash = self.__getobj__.dup
141
+
142
+ self.each_pair do |k, v|
143
+ if v.is_a?(::Trax::Core::Transformer)
144
+ duplicate_hash[k] = v.to_hash
145
+ elsif v.is_a?(Property)
146
+ duplicate_hash[k] = v.__getobj__
147
+ end
148
+ end
149
+
150
+ duplicate_hash
151
+ end
152
+ end
153
+
154
+ private
155
+
156
+ def initialize_default_values
157
+ self.class.properties_with_default_values.each do |prop|
158
+ unless @output.key?(prop.property_name)
159
+ if prop.default.is_a?(Proc)
160
+ @output[prop.property_name] = prop.default.arity > 0 ? prop.default.call(@output) : prop.default.call
161
+ else
162
+ @output[prop.property_name] = prop.default
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ def initialize_output_properties
169
+ self.class.properties.each_pair do |k,property_klass|
170
+ if @input.key?(property_klass.property_name)
171
+ value = @input[property_klass.property_name]
172
+ @output[property_klass.property_name] = property_klass.new(value, self)
173
+ elsif @input.key?(property_klass.try(:from))
174
+ value = @input[property_klass.from]
175
+ @output[property_klass.property_name] = property_klass.new(value, self)
176
+ elsif property_klass.from_parent?
177
+ value = self.class.fetch_property_from_object(property_klass.from_parent, self.parent.input)
178
+ @output[property_klass.property_name] = property_klass.new(value, self)
179
+ elsif property_klass.ancestors.include?(::Trax::Core::Transformer)
180
+ value = if property_klass.default.is_a?(Proc)
181
+ property_klass.default.arity > 0 ? property_klass.default.call(self) : property_klass.default.call
182
+ else
183
+ property_klass.default
184
+ end
185
+
186
+ @output[property_klass.property_name] = property_klass.new(value, self)
187
+ end
188
+ end
189
+ end
190
+
191
+ #will not transform output based on callback result
192
+ def run_after_initialize_callbacks
193
+ self.class.after_initialize_callbacks.each do |callback|
194
+ @output.instance_eval(&callback)
195
+ end
196
+ end
197
+
198
+ def run_after_initialize_callbacks?
199
+ self.class.after_initialize_callbacks.any?
200
+ end
201
+
202
+ #will transform output with return of each callback
203
+ def run_after_transform_callbacks
204
+ self.class.after_transform_callbacks.each do |callback|
205
+ @output = self.instance_exec(@output, &callback)
206
+ end
207
+ end
208
+
209
+ def run_after_transform_callbacks?
210
+ self.class.after_transform_callbacks.any?
211
+ end
212
+ end
213
+
214
+ class Property < SimpleDelegator
215
+ def self.is_nested?
216
+ @is_nested ||= !!self.try(:parent_definition)
217
+ end
218
+
219
+ def self.is_translated?
220
+ @is_translated ||= !!self.try(:from)
221
+ end
222
+
223
+ def self.from_parent?
224
+ @from_parent ||= !!try(:from_parent)
225
+ end
226
+
227
+ def initialize(value, transformer)
228
+ @value = value
229
+
230
+ if self.class.try(:with)
231
+ @value = self.class.with.arity > 1 ? self.class.with.call(@value, transformer) : self.class.with.call(@value)
232
+ end
233
+ end
234
+
235
+ def nil?
236
+ __getobj__.nil?
237
+ end
238
+
239
+ def __getobj__
240
+ @value
241
+ end
242
+ end
243
+ end
244
+ end
@@ -9,6 +9,7 @@ module Trax
9
9
  def self.to_schema
10
10
  result = super
11
11
  result[:values] = [true, false]
12
+ result
12
13
  end
13
14
  end
14
15
  end
@@ -15,7 +15,7 @@ module Trax
15
15
  class_attribute :allow_nil, :raise_on_invalid
16
16
 
17
17
  ### Class Methods ###
18
- def self.define_enum_value(const_name, val=nil, **attributes)
18
+ def self.define_enum_value(const_name, val=nil, **attributes, &block)
19
19
  name = "#{const_name}".underscore.to_sym
20
20
  const_name = name.to_s.camelize
21
21
  val = (self._values_hash.length + 1) if val.nil?
@@ -23,11 +23,8 @@ module Trax
23
23
  raise ::Trax::Core::Errors::DuplicateEnumValue.new(:klass => self.class.name, :value => const_name) if self === name
24
24
  raise ::Trax::Core::Errors::DuplicateEnumValue.new(:klass => self.class.name, :value => val) if self === val
25
25
 
26
- value_klass = ::Trax::Core::NamedClass.new("#{self.name}::#{const_name}", ::Trax::Core::Types::EnumValue){
27
- self.tag = name
28
- self.value = val
29
- self.attributes = attributes
30
- }
26
+ value_klass_class_attributes = {:tag => name, :value => val, :attributes => attributes}
27
+ value_klass = ::Trax::Core::NamedClass.new("#{self.name}::#{const_name}", ::Trax::Core::Types::EnumValue, **value_klass_class_attributes, &block)
31
28
 
32
29
  self._values_hash[val] = value_klass
33
30
  self._names_hash[name] = value_klass
@@ -134,13 +131,15 @@ module Trax
134
131
  end
135
132
 
136
133
  def self.to_schema
137
- ::Trax::Core::Definition.new(
134
+ result = ::Trax::Core::Definition.new(
138
135
  :name => self.name.demodulize.underscore,
139
136
  :source => self.name,
140
- :type => :enum,
137
+ :type => self.type,
141
138
  :choices => choices.map(&:to_schema),
142
139
  :values => keys
143
140
  )
141
+ result[:default] = self.default if self.respond_to?(:default)
142
+ result
144
143
  end
145
144
 
146
145
  class << self
@@ -2,13 +2,6 @@ module Trax
2
2
  module Core
3
3
  module Types
4
4
  class EnumValue
5
- def self.inherited(subclass)
6
- super(subclass)
7
- self.class_attribute(:tag)
8
- self.class_attribute(:value)
9
- self.class_attribute(:attributes)
10
- end
11
-
12
5
  def self.as_json(options={})
13
6
  tag.to_s
14
7
  end
@@ -42,12 +35,13 @@ module Trax
42
35
  :source => self.name,
43
36
  :name => to_s,
44
37
  :type => :enum_value,
45
- :integer_value => to_i
38
+ :integer_value => to_i,
39
+ :attributes => attributes
46
40
  )
47
41
  end
48
42
 
49
43
  def self.inspect
50
- ":#{tag}"
44
+ tag ? ":#{tag}" : super
51
45
  end
52
46
 
53
47
  def self.include?(val)
@@ -60,7 +54,7 @@ module Trax
60
54
  end
61
55
 
62
56
  def self.===(val)
63
- [tag, to_s, to_i].include?(val)
57
+ [::Trax::Core::Types::Enum, tag, to_s, to_i].include?(val)
64
58
  end
65
59
  end
66
60
  end
@@ -5,6 +5,14 @@ module Trax
5
5
  def self.type
6
6
  :json
7
7
  end
8
+
9
+ def self.to_schema
10
+ ::Trax::Core::Definition.new(
11
+ :name => self.name.demodulize.underscore,
12
+ :source => self.name,
13
+ :type => self.type
14
+ )
15
+ end
8
16
  end
9
17
  end
10
18
  end
@@ -19,11 +19,11 @@ module Trax
19
19
  :array_of => [],
20
20
  :boolean => nil,
21
21
  :enum => nil,
22
- :float => 0.0,
22
+ :float => nil,
23
23
  :integer => nil,
24
24
  :json => {},
25
25
  :set => [],
26
- :string => "",
26
+ :string => nil,
27
27
  :struct => {},
28
28
  :time => nil
29
29
  }.with_indifferent_access.freeze
@@ -134,10 +134,51 @@ module Trax
134
134
  alias :time :time_property
135
135
  end
136
136
 
137
+ def reverse_merge(other_hash)
138
+ self.class.new(other_hash).merge(self)
139
+ end
140
+
141
+ def reverse_merge!(other_hash)
142
+ self.class.new(other_hash).merge!(self)
143
+ end
144
+
145
+ def reverse_merge_present_values_only
146
+ other = other_hash.delete_if{|k,v| v.nil? || self.value_present_for_key?(k) }
147
+ other.keys.each_with_object(self) do |k, result|
148
+ self.delete(k) if !self.value_present_for_key?(k)
149
+ end
150
+
151
+ self.merge(other)
152
+ end
153
+
154
+ def reverse_merge_present_values_only!(other_hash)
155
+ other = other_hash.delete_if{|k,v| v.nil? || self.value_present_for_key?(k) }
156
+ other.keys.each_with_object(self) do |k, result|
157
+ self.delete(k) if !self.value_present_for_key?(k)
158
+ end
159
+
160
+ self.merge!(other)
161
+ end
162
+
137
163
  def value
138
164
  self
139
165
  end
140
166
 
167
+ def value_present_for_key?(k)
168
+ case self.class.fields_module.all[k].type
169
+ when :struct
170
+ !self.__send__(k).empty?
171
+ when :boolean
172
+ !self.__send__(k).nil?
173
+ when :array, :set
174
+ self.__send__(k) && self.__send__(k).length > 0
175
+ when :string
176
+ self.__send__(k).present?
177
+ else
178
+ !self.__send__(k).nil?
179
+ end
180
+ end
181
+
141
182
  private
142
183
 
143
184
  #By default, strings/int/bool wont get cast to value objects
@@ -146,8 +187,8 @@ module Trax
146
187
  name = name.is_a?(::Symbol) ? name.to_s : name
147
188
  klass_name = "#{fields_module.name.underscore}/#{property_name}".camelize
148
189
 
149
- attribute_klass = if options.key?(:extend)
150
- _klass_prototype = options[:extend].is_a?(::String) ? options[:extend].safe_constantize : options[:extend]
190
+ attribute_klass = if options.key?(:extends)
191
+ _klass_prototype = options[:extends].is_a?(::String) ? options[:extends].safe_constantize : options[:extends]
151
192
  _klass = ::Trax::Core::NamedClass.new(klass_name, _klass_prototype, :parent_definition => self, **options, &block)
152
193
  _klass
153
194
  else
@@ -10,6 +10,10 @@ module Trax
10
10
  @value
11
11
  end
12
12
 
13
+ def nil?
14
+ @value.nil?
15
+ end
16
+
13
17
  def self.symbolic_name
14
18
  name.demodulize.underscore.to_sym
15
19
  end
@@ -19,11 +23,13 @@ module Trax
19
23
  end
20
24
 
21
25
  def self.to_schema
22
- ::Trax::Core::Definition.new(
26
+ result = ::Trax::Core::Definition.new(
23
27
  :name => self.name.demodulize.underscore,
24
28
  :source => self.name,
25
29
  :type => self.type
26
30
  )
31
+ result[:default] = self.default if self.respond_to?(:default)
32
+ result
27
33
  end
28
34
  end
29
35
  end