virtus 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/Changelog.md +10 -2
  2. data/Guardfile +9 -0
  3. data/README.md +12 -8
  4. data/TODO +0 -35
  5. data/config/flay.yml +1 -1
  6. data/config/site.reek +3 -1
  7. data/lib/virtus.rb +1 -0
  8. data/lib/virtus/attribute.rb +1 -1
  9. data/lib/virtus/attribute/collection.rb +2 -2
  10. data/lib/virtus/attribute/default_value/from_symbol.rb +2 -2
  11. data/lib/virtus/attribute_set.rb +4 -17
  12. data/lib/virtus/attributes_accessor.rb +22 -20
  13. data/lib/virtus/class_methods.rb +1 -1
  14. data/lib/virtus/coercion/hash.rb +1 -1
  15. data/lib/virtus/coercion/object.rb +1 -1
  16. data/lib/virtus/coercion/string.rb +20 -7
  17. data/lib/virtus/coercion/time_coercions.rb +5 -1
  18. data/lib/virtus/instance_methods.rb +8 -8
  19. data/lib/virtus/support/type_lookup.rb +24 -8
  20. data/lib/virtus/value_object/equalizer.rb +3 -2
  21. data/lib/virtus/version.rb +1 -1
  22. data/spec/shared/options_class_method.rb +1 -1
  23. data/spec/spec_helper.rb +1 -1
  24. data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +1 -7
  25. data/spec/unit/virtus/attribute/coerce_spec.rb +2 -2
  26. data/spec/unit/virtus/attribute/collection/coerce_and_append_member_spec.rb +16 -0
  27. data/spec/unit/virtus/attribute/collection/coerce_spec.rb +20 -7
  28. data/spec/unit/virtus/attribute/collection/member_coercion/coerce_and_append_member_spec.rb +18 -0
  29. data/spec/unit/virtus/attribute/collection/member_type_spec.rb +17 -0
  30. data/spec/unit/virtus/attribute/collection/new_collection_spec.rb +9 -0
  31. data/spec/unit/virtus/attribute/default_value/class_methods/build_spec.rb +25 -0
  32. data/spec/unit/virtus/attribute/default_value/evaluate_spec.rb +0 -43
  33. data/spec/unit/virtus/attribute/default_value/from_callable/class_methods/handle_spec.rb +20 -0
  34. data/spec/unit/virtus/attribute/default_value/from_callable/evaluate_spec.rb +21 -0
  35. data/spec/unit/virtus/attribute/default_value/from_clonable/class_methods/handle_spec.rb +20 -0
  36. data/spec/unit/virtus/attribute/default_value/from_clonable/evaluate_spec.rb +22 -0
  37. data/spec/unit/virtus/attribute/default_value/from_symbol/class_methods/handle_spec.rb +20 -0
  38. data/spec/unit/virtus/attribute/default_value/from_symbol/evaluate_spec.rb +18 -0
  39. data/spec/unit/virtus/attribute_set/element_set_spec.rb +5 -4
  40. data/spec/unit/virtus/attributes_accessor/define_reader_method_spec.rb +36 -0
  41. data/spec/unit/virtus/attributes_accessor/define_writer_method_spec.rb +36 -0
  42. data/spec/unit/virtus/coercion/array/{to_set_spec.rb → class_methods/to_set_spec.rb} +0 -0
  43. data/spec/unit/virtus/coercion/{class_name_reference_spec.rb → class_methods/element_reference_spec.rb} +0 -0
  44. data/spec/unit/virtus/coercion/class_methods/primitive_spec.rb +13 -0
  45. data/spec/unit/virtus/coercion/decimal/class_methods/to_decimal_spec.rb +10 -0
  46. data/spec/unit/virtus/coercion/float/class_methods/to_float_spec.rb +10 -0
  47. data/spec/unit/virtus/coercion/integer/class_methods/to_integer_spec.rb +10 -0
  48. data/spec/unit/virtus/coercion/numeric/class_methods/to_decimal_spec.rb +0 -0
  49. data/spec/unit/virtus/coercion/numeric/class_methods/to_float_spec.rb +0 -0
  50. data/spec/unit/virtus/coercion/numeric/class_methods/to_integer_spec.rb +0 -0
  51. data/spec/unit/virtus/coercion/numeric/class_methods/to_string_spec.rb +0 -0
  52. data/spec/unit/virtus/coercion/object/class_methods/to_string_spec.rb +1 -1
  53. data/spec/unit/virtus/coercion/string/class_methods/to_symbol_spec.rb +10 -0
  54. data/spec/unit/virtus/coercion/time/class_methods/to_integer_spec.rb +11 -0
  55. data/spec/unit/virtus/instance_methods/attributes_spec.rb +25 -0
  56. data/spec/unit/virtus/instance_methods/initialize_spec.rb +42 -0
  57. data/spec/unit/virtus/type_lookup/class_methods/extended_spec.rb +10 -0
  58. data/spec/unit/virtus/value_object/class_methods/attribute_spec.rb +16 -0
  59. data/spec/unit/virtus/value_object/equalizer/append_spec.rb +61 -0
  60. data/virtus.gemspec +4 -3
  61. metadata +80 -75
  62. data/spec/unit/virtus/attribute_set/parent_spec.rb +0 -11
@@ -1,4 +1,12 @@
1
- # v0.3.0 to-be-released
1
+ # v0.4.0 2012-03-22
2
+
3
+ * [improvement] Add a caching mechanism for type lookups (solnic)
4
+ * [fixed] Fix attributes mass-assignment when nil is passed (fgrehm)
5
+ * [changed] Replace usage of #to_hash with Hash.try_convert (dkubb)
6
+
7
+ [Compare v0.3.0..v0.4.0](https://github.com/solnic/virtus/compare/v0.3.0...master)
8
+
9
+ # v0.3.0 2012-02-25
2
10
 
3
11
  * [feature] Support for default values from a symbol (which can be a method name) (solnic)
4
12
  * [feature] Support for mass-assignment via custom setters not generated with attribute (fgrehm)
@@ -13,7 +21,7 @@
13
21
  * [BREAKING CHANGE] Removed Attribute#instance_variable_name - this is a private ivar (solnic)
14
22
  * [BREAKING CHANGE] Removed Equalizer#host_name and Equalizer#keys (solnic)
15
23
 
16
- [Compare v0.2.0..master](https://github.com/solnic/virtus/compare/v0.2.0...master)
24
+ [Compare v0.2.0..v0.3.0](https://github.com/solnic/virtus/compare/v0.2.0...v0.3.0)
17
25
 
18
26
  # v0.2.0 2012-02-08
19
27
 
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 1 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/README.md CHANGED
@@ -61,13 +61,16 @@ class Page
61
61
  include Virtus
62
62
 
63
63
  attribute :title, String
64
-
64
+
65
65
  # default from a singleton value (integer in this case)
66
66
  attribute :views, Integer, :default => 0
67
-
67
+
68
+ # default from a singleton value (boolean in this case)
69
+ attribute :published, Boolean, :default => false
70
+
68
71
  # default from a callable object (proc in this case)
69
72
  attribute :slug, String, :default => lambda { |page, attribute| page.title.downcase.gsub(' ', '-') }
70
-
73
+
71
74
  # default from a method name as symbol
72
75
  attribute :editor_title, String, :default => :default_editor_title
73
76
 
@@ -76,10 +79,11 @@ class Page
76
79
  end
77
80
  end
78
81
 
79
- page = Page.new(:title => 'Virtus Is Awesome', :editor_title => 'Virtus README')
80
- page.slug # => 'virtus-is-awesome'
81
- page.views # => 0
82
- page.editor_title # => "UNPUBSLISHED: Virtus README"
82
+ page = Page.new(:title => 'Virtus README')
83
+ page.slug # => 'virtus-readme'
84
+ page.views # => 0
85
+ page.published # => false
86
+ page.editor_title # => "UNPUBLISHED: Virtus README"
83
87
  ```
84
88
 
85
89
  **Embedded Value**
@@ -238,7 +242,7 @@ user.password # => '3858f62230ac3c915f300c664312c63f'
238
242
  ``` ruby
239
243
  require 'json'
240
244
 
241
- module MyAppClass
245
+ module MyApp
242
246
 
243
247
  # Defining the custom attribute(s)
244
248
  module Attributes
data/TODO CHANGED
@@ -1,36 +1 @@
1
- * Add missing specs:
2
- * Add spec file spec/unit/virtus/attribute/collection/member_coercion/coerce_and_append_member_spec.rb for Virtus::Attribute::Collection::MemberCoercion#coerce_and_append_member
3
- * Add spec file spec/unit/virtus/attribute/collection/member_type_spec.rb for Virtus::Attribute::Collection#member_type
4
- * Add spec file spec/unit/virtus/attribute/collection/new_collection_spec.rb for Virtus::Attribute::Collection#new_collection
5
- * Add spec file spec/unit/virtus/attribute/collection/coerce_and_append_member_spec.rb for Virtus::Attribute::Collection#coerce_and_append_member
6
- * Add spec file spec/unit/virtus/attribute/default_value/from_symbol/class_methods/handle_spec.rb for Virtus::Attribute::DefaultValue::FromSymbol.handle?
7
- * Add spec file spec/unit/virtus/attribute/default_value/from_symbol/evaluate_spec.rb for Virtus::Attribute::DefaultValue::FromSymbol#evaluate
8
- * Add spec file spec/unit/virtus/attribute/default_value/from_callable/class_methods/handle_spec.rb for Virtus::Attribute::DefaultValue::FromCallable.handle?
9
- * Add spec file spec/unit/virtus/attribute/default_value/from_callable/evaluate_spec.rb for Virtus::Attribute::DefaultValue::FromCallable#evaluate
10
- * Add spec file spec/unit/virtus/attribute/default_value/from_clonable/class_methods/handle_spec.rb for Virtus::Attribute::DefaultValue::FromClonable.handle?
11
- * Add spec file spec/unit/virtus/attribute/default_value/from_clonable/evaluate_spec.rb for Virtus::Attribute::DefaultValue::FromClonable#evaluate
12
- * Add spec file spec/unit/virtus/attribute/default_value/class_methods/build_spec.rb for Virtus::Attribute::DefaultValue.build
13
- * Add spec file spec/unit/virtus/coercion/string/class_methods/to_symbol_spec.rb for Virtus::Coercion::String.to_symbol
14
- * Add spec file spec/unit/virtus/coercion/time/class_methods/to_integer_spec.rb for Virtus::Coercion::Time.to_integer
15
- * Add spec file spec/unit/virtus/coercion/time_coercions/to_datetime_spec.rb for Virtus::Coercion::TimeCoercions#to_datetime
16
- * Add spec file spec/unit/virtus/coercion/time_coercions/to_time_spec.rb for Virtus::Coercion::TimeCoercions#to_time
17
- * Add spec file spec/unit/virtus/coercion/time_coercions/to_date_spec.rb for Virtus::Coercion::TimeCoercions#to_date
18
- * Add spec file spec/unit/virtus/coercion/time_coercions/to_string_spec.rb for Virtus::Coercion::TimeCoercions#to_string
19
- * Add spec file spec/unit/virtus/coercion/array/class_methods/to_set_spec.rb for Virtus::Coercion::Array.to_set
20
- * Add spec file spec/unit/virtus/coercion/decimal/class_methods/to_decimal_spec.rb for Virtus::Coercion::Decimal.to_decimal
21
- * Add spec file spec/unit/virtus/coercion/float/class_methods/to_float_spec.rb for Virtus::Coercion::Float.to_float
22
- * Add spec file spec/unit/virtus/coercion/integer/class_methods/to_integer_spec.rb for Virtus::Coercion::Integer.to_integer
23
- * Add spec file spec/unit/virtus/coercion/numeric/class_methods/to_decimal_spec.rb for Virtus::Coercion::Numeric.to_decimal
24
- * Add spec file spec/unit/virtus/coercion/numeric/class_methods/to_float_spec.rb for Virtus::Coercion::Numeric.to_float
25
- * Add spec file spec/unit/virtus/coercion/numeric/class_methods/to_integer_spec.rb for Virtus::Coercion::Numeric.to_integer
26
- * Add spec file spec/unit/virtus/coercion/numeric/class_methods/to_string_spec.rb for Virtus::Coercion::Numeric.to_string
27
- * Add spec file spec/unit/virtus/coercion/class_methods/element_reference_spec.rb for Virtus::Coercion.[]
28
- * Add spec file spec/unit/virtus/coercion/class_methods/primitive_spec.rb for Virtus::Coercion.primitive
29
- * Add spec file spec/unit/virtus/value_object/class_methods/attribute_spec.rb for Virtus::ValueObject::ClassMethods#attribute
30
- * Add spec file spec/unit/virtus/value_object/instance_methods/with_spec.rb for Virtus::ValueObject::InstanceMethods#with
31
- * Add spec file spec/unit/virtus/value_object/equalizer/append_spec.rb for Virtus::ValueObject::Equalizer#<<
32
- * Add spec file spec/unit/virtus/value_object/equalizer/compile_spec.rb for Virtus::ValueObject::Equalizer#compile
33
- * Add spec file spec/unit/virtus/attributes_accessor/define_reader_method_spec.rb for Virtus::AttributesAccessor#define_reader_method
34
- * Add spec file spec/unit/virtus/attributes_accessor/define_writer_method_spec.rb for Virtus::AttributesAccessor#define_writer_method
35
-
36
1
  * Make #to_time #to_date and #to_datetime work on Ruby 1.8.7 instead of typecasting to string and parsing the value
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 19
3
- total_score: 326
3
+ total_score: 344
@@ -94,7 +94,9 @@ DataClump:
94
94
  max_copies: 2
95
95
  min_clump_size: 2
96
96
  ControlCouple:
97
- exclude: []
97
+ exclude: [
98
+ 'Virtus::InstanceMethods#initialize'
99
+ ]
98
100
  enabled: true
99
101
  LongYieldList:
100
102
  max_params: 1
@@ -83,3 +83,4 @@ require 'virtus/attribute/symbol'
83
83
  require 'virtus/attribute/string'
84
84
  require 'virtus/attribute/time'
85
85
  require 'virtus/attribute/embedded_value'
86
+
@@ -217,7 +217,7 @@ module Virtus
217
217
  #
218
218
  # @api public
219
219
  def coerce(value)
220
- Coercion[value.class].send(coercion_method, value)
220
+ Coercion[value.class].public_send(coercion_method, value)
221
221
  end
222
222
 
223
223
  # Is the given value coerced into the target type for this attribute?
@@ -63,8 +63,8 @@ module Virtus
63
63
  def coerce(value)
64
64
  coerced = super
65
65
  return coerced unless coerced.respond_to?(:inject)
66
- coerced.inject(new_collection) do |collection, entry|
67
- coerce_and_append_member(collection, entry)
66
+ coerced.inject(new_collection) do |*args|
67
+ coerce_and_append_member(*args)
68
68
  end
69
69
  end
70
70
 
@@ -16,7 +16,7 @@ module Virtus
16
16
  value.is_a?(::Symbol)
17
17
  end
18
18
 
19
- # Evaluates the value via instance#__send__(value)
19
+ # Evaluates the value via instance#public_send(value)
20
20
  #
21
21
  # Symbol value is returned if the instance doesn't respond to value
22
22
  #
@@ -26,7 +26,7 @@ module Virtus
26
26
  #
27
27
  # @api private
28
28
  def evaluate(instance)
29
- instance.respond_to?(@value) ? instance.__send__(@value) : @value
29
+ instance.respond_to?(@value) ? instance.public_send(@value) : @value
30
30
  end
31
31
 
32
32
  end # class FromSymbol
@@ -4,17 +4,6 @@ module Virtus
4
4
  class AttributeSet
5
5
  include Enumerable
6
6
 
7
- # Return the parent attributes
8
- #
9
- # @return [AttributeSet]
10
- # the parent attributes
11
- #
12
- # @return [nil]
13
- # nil if there are no parent attributes
14
- #
15
- # @api private
16
- attr_reader :parent
17
-
18
7
  # Initialize an AttributeSet
19
8
  #
20
9
  # @param [AttributeSet] parent
@@ -27,7 +16,6 @@ module Virtus
27
16
  @parent = parent
28
17
  @attributes = attributes.dup
29
18
  @index = {}
30
- @string_index = {}
31
19
  reset
32
20
  end
33
21
 
@@ -47,7 +35,7 @@ module Virtus
47
35
  # @api public
48
36
  def each
49
37
  return to_enum unless block_given?
50
- @index.each_value { |attribute| yield attribute }
38
+ @index.values.uniq.each { |attribute| yield attribute }
51
39
  self
52
40
  end
53
41
 
@@ -92,7 +80,7 @@ module Virtus
92
80
  #
93
81
  # @api public
94
82
  def [](name)
95
- @index.fetch(name) { @string_index[name] }
83
+ @index[name]
96
84
  end
97
85
 
98
86
  # Set an attribute by name
@@ -117,8 +105,7 @@ module Virtus
117
105
  #
118
106
  # @api private
119
107
  def reset
120
- parent = self.parent
121
- merge_attributes(parent) if parent
108
+ merge_attributes(@parent) if @parent
122
109
  merge_attributes(@attributes)
123
110
  self
124
111
  end
@@ -146,7 +133,7 @@ module Virtus
146
133
  #
147
134
  # @api private
148
135
  def update_index(name, attribute)
149
- @index[name] = @string_index[name.to_s.freeze] = attribute
136
+ @index[name] = @index[name.to_s.freeze] = attribute
150
137
  end
151
138
 
152
139
  end # class AttributeSet
@@ -3,25 +3,6 @@ module Virtus
3
3
  # Host attribute accessor methods
4
4
  class AttributesAccessor < Module
5
5
 
6
- # The inspect value of this Module
7
- #
8
- # This provides meaningful output when inspecting the ancestors
9
- # of a class/module that includes this module
10
- #
11
- # @example
12
- #
13
- # class ClassWithAttributes
14
- # include Virtus
15
- # end
16
- #
17
- # mod = ClassWithAttributes.send(:virtus_setup_attributes_accessor_module)
18
- # mod.inspect
19
- #
20
- # @return [String]
21
- #
22
- # @api public
23
- attr_reader :inspect
24
-
25
6
  # Initialize a module for hosting Attribute access methods
26
7
  #
27
8
  # @param [Symbol, String] name
@@ -29,7 +10,7 @@ module Virtus
29
10
  # @api private
30
11
  def initialize(name)
31
12
  super()
32
- @inspect = "#{name}::AttributesAccessor"
13
+ @name = name
33
14
  end
34
15
 
35
16
  # Defines an attribute reader method
@@ -62,5 +43,26 @@ module Virtus
62
43
  self
63
44
  end
64
45
 
46
+ # The inspect value of this Module
47
+ #
48
+ # This provides meaningful output when inspecting the ancestors
49
+ # of a class/module that includes this module
50
+ #
51
+ # @example
52
+ #
53
+ # class ClassWithAttributes
54
+ # include Virtus
55
+ # end
56
+ #
57
+ # mod = ClassWithAttributes.send(:virtus_setup_attributes_accessor_module)
58
+ # mod.inspect
59
+ #
60
+ # @return [String]
61
+ #
62
+ # @api public
63
+ def inspect
64
+ "#{@name}::AttributesAccessor"
65
+ end
66
+
65
67
  end
66
68
  end
@@ -76,7 +76,7 @@ module Virtus
76
76
  return @attributes if defined?(@attributes)
77
77
  superclass = self.superclass
78
78
  method = __method__
79
- parent = superclass.send(method) if superclass.respond_to?(method)
79
+ parent = superclass.public_send(method) if superclass.respond_to?(method)
80
80
  @attributes = AttributeSet.new(parent)
81
81
  end
82
82
 
@@ -59,7 +59,7 @@ module Virtus
59
59
  now = ::Time.now
60
60
 
61
61
  TIME_SEGMENTS.map do |segment|
62
- val = value.fetch(segment, now.send(segment))
62
+ val = value.fetch(segment, now.public_send(segment))
63
63
  Coercion[val.class.name].to_integer(val)
64
64
  end
65
65
  end
@@ -115,7 +115,7 @@ module Virtus
115
115
  #
116
116
  # @api private
117
117
  def self.coerce_with_method(value, method)
118
- value.respond_to?(method) ? value.send(method) : value
118
+ value.respond_to?(method) ? value.public_send(method) : value
119
119
  end
120
120
 
121
121
  private_class_method :coerce_with_method
@@ -27,15 +27,28 @@ module Virtus
27
27
  def self.to_constant(value)
28
28
  names = value.split('::')
29
29
  names.shift if names.first.empty?
30
- names.inject(::Object) do |mod, name|
31
- if mod.const_defined?(name, *EXTRA_CONST_ARGS)
32
- mod.const_get(name, *EXTRA_CONST_ARGS)
33
- else
34
- mod.const_missing(name)
35
- end
30
+ names.inject(::Object) { |*args| constant_lookup(*args) }
31
+ end
32
+
33
+ # Lookup a constant within a module
34
+ #
35
+ # @param [Module] mod
36
+ #
37
+ # @param [String] name
38
+ #
39
+ # @return [Object]
40
+ #
41
+ # @api private
42
+ def self.constant_lookup(mod, name)
43
+ if mod.const_defined?(name, *EXTRA_CONST_ARGS)
44
+ mod.const_get(name, *EXTRA_CONST_ARGS)
45
+ else
46
+ mod.const_missing(name)
36
47
  end
37
48
  end
38
49
 
50
+ private_class_method :constant_lookup
51
+
39
52
  # Coerce give value to a symbol
40
53
  #
41
54
  # @example
@@ -167,7 +180,7 @@ module Virtus
167
180
  # @api private
168
181
  def self.to_numeric(value, method)
169
182
  if value =~ NUMERIC_REGEXP
170
- $1.send(method)
183
+ $1.public_send(method)
171
184
  else
172
185
  value
173
186
  end
@@ -74,7 +74,11 @@ module Virtus
74
74
  # @api private
75
75
  def coerce_with_method(value, method)
76
76
  coerced = super
77
- coerced.equal?(value) ? String.send(method, to_string(value)) : coerced
77
+ if coerced.equal?(value)
78
+ String.public_send(method, to_string(value))
79
+ else
80
+ coerced
81
+ end
78
82
  end
79
83
 
80
84
  end # module TimeCoercions
@@ -11,8 +11,8 @@ module Virtus
11
11
  # @return [undefined]
12
12
  #
13
13
  # @api private
14
- def initialize(attribute_values = {})
15
- self.attributes = attribute_values
14
+ def initialize(attributes = nil)
15
+ self.attributes = attributes if attributes
16
16
  end
17
17
 
18
18
  # Returns a value of the attribute with the given name
@@ -87,7 +87,7 @@ module Virtus
87
87
 
88
88
  # Mass-assign attribute values
89
89
  #
90
- # Keys in the +attribute_values+ param can be symbols or strings.
90
+ # Keys in the +attributes+ param can be symbols or strings.
91
91
  # All referenced Attribute writer methods *will* be called.
92
92
  # Non-attribute setter methods on the receiver *will* be called.
93
93
  #
@@ -102,14 +102,14 @@ module Virtus
102
102
  # user = User.new
103
103
  # user.attributes = { :name => 'John', 'age' => 28 }
104
104
  #
105
- # @param [#to_hash] attribute_values
105
+ # @param [#to_hash] attributes
106
106
  # a hash of attribute names and values to set on the receiver
107
107
  #
108
108
  # @return [Hash]
109
109
  #
110
110
  # @api public
111
- def attributes=(attribute_values)
112
- set_attributes(attribute_values)
111
+ def attributes=(attributes)
112
+ set_attributes(attributes)
113
113
  end
114
114
 
115
115
  # Returns a hash of all publicly accessible attributes
@@ -153,8 +153,8 @@ module Virtus
153
153
  # @return [Hash]
154
154
  #
155
155
  # @api private
156
- def set_attributes(attribute_values)
157
- attribute_values.each do |name, value|
156
+ def set_attributes(attributes)
157
+ ::Hash.try_convert(attributes).each do |name, value|
158
158
  set_attribute(name, value) if self.class.allowed_writer_methods.include?("#{name}=")
159
159
  end
160
160
  end
@@ -5,6 +5,17 @@ module Virtus
5
5
 
6
6
  TYPE_FORMAT = /\A[A-Z]\w*\z/.freeze
7
7
 
8
+ # Set cache ivar on the model
9
+ #
10
+ # @param [Class] model
11
+ #
12
+ # @return [undefined]
13
+ #
14
+ # @api private
15
+ def self.extended(model)
16
+ model.instance_variable_set('@type_lookup_cache', {})
17
+ end
18
+
8
19
  # Returns a descendant based on a name or class
9
20
  #
10
21
  # @example
@@ -21,14 +32,7 @@ module Virtus
21
32
  #
22
33
  # @api public
23
34
  def determine_type(class_or_name)
24
- case class_or_name
25
- when singleton_class
26
- determine_type_from_descendant(class_or_name)
27
- when Class
28
- determine_type_from_primitive(class_or_name)
29
- else
30
- determine_type_from_string(class_or_name.to_s)
31
- end
35
+ @type_lookup_cache[class_or_name] ||= determine_type_and_cache(class_or_name)
32
36
  end
33
37
 
34
38
  # Return the default primitive supported
@@ -42,6 +46,18 @@ module Virtus
42
46
 
43
47
  private
44
48
 
49
+ # @api private
50
+ def determine_type_and_cache(class_or_name)
51
+ type = case class_or_name
52
+ when singleton_class
53
+ determine_type_from_descendant(class_or_name)
54
+ when Class
55
+ determine_type_from_primitive(class_or_name)
56
+ else
57
+ determine_type_from_string(class_or_name.to_s)
58
+ end
59
+ end
60
+
45
61
  # Return the class given a descendant
46
62
  #
47
63
  # @param [Class] descendant