virtus 0.0.2 → 0.0.3

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 (85) hide show
  1. data/.gitignore +3 -0
  2. data/.rvmrc +1 -1
  3. data/Gemfile +20 -1
  4. data/History.txt +21 -0
  5. data/README.markdown +2 -2
  6. data/Rakefile +1 -2
  7. data/VERSION +1 -1
  8. data/config/flay.yml +3 -0
  9. data/config/flog.yml +2 -0
  10. data/config/roodi.yml +18 -0
  11. data/config/site.reek +91 -0
  12. data/config/yardstick.yml +2 -0
  13. data/lib/virtus.rb +51 -45
  14. data/lib/virtus/attribute.rb +301 -0
  15. data/lib/virtus/attribute/array.rb +17 -0
  16. data/lib/virtus/attribute/boolean.rb +60 -0
  17. data/lib/virtus/attribute/date.rb +35 -0
  18. data/lib/virtus/attribute/date_time.rb +34 -0
  19. data/lib/virtus/attribute/decimal.rb +24 -0
  20. data/lib/virtus/attribute/float.rb +33 -0
  21. data/lib/virtus/attribute/hash.rb +18 -0
  22. data/lib/virtus/attribute/integer.rb +30 -0
  23. data/lib/virtus/{attributes → attribute}/numeric.rb +2 -3
  24. data/lib/virtus/{attributes → attribute}/object.rb +2 -1
  25. data/lib/virtus/attribute/string.rb +31 -0
  26. data/lib/virtus/attribute/time.rb +34 -0
  27. data/lib/virtus/class_methods.rb +25 -8
  28. data/lib/virtus/instance_methods.rb +48 -9
  29. data/lib/virtus/support/chainable.rb +4 -6
  30. data/lib/virtus/typecast/boolean.rb +27 -0
  31. data/lib/virtus/typecast/numeric.rb +82 -0
  32. data/lib/virtus/typecast/time.rb +162 -0
  33. data/spec/integration/virtus/attributes/attribute/typecast_spec.rb +4 -4
  34. data/spec/integration/virtus/class_methods/attribute_spec.rb +1 -1
  35. data/spec/integration/virtus/class_methods/attributes_spec.rb +3 -2
  36. data/spec/integration/virtus/class_methods/const_missing_spec.rb +2 -2
  37. data/spec/rcov.opts +6 -0
  38. data/spec/spec_helper.rb +0 -9
  39. data/spec/unit/shared/attribute.rb +8 -8
  40. data/spec/unit/virtus/{attributes → attribute}/array_spec.rb +1 -1
  41. data/spec/unit/virtus/attribute/attribute_spec.rb +12 -0
  42. data/spec/unit/virtus/{attributes → attribute}/boolean_spec.rb +4 -4
  43. data/spec/unit/virtus/{attributes → attribute}/date_spec.rb +13 -7
  44. data/spec/unit/virtus/{attributes → attribute}/date_time_spec.rb +31 -10
  45. data/spec/unit/virtus/{attributes → attribute}/decimal_spec.rb +18 -18
  46. data/spec/unit/virtus/{attributes → attribute}/float_spec.rb +18 -18
  47. data/spec/unit/virtus/{attributes → attribute}/hash_spec.rb +1 -1
  48. data/spec/unit/virtus/{attributes → attribute}/integer_spec.rb +18 -18
  49. data/spec/unit/virtus/attribute/numeric/class_methods/descendants_spec.rb +15 -0
  50. data/spec/unit/virtus/attribute/object/class_methods/descendants_spec.rb +16 -0
  51. data/spec/unit/virtus/{attributes → attribute}/string_spec.rb +2 -2
  52. data/spec/unit/virtus/{attributes → attribute}/time_spec.rb +19 -9
  53. data/spec/unit/virtus/class_methods/new_spec.rb +7 -7
  54. data/spec/unit/virtus/determine_type_spec.rb +4 -4
  55. data/spec/unit/virtus/instance_methods/attribute_get_spec.rb +1 -1
  56. data/spec/unit/virtus/instance_methods/attribute_set_spec.rb +2 -2
  57. data/spec/unit/virtus/instance_methods/attributes_spec.rb +2 -2
  58. data/tasks/metrics/ci.rake +7 -0
  59. data/tasks/metrics/flay.rake +41 -0
  60. data/tasks/metrics/flog.rake +43 -0
  61. data/tasks/metrics/heckle.rake +261 -0
  62. data/tasks/metrics/metric_fu.rake +29 -0
  63. data/tasks/metrics/reek.rake +9 -0
  64. data/tasks/metrics/roodi.rake +15 -0
  65. data/tasks/metrics/yardstick.rake +23 -0
  66. data/tasks/spec.rake +26 -0
  67. data/tasks/yard.rake +9 -0
  68. data/virtus.gemspec +48 -33
  69. metadata +51 -41
  70. data/lib/virtus/attributes/array.rb +0 -8
  71. data/lib/virtus/attributes/attribute.rb +0 -214
  72. data/lib/virtus/attributes/boolean.rb +0 -39
  73. data/lib/virtus/attributes/date.rb +0 -44
  74. data/lib/virtus/attributes/date_time.rb +0 -43
  75. data/lib/virtus/attributes/decimal.rb +0 -24
  76. data/lib/virtus/attributes/float.rb +0 -20
  77. data/lib/virtus/attributes/hash.rb +0 -8
  78. data/lib/virtus/attributes/integer.rb +0 -20
  79. data/lib/virtus/attributes/string.rb +0 -11
  80. data/lib/virtus/attributes/time.rb +0 -45
  81. data/lib/virtus/attributes/typecast/numeric.rb +0 -32
  82. data/lib/virtus/attributes/typecast/time.rb +0 -27
  83. data/spec/unit/virtus/attributes/attribute_spec.rb +0 -13
  84. data/spec/unit/virtus/attributes/numeric/class_methods/descendants_spec.rb +0 -15
  85. data/spec/unit/virtus/attributes/object/class_methods/descendants_spec.rb +0 -16
data/.gitignore CHANGED
@@ -2,3 +2,6 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ measurements/
6
+ tmp/
7
+ profiling/
data/.rvmrc CHANGED
@@ -1 +1 @@
1
- rvm --create use 1.9.2@virtus
1
+ rvm use @$(basename `pwd`) --create
data/Gemfile CHANGED
@@ -1,7 +1,26 @@
1
1
  source "http://rubygems.org"
2
2
 
3
+ group :virtus do
4
+ gem 'virtus', File.read('VERSION'), :path => File.dirname(__FILE__)
5
+ end
6
+
3
7
  group :development do
4
8
  gem "jeweler", "~> 1.5.2"
5
9
  gem "rspec", "~> 2.6.0"
6
- gem "simplecov", "~> 0.4.2", :platforms => [ :mri_19 ]
10
+ end
11
+
12
+ platforms :mri_18 do
13
+ group :metrics do
14
+ gem 'flay', '~> 1.4.2'
15
+ gem 'flog', '~> 2.5.1'
16
+ gem 'heckle', '~> 1.4.3'
17
+ gem 'json', '~> 1.5.1'
18
+ gem 'metric_fu', '~> 2.1.1'
19
+ gem 'mspec', '~> 1.5.17'
20
+ gem 'rcov', '~> 0.9.9'
21
+ gem 'reek', '~> 1.2.8', :git => 'git://github.com/dkubb/reek.git'
22
+ gem 'roodi', '~> 2.1.0'
23
+ gem 'ruby2ruby', '= 1.2.2'
24
+ gem 'yardstick', '~> 0.4.0'
25
+ end
7
26
  end
@@ -0,0 +1,21 @@
1
+ === v0.0.3 2011-06-09
2
+
3
+ * [BREAKING CHANGE] Attribute classes were moved to Virtus::Attribute namespace
4
+ * [BREAKING CHANGE] Attribute instance no longer holds the reference to a model
5
+ * [BREAKING CHANGE] #typecast no longer receives an instance of a model (override #set which calls #typecast if you need that)
6
+ * [changed] Adding reader/writer methods was moved from the attribute constructor to Virtus::ClassMethods.attribute
7
+ * [changed] Typecast logic has been moved into separate classes (see Virtus::Typecast)
8
+ * [added] Virtus::Attribute::DateTime#typecast supports objects which implement #to_datetime
9
+ * [general] Internals have been cleaned up, simplified and properly documented
10
+
11
+ Details: https://github.com/solnic/virtus/compare/v0.0.2...v0.0.3
12
+
13
+ === v0.0.2 2011-06-06
14
+
15
+ * [bugfix] Fixed #typecast in custom attribute classes
16
+
17
+ Details: https://github.com/solnic/virtus/compare/v0.0.1...v0.0.2
18
+
19
+ === v0.0.1 2011-06-04
20
+
21
+ First public release :)
@@ -47,10 +47,10 @@ attributes that require typecasting and/or validations.
47
47
 
48
48
  module MyApp
49
49
  module Attributes
50
- class JSON < Virtus::Attributes::Object
50
+ class JSON < Virtus::Attribute::Object
51
51
  primitive Hash
52
52
 
53
- def typecast(value, model = nil)
53
+ def typecast(value)
54
54
  ::JSON.parse(value)
55
55
  end
56
56
  end
data/Rakefile CHANGED
@@ -20,8 +20,7 @@ end
20
20
 
21
21
  Jeweler::GemcutterTasks.new
22
22
 
23
- desc "Run specs"
24
- RSpec::Core::RakeTask.new
23
+ FileList['tasks/**/*.rake'].each { |task| import task }
25
24
 
26
25
  desc 'Default: run specs.'
27
26
  task :default => :spec
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
@@ -0,0 +1,3 @@
1
+ ---
2
+ threshold: 19.0
3
+ total_score: 191
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 18.2
@@ -0,0 +1,18 @@
1
+ ---
2
+ AbcMetricMethodCheck: { score: 11.8 }
3
+ AssignmentInConditionalCheck: { }
4
+ CaseMissingElseCheck: { }
5
+ ClassLineCountCheck: { line_count: 390 }
6
+ ClassNameCheck: { pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/ }
7
+ ClassVariableCheck: { }
8
+ CyclomaticComplexityBlockCheck: { complexity: 2 }
9
+ CyclomaticComplexityMethodCheck: { complexity: 5 }
10
+ EmptyRescueBodyCheck: { }
11
+ ForLoopCheck: { }
12
+ # TODO: decrease line_count to 5 to 10
13
+ MethodLineCountCheck: { line_count: 20 }
14
+ MethodNameCheck: { pattern: !ruby/regexp /\A(?:[a-z\d](?:_?[a-z\d])+[?!=]?|\[\]=?|==|<=>|[+*&|-])\z/ }
15
+ ModuleLineCountCheck: { line_count: 392 }
16
+ ModuleNameCheck: { pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/ }
17
+ # TODO: decrease parameter_count to 2 or less
18
+ ParameterNumberCheck: { parameter_count: 3 }
@@ -0,0 +1,91 @@
1
+ ---
2
+ UncommunicativeParameterName:
3
+ accept: []
4
+ exclude: []
5
+ enabled: true
6
+ reject:
7
+ - !ruby/regexp /^.$/
8
+ - !ruby/regexp /[0-9]$/
9
+ - !ruby/regexp /[A-Z]/
10
+ LargeClass:
11
+ max_methods: 15 # TODO: decrease max_methods to 10-15 or less
12
+ exclude: []
13
+ enabled: true
14
+ max_instance_variables: 5
15
+ UncommunicativeMethodName:
16
+ accept: []
17
+ exclude: []
18
+ enabled: true
19
+ reject:
20
+ - !ruby/regexp /^[a-z]$/
21
+ - !ruby/regexp /[0-9]$/
22
+ - !ruby/regexp /[A-Z]/
23
+ LongParameterList:
24
+ max_params: 3 # TODO: decrease max_params to 2
25
+ exclude: []
26
+ enabled: true
27
+ overrides: {}
28
+ FeatureEnvy:
29
+ exclude: []
30
+ enabled: true
31
+ ClassVariable:
32
+ exclude: []
33
+ enabled: true
34
+ BooleanParameter:
35
+ exclude: []
36
+ enabled: true
37
+ IrresponsibleModule:
38
+ exclude: []
39
+ enabled: true
40
+ UncommunicativeModuleName:
41
+ accept: []
42
+ exclude: []
43
+ enabled: true
44
+ reject:
45
+ - !ruby/regexp /^.$/
46
+ - !ruby/regexp /[0-9]$/
47
+ NestedIterators:
48
+ ignore_iterators: []
49
+ exclude: []
50
+ enabled: true
51
+ max_allowed_nesting: 1
52
+ LongMethod:
53
+ max_statements: 7 # TODO: decrease max_statements to 5 or less
54
+ exclude: []
55
+ enabled: true
56
+ Duplication:
57
+ allow_calls: []
58
+ exclude: []
59
+ enabled: true
60
+ max_calls: 1
61
+ UtilityFunction:
62
+ max_helper_calls: 1
63
+ exclude: []
64
+ enabled: true
65
+ Attribute:
66
+ exclude: []
67
+ enabled: false
68
+ UncommunicativeVariableName:
69
+ accept: []
70
+ exclude: []
71
+ enabled: true
72
+ reject:
73
+ - !ruby/regexp /^.$/
74
+ - !ruby/regexp /[0-9]$/
75
+ - !ruby/regexp /[A-Z]/
76
+ SimulatedPolymorphism:
77
+ exclude: []
78
+ enabled: true
79
+ max_ifs: 1
80
+ DataClump:
81
+ exclude: []
82
+ enabled: true
83
+ max_copies: 1
84
+ min_clump_size: 2
85
+ ControlCouple:
86
+ exclude: []
87
+ enabled: true
88
+ LongYieldList:
89
+ max_params: 2
90
+ exclude: []
91
+ enabled: true
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 100.0
@@ -5,57 +5,63 @@ require 'time'
5
5
  require 'bigdecimal'
6
6
  require 'bigdecimal/util'
7
7
 
8
+ # Base module which adds Attribute API to your classes
8
9
  module Virtus
10
+ # Represents an undefined parameter used by auto-generated option methods
9
11
  module Undefined; end
10
12
 
11
- class << self
12
- # Extends base class with Attributes and Chainable modules
13
- #
14
- # @param [Object] base
15
- #
16
- # @api private
17
- def included(base)
18
- base.extend(ClassMethods)
19
- base.send(:include, InstanceMethods)
20
- base.extend(Support::Chainable)
21
- end
13
+ # Extends base class with Attributes and Chainable modules
14
+ #
15
+ # @param [Class] base
16
+ #
17
+ # @return [Class]
18
+ #
19
+ # @api private
20
+ def self.included(base)
21
+ base.extend(ClassMethods)
22
+ base.send(:include, InstanceMethods)
23
+ base.extend(Support::Chainable)
24
+ end
22
25
 
23
- # Returns a Virtus::Attributes::Object sub-class based on a name or class.
24
- #
25
- # @param [Class,String] class_or_name
26
- # name of a class or a class itself
27
- #
28
- # @return [Class]
29
- # one of the Virtus::Attributes::Object sub-class
30
- #
31
- # @api semipublic
32
- def determine_type(class_or_name)
33
- if class_or_name.is_a?(Class) && class_or_name < Attributes::Object
34
- class_or_name
35
- elsif Attributes.const_defined?(name = class_or_name.to_s)
36
- Attributes.const_get(name)
37
- end
26
+ # Returns a Virtus::Attributes::Object sub-class based on a name or class
27
+ #
28
+ # @example
29
+ # Virtus.determine_type('String') # => Virtus::Attribute::String
30
+ #
31
+ # @param [Class,String] class_or_name
32
+ # name of a class or a class itself
33
+ #
34
+ # @return [Class]
35
+ # one of the Virtus::Attributes::Object sub-class
36
+ #
37
+ # @api semipublic
38
+ def self.determine_type(class_or_name)
39
+ if class_or_name.is_a?(Class) && class_or_name < Attribute::Object
40
+ class_or_name
41
+ elsif Attribute.const_defined?(name = class_or_name.to_s)
42
+ Attribute.const_get(name)
38
43
  end
39
44
  end
40
45
  end
41
46
 
42
- dir = Pathname(__FILE__).dirname.expand_path
47
+ require 'virtus/support/chainable'
48
+ require 'virtus/class_methods'
49
+ require 'virtus/instance_methods'
50
+
51
+ require 'virtus/typecast/boolean'
52
+ require 'virtus/typecast/numeric'
53
+ require 'virtus/typecast/time'
43
54
 
44
- require dir + 'virtus/support/chainable'
45
- require dir + 'virtus/class_methods'
46
- require dir + 'virtus/instance_methods'
47
- require dir + 'virtus/attributes/typecast/numeric'
48
- require dir + 'virtus/attributes/typecast/time'
49
- require dir + 'virtus/attributes/attribute'
50
- require dir + 'virtus/attributes/object'
51
- require dir + 'virtus/attributes/array'
52
- require dir + 'virtus/attributes/boolean'
53
- require dir + 'virtus/attributes/date'
54
- require dir + 'virtus/attributes/date_time'
55
- require dir + 'virtus/attributes/numeric'
56
- require dir + 'virtus/attributes/decimal'
57
- require dir + 'virtus/attributes/float'
58
- require dir + 'virtus/attributes/hash'
59
- require dir + 'virtus/attributes/integer'
60
- require dir + 'virtus/attributes/string'
61
- require dir + 'virtus/attributes/time'
55
+ require 'virtus/attribute'
56
+ require 'virtus/attribute/object'
57
+ require 'virtus/attribute/array'
58
+ require 'virtus/attribute/boolean'
59
+ require 'virtus/attribute/date'
60
+ require 'virtus/attribute/date_time'
61
+ require 'virtus/attribute/numeric'
62
+ require 'virtus/attribute/decimal'
63
+ require 'virtus/attribute/float'
64
+ require 'virtus/attribute/hash'
65
+ require 'virtus/attribute/integer'
66
+ require 'virtus/attribute/string'
67
+ require 'virtus/attribute/time'
@@ -0,0 +1,301 @@
1
+ module Virtus
2
+ # Abstract class implementing base API for attribute types
3
+ #
4
+ # @abstract
5
+ class Attribute
6
+ # Returns default options hash for a given attribute class
7
+ #
8
+ # @example
9
+ # Virtus::Attribute::String.options
10
+ # # => {:primitive => String, :complex => false}
11
+ #
12
+ # @return [Hash]
13
+ # a hash of default option values
14
+ #
15
+ # @api public
16
+ def self.options
17
+ options = {}
18
+ accepted_options.each do |method|
19
+ value = send(method)
20
+ options[method] = value unless value.nil?
21
+ end
22
+ options
23
+ end
24
+
25
+ # Returns an array of valid options
26
+ #
27
+ # @example
28
+ # Virtus::Attribute::String.accepted_options
29
+ # # => [:primitive, :complex, :accessor, :reader, :writer]
30
+ #
31
+ # @return [Array]
32
+ # the array of valid option names
33
+ #
34
+ # @api public
35
+ def self.accepted_options
36
+ @accepted_options ||= []
37
+ end
38
+
39
+ # Defines which options are valid for a given attribute class
40
+ #
41
+ # @example
42
+ # class MyAttribute < Virtus::Attribute::Object
43
+ # accept_options :foo, :bar
44
+ # end
45
+ #
46
+ # @return [Array]
47
+ # All accepted options
48
+ #
49
+ # @api public
50
+ def self.accept_options(*args)
51
+ accepted_options.concat(args)
52
+
53
+ # create methods for each new option
54
+ args.each { |option| add_option_method(option) }
55
+
56
+ # add new options to all descendants
57
+ descendants.each { |descendant| descendant.accepted_options.concat(args) }
58
+
59
+ accepted_options
60
+ end
61
+
62
+ # Adds a reader/writer method for the give option name
63
+ #
64
+ # @return [NilClass]
65
+ #
66
+ # @api private
67
+ def self.add_option_method(option)
68
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
69
+ def self.#{option}(value = Undefined) # def self.unique(value = Undefined)
70
+ return @#{option} if value.equal?(Undefined) # return @unique if value.equal?(Undefined)
71
+ @#{option} = value # @unique = value
72
+ end # end
73
+ RUBY
74
+ end
75
+ private_class_method :add_option_method
76
+
77
+ # Returns all the descendant classes
78
+ #
79
+ # @example
80
+ # Virtus::Attribute::Numeric.descendants
81
+ # # => [Virtus::Attribute::Decimal, Virtus::Attribute::Float, Virtus::Attribute::Integer]
82
+ #
83
+ # @return [Array]
84
+ # the array of descendants
85
+ #
86
+ # @api public
87
+ def self.descendants
88
+ @descendants ||= []
89
+ end
90
+
91
+ # Adds descendant to descendants array and inherits default options
92
+ #
93
+ # @param [Class]
94
+ #
95
+ # @return [Class]
96
+ #
97
+ # @api private
98
+ def self.inherited(descendant)
99
+ descendants << descendant
100
+ descendant.accepted_options.concat(accepted_options)
101
+ options.each { |key, value| descendant.send(key, value) }
102
+ descendant
103
+ end
104
+
105
+ # Returns name of the attribute
106
+ #
107
+ # @example
108
+ # User.attributes[:age].name # => :age
109
+ #
110
+ # @return [Symbol]
111
+ #
112
+ # @api public
113
+ attr_reader :name
114
+
115
+ # Returns primitive class of the attribute
116
+ #
117
+ # @return [Class]
118
+ #
119
+ # @api private
120
+ attr_reader :primitive
121
+
122
+ # Returns options hash for the attribute
123
+ #
124
+ # @return [Hash]
125
+ #
126
+ # @api private
127
+ attr_reader :options
128
+
129
+ # Returns instance variable name of the attribute
130
+ #
131
+ # @return [String]
132
+ #
133
+ # @api private
134
+ attr_reader :instance_variable_name
135
+
136
+ # Returns reader visibility
137
+ #
138
+ # @return [Symbol]
139
+ #
140
+ # @api private
141
+ attr_reader :reader_visibility
142
+
143
+
144
+ # Returns write visibility
145
+ #
146
+ # @return [Symbol]
147
+ #
148
+ # @api private
149
+ attr_reader :writer_visibility
150
+
151
+ DEFAULT_ACCESSOR = :public.freeze
152
+
153
+ OPTIONS = [ :primitive, :complex, :accessor, :reader, :writer ].freeze
154
+
155
+ accept_options *OPTIONS
156
+
157
+ # Initializes an attribute instance
158
+ #
159
+ # @param [Symbol] name
160
+ # the name of an attribute
161
+ #
162
+ # @param [Hash] options
163
+ # hash of extra options which overrides defaults set on an attribute class
164
+ #
165
+ # @api private
166
+ def initialize(name, options = {})
167
+ @name = name
168
+ @options = self.class.options.merge(options).freeze
169
+
170
+ @primitive = @options[:primitive]
171
+
172
+ @instance_variable_name = "@#{@name}".freeze
173
+
174
+ default_accessor = @options.fetch(:accessor, DEFAULT_ACCESSOR)
175
+ @reader_visibility = @options.fetch(:reader, default_accessor)
176
+ @writer_visibility = @options.fetch(:writer, default_accessor)
177
+ end
178
+
179
+ # Returns if an attribute is a complex one
180
+ #
181
+ # @example
182
+ # Virtus::Attribute::String.complex? # => false
183
+ # Virtus::Attribute::Array.complex? # => true
184
+ #
185
+ # @return [TrueClass, FalseClass]
186
+ #
187
+ # @api semipublic
188
+ def complex?
189
+ options[:complex]
190
+ end
191
+
192
+ # Returns if the given value's class is an attribute's primitive
193
+ #
194
+ # @return [TrueClass, FalseClass]
195
+ #
196
+ # @api private
197
+ def primitive?(value)
198
+ value.kind_of?(primitive)
199
+ end
200
+
201
+ # Converts the given value to the primitive type
202
+ #
203
+ # @param [Object] value
204
+ # the value
205
+ #
206
+ # @return [Object]
207
+ # nil, original value or value converted to the primitive type
208
+ #
209
+ # @api private
210
+ def typecast(value)
211
+ if value.nil? || primitive?(value)
212
+ value
213
+ else
214
+ typecast_to_primitive(value)
215
+ end
216
+ end
217
+
218
+ # Converts the given value to the primitive type
219
+ #
220
+ # @api private
221
+ def typecast_to_primitive(value)
222
+ value
223
+ end
224
+
225
+ # Returns value of an attribute for the given instance
226
+ #
227
+ # @return [Object]
228
+ # value of an attribute
229
+ #
230
+ # @api private
231
+ def get(instance)
232
+ get!(instance)
233
+ end
234
+
235
+ # Returns the instance variable of the attribute
236
+ #
237
+ # @return [Object]
238
+ # value of an attribute
239
+ #
240
+ # @api private
241
+ def get!(instance)
242
+ instance.instance_variable_get(instance_variable_name)
243
+ end
244
+
245
+ # Sets the value on the instance
246
+ #
247
+ # @return [Object]
248
+ # value of an attribute
249
+ #
250
+ # @api private
251
+ def set(instance, value)
252
+ set!(instance, typecast(value)) unless value.nil?
253
+ end
254
+
255
+ # Sets instance variable of the attribute
256
+ #
257
+ # @return [Object]
258
+ # value of an attribute
259
+ #
260
+ # @api private
261
+ def set!(instance, value)
262
+ instance.instance_variable_set(instance_variable_name, value)
263
+ end
264
+
265
+ # Creates an attribute reader method
266
+ #
267
+ # @return [NilClass]
268
+ #
269
+ # @api private
270
+ def add_reader_method(model)
271
+ model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
272
+ chainable(:attribute) do
273
+ #{reader_visibility}
274
+
275
+ def #{name}
276
+ return #{instance_variable_name} if defined?(#{instance_variable_name})
277
+ attribute = self.class.attributes[#{name.inspect}]
278
+ #{instance_variable_name} = attribute ? attribute.get(self) : nil
279
+ end
280
+ end
281
+ RUBY
282
+ end
283
+
284
+ # Creates an attribute writer method
285
+ #
286
+ # @return [NilClass]
287
+ #
288
+ # @api private
289
+ def add_writer_method(model)
290
+ model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
291
+ chainable(:attribute) do
292
+ #{writer_visibility}
293
+
294
+ def #{name}=(value)
295
+ self.class.attributes[#{name.inspect}].set(self, value)
296
+ end
297
+ end
298
+ RUBY
299
+ end
300
+ end # Attribute
301
+ end # Virtus