virtus 0.0.2 → 0.0.3

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