virtus 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,8 +1,9 @@
1
1
  source :rubygems
2
2
 
3
3
  group :development do
4
- gem 'jeweler', '~> 1.6.4'
5
- gem 'rspec', '~> 2.6.0'
4
+ gem 'backports', '~> 2.3.0'
5
+ gem 'jeweler', '~> 1.6.4'
6
+ gem 'rspec', '~> 2.6.0'
6
7
  end
7
8
 
8
9
  group :metrics do
@@ -14,6 +15,7 @@ group :metrics do
14
15
 
15
16
  platforms :mri_18 do
16
17
  gem 'heckle', '~> 1.4.3'
18
+ gem 'json', '~> 1.5.3'
17
19
  gem 'metric_fu', '~> 2.1.1'
18
20
  gem 'mspec', '~> 1.5.17'
19
21
  gem 'rcov', '~> 0.9.9'
@@ -1,3 +1,10 @@
1
+ === v0.0.5 to-be-released
2
+
3
+ Details: https://github.com/solnic/virtus/compare/v0.0.4...master
4
+
5
+ * [BREAKING CHANGE] Moved Virtus.determine_type to Virtus::Attribute.determine_type (dkubb)
6
+ * [general] Added backports as a development dependency (dkubb)
7
+
1
8
  === v0.0.4 2011-07-08
2
9
 
3
10
  * [BREAKING CHANGE] attributes hash has been replaced by a specialized class AttributeSet (dkubb)
@@ -25,20 +25,20 @@ attributes that require typecasting and/or validations.
25
25
  end
26
26
 
27
27
  # setting attributes in the constructor
28
- user = User.new(:age => 28)
28
+ user = User.new(:name => 'Piotr', :age => 28)
29
29
 
30
30
  # attribute readers
31
- user.name # => "Piotr"
31
+ user.name # => "Piotr"
32
32
 
33
33
  # hash of attributes
34
- user.attributes # => { :name => "Piotr" }
34
+ user.attributes # => { :name => "Piotr" }
35
35
 
36
36
  # automatic typecasting
37
37
  user.age = '28'
38
- user.age # => 28
38
+ user.age # => 28
39
39
 
40
40
  user.birthday = 'November 18th, 1983'
41
- user.birthday # => #<DateTime: 1983-11-18T00:00:00+00:00 (4891313/2,0/1,2299161)>
41
+ user.birthday # => #<DateTime: 1983-11-18T00:00:00+00:00 (4891313/2,0/1,2299161)>
42
42
 
43
43
  ## Custom Attributes
44
44
 
@@ -65,8 +65,8 @@ attributes that require typecasting and/or validations.
65
65
 
66
66
  user = MyApp::User.new
67
67
 
68
- user.info = '{"email" : "john@domain.com" }'
69
- user.info # => {"email"=>"john@domain.com"}
68
+ user.info = '{"email":"john@domain.com"}'
69
+ user.info # => {"email"=>"john@domain.com"}
70
70
 
71
71
  ## Note on Patches/Pull Requests
72
72
 
data/Rakefile CHANGED
@@ -4,18 +4,13 @@ require 'jeweler'
4
4
  require 'rspec/core/rake_task'
5
5
 
6
6
  Jeweler::Tasks.new do |gem|
7
- gem.name = "virtus"
7
+ gem.name = 'virtus'
8
8
  gem.platform = Gem::Platform::RUBY
9
- gem.authors = ["Piotr Solnica"]
10
- gem.email = ["piotr@rubyverse.com"]
11
- gem.homepage = "https://github.com/solnic/virtus"
12
- gem.summary = %q{Attributes for your plain ruby objects}
9
+ gem.authors = [ 'Piotr Solnica' ]
10
+ gem.email = [ 'piotr@rubyverse.com' ]
11
+ gem.homepage = 'https://github.com/solnic/virtus'
12
+ gem.summary = 'Attributes for your plain ruby objects'
13
13
  gem.description = gem.summary
14
-
15
- gem.files = `git ls-files`.split("\n")
16
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
- gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
- gem.require_paths = ["lib"]
19
14
  end
20
15
 
21
16
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.0.5
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 19.0
3
- total_score: 208
3
+ total_score: 187
@@ -2,7 +2,7 @@
2
2
  AbcMetricMethodCheck: { score: 9.5 }
3
3
  AssignmentInConditionalCheck: { }
4
4
  CaseMissingElseCheck: { }
5
- ClassLineCountCheck: { line_count: 345 }
5
+ ClassLineCountCheck: { line_count: 404 }
6
6
  ClassNameCheck: { pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/ }
7
7
  ClassVariableCheck: { }
8
8
  CyclomaticComplexityBlockCheck: { complexity: 2 }
@@ -10,9 +10,9 @@ CyclomaticComplexityMethodCheck: { complexity: 3 }
10
10
  EmptyRescueBodyCheck: { }
11
11
  ForLoopCheck: { }
12
12
  # TODO: decrease line_count to 5 to 10
13
- MethodLineCountCheck: { line_count: 17 }
13
+ MethodLineCountCheck: { line_count: 19 }
14
14
  MethodNameCheck: { pattern: !ruby/regexp /\A(?:[a-z\d](?:_?[a-z\d])+[?!=]?|\[\]=?|==|<=>|<<|[+*&|-])\z/ }
15
- ModuleLineCountCheck: { line_count: 351 }
15
+ ModuleLineCountCheck: { line_count: 410 }
16
16
  ModuleNameCheck: { pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/ }
17
17
  # TODO: decrease parameter_count to 2 or less
18
18
  ParameterNumberCheck: { parameter_count: 3 }
@@ -11,41 +11,15 @@ module Virtus
11
11
 
12
12
  # Extends base class with class and instance methods
13
13
  #
14
- # @param [Class] base
14
+ # @param [Class] descendant
15
15
  #
16
16
  # @return [Class]
17
17
  #
18
18
  # @api private
19
- def self.included(base)
20
- base.extend(DescendantsTracker)
21
- base.extend(ClassMethods)
22
- base.send(:include, InstanceMethods)
23
- end
24
-
25
- # Returns a Virtus::Attribute::Object sub-class based on a name or class
26
- #
27
- # @example
28
- # Virtus.determine_type('String') # => Virtus::Attribute::String
29
- #
30
- # @param [Class,String] class_or_name
31
- # name of a class or a class itself
32
- #
33
- # @return [Class]
34
- # one of the Virtus::Attribute::Object sub-class
35
- #
36
- # @api semipublic
37
- def self.determine_type(class_or_name)
38
- if class_or_name.kind_of?(Class)
39
- if class_or_name < Attribute::Object
40
- class_or_name
41
- else
42
- Attribute.descendants.detect do |descendant|
43
- class_or_name <= descendant.primitive
44
- end
45
- end
46
- elsif Attribute.const_defined?(name = class_or_name.to_s)
47
- Attribute.const_get(name)
48
- end
19
+ def self.included(descendant)
20
+ descendant.extend(DescendantsTracker)
21
+ descendant.extend(ClassMethods)
22
+ descendant.send(:include, InstanceMethods)
49
23
  end
50
24
 
51
25
  end # module Virtus
@@ -6,6 +6,77 @@ module Virtus
6
6
  class Attribute
7
7
  extend DescendantsTracker
8
8
 
9
+ # Returns a Virtus::Attribute::Object descendant based on a name or class
10
+ #
11
+ # @example
12
+ # Attribute.determine_type('String') # => Virtus::Attribute::String
13
+ #
14
+ # @param [Class, #to_s] class_or_name
15
+ # name of a class or a class itself
16
+ #
17
+ # @return [Class]
18
+ # one of the Virtus::Attribute::Object descendants
19
+ #
20
+ # @return [nil]
21
+ # nil if the type cannot be determined by the class_or_name
22
+ #
23
+ # @api public
24
+ def self.determine_type(class_or_name)
25
+ # first match on the Attribute singleton class first, then match
26
+ # any class, finally fallback to matching on the string
27
+ case class_or_name
28
+ when Attribute::Object.singleton_class then determine_type_from_attribute(class_or_name)
29
+ when Class then determine_type_from_primitive(class_or_name)
30
+ else
31
+ determine_type_from_string(class_or_name.to_s)
32
+ end
33
+ end
34
+
35
+ # Return the Attribute class given an Attribute descendant
36
+ #
37
+ # @param [Class<Attribute>] attribute
38
+ #
39
+ # @return [Class<Attribute>]
40
+ #
41
+ # @api private
42
+ def self.determine_type_from_attribute(attribute)
43
+ attribute
44
+ end
45
+
46
+ private_class_method :determine_type_from_attribute
47
+
48
+ # Return the Attribute class given a primitive
49
+ #
50
+ # @param [Class] primitive
51
+ #
52
+ # @return [Class<Attribute>]
53
+ #
54
+ # @return [nil]
55
+ # nil if the type cannot be determined by the primitive
56
+ #
57
+ # @api private
58
+ def self.determine_type_from_primitive(primitive)
59
+ descendants.detect { |descendant| primitive <= descendant.primitive }
60
+ end
61
+
62
+ private_class_method :determine_type_from_primitive
63
+
64
+ # Return the Attribute class given a string
65
+ #
66
+ # @param [String] string
67
+ #
68
+ # @return [Class<Attribute>]
69
+ #
70
+ # @return [nil]
71
+ # nil if the type cannot be determined by the string
72
+ #
73
+ # @api private
74
+ def self.determine_type_from_string(string)
75
+ const_get(string) if const_defined?(string)
76
+ end
77
+
78
+ private_class_method :determine_type_from_string
79
+
9
80
  # Returns default options hash for a given attribute class
10
81
  #
11
82
  # @example
@@ -51,29 +122,23 @@ module Virtus
51
122
  #
52
123
  # @api public
53
124
  def self.accept_options(*new_options)
54
- # add new options to the array
55
- concat_options(new_options)
56
-
57
- # create methods for each new option
125
+ add_accepted_options(new_options)
58
126
  new_options.each { |option| add_option_method(option) }
59
-
60
- # add new options to all descendants
61
- descendants.each { |descendant| descendant.concat_options(new_options) }
62
-
63
- accepted_options
127
+ descendants.each { |descendant| descendant.add_accepted_options(new_options) }
128
+ self
64
129
  end
65
130
 
66
131
  # Adds a reader/writer method for the give option name
67
132
  #
68
- # @return [NilClass]
133
+ # @return [undefined]
69
134
  #
70
135
  # @api private
71
136
  def self.add_option_method(option)
72
137
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
73
- def self.#{option}(value = Undefined) # def self.unique(value = Undefined)
74
- return @#{option} if value.equal?(Undefined) # return @unique if value.equal?(Undefined)
75
- @#{option} = value # @unique = value
76
- end # end
138
+ def self.#{option}(value = Undefined) # def self.primitive(value = Undefined)
139
+ return @#{option} if value.equal?(Undefined) # return @primitive if value.equal?(Undefined)
140
+ @#{option} = value # @primitive = value
141
+ end # end
77
142
  RUBY
78
143
  end
79
144
 
@@ -84,14 +149,14 @@ module Virtus
84
149
  # @param [#to_hash] new_options
85
150
  # options to be set
86
151
  #
87
- # @return [Hash]
88
- # default options set on the attribute class
152
+ # @return [self]
89
153
  #
90
154
  # @api private
91
155
  def self.set_options(new_options)
92
156
  new_options.to_hash.each do |option_name, option_value|
93
157
  send(option_name, option_value)
94
158
  end
159
+ self
95
160
  end
96
161
 
97
162
  # Adds new options that an attribute class can accept
@@ -99,12 +164,12 @@ module Virtus
99
164
  # @param [#to_ary] new_options
100
165
  # new options to be added
101
166
  #
102
- # @return [Array]
103
- # all accepted options
167
+ # @return [self]
104
168
  #
105
169
  # @api private
106
- def self.concat_options(new_options)
107
- accepted_options.concat(new_options.to_ary).uniq
170
+ def self.add_accepted_options(new_options)
171
+ accepted_options.concat(new_options.to_ary)
172
+ self
108
173
  end
109
174
 
110
175
  # Adds descendant to descendants array and inherits default options
@@ -116,19 +181,18 @@ module Virtus
116
181
  # @api private
117
182
  def self.inherited(descendant)
118
183
  super
119
- descendant.concat_options(accepted_options)
120
- descendant.set_options(options)
184
+ descendant.add_accepted_options(accepted_options).set_options(options)
121
185
  self
122
186
  end
123
187
 
124
188
  # Returns if the given value's class is an attribute's primitive
125
189
  #
126
190
  # @example
127
- # Virtus::Attribute::String.primitive?('String') # => true
191
+ # Virtus::Attribute::String.primitive?('String') # => true
128
192
  #
129
- # @return [TrueClass, FalseClass]
193
+ # @return [Boolean]
130
194
  #
131
- # @api semipublic
195
+ # @api public
132
196
  def self.primitive?(value)
133
197
  value.kind_of?(primitive)
134
198
  end
@@ -136,7 +200,7 @@ module Virtus
136
200
  # Returns name of the attribute
137
201
  #
138
202
  # @example
139
- # User.attributes[:age].name # => :age
203
+ # User.attributes[:age].name # => :age
140
204
  #
141
205
  # @return [Symbol]
142
206
  #
@@ -185,6 +249,8 @@ module Virtus
185
249
  # @param [#to_hash] options
186
250
  # hash of extra options which overrides defaults set on an attribute class
187
251
  #
252
+ # @return [undefined]
253
+ #
188
254
  # @api private
189
255
  def initialize(name, options = {})
190
256
  @name = name
@@ -199,12 +265,12 @@ module Virtus
199
265
  # Returns if an attribute is a complex one
200
266
  #
201
267
  # @example
202
- # Virtus::Attribute::String.complex? # => false
203
- # Virtus::Attribute::Array.complex? # => true
268
+ # Virtus::Attribute::String.complex? # => false
269
+ # Virtus::Attribute::Array.complex? # => true
204
270
  #
205
- # @return [TrueClass, FalseClass]
271
+ # @return [Boolean]
206
272
  #
207
- # @api semipublic
273
+ # @api public
208
274
  def complex?
209
275
  @complex
210
276
  end
@@ -228,6 +294,8 @@ module Virtus
228
294
 
229
295
  # Converts the given value to the primitive type
230
296
  #
297
+ # @return [Object]
298
+ #
231
299
  # @api private
232
300
  def typecast_to_primitive(value)
233
301
  value
@@ -275,7 +343,7 @@ module Virtus
275
343
 
276
344
  # Creates an attribute reader method
277
345
  #
278
- # @return [NilClass]
346
+ # @return [self]
279
347
  #
280
348
  # @api private
281
349
  def add_reader_method(model)
@@ -283,22 +351,24 @@ module Virtus
283
351
  method_name = name
284
352
 
285
353
  model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
286
- module AttributeMethods
287
- def #{method_name}
288
- return #{instance_variable_name} if defined?(#{instance_variable_name})
289
- attribute = self.class.attributes[#{method_name.inspect}]
290
- #{instance_variable_name} = attribute ? attribute.get(self) : nil
291
- end
292
- end
293
- include AttributeMethods
354
+ module AttributeMethods # module AttributeMethods
355
+ def #{method_name} # def name
356
+ return #{instance_variable_name} if defined?(#{instance_variable_name}) # return @name if defined?(@name)
357
+ attribute = self.class.attributes[#{method_name.inspect}] # attribute = self.class.attributes[:name]
358
+ #{instance_variable_name} = attribute ? attribute.get(self) : nil # @name = attribute ? attribute.get(self) : nil
359
+ end # end
360
+ end # end
361
+ include AttributeMethods # include AttributeMethods
294
362
  RUBY
295
363
 
296
364
  model.send(reader_visibility, method_name)
365
+
366
+ self
297
367
  end
298
368
 
299
369
  # Creates an attribute writer method
300
370
  #
301
- # @return [NilClass]
371
+ # @return [self]
302
372
  #
303
373
  # @api private
304
374
  def add_writer_method(model)
@@ -306,15 +376,17 @@ module Virtus
306
376
  method_name = "#{name}="
307
377
 
308
378
  model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
309
- module AttributeMethods
310
- def #{method_name}(value)
311
- self.class.attributes[#{name.inspect}].set(self, value)
312
- end
313
- end
314
- include AttributeMethods
379
+ module AttributeMethods # module AttributeMethods
380
+ def #{method_name}(value) # def name=(value)
381
+ self.class.attributes[#{name.inspect}].set(self, value) # self.class.attributes[:name].set(self, value)
382
+ end # end
383
+ end # end
384
+ include AttributeMethods # include AttributeMethods
315
385
  RUBY
316
386
 
317
387
  model.send(writer_visibility, method_name)
388
+
389
+ self
318
390
  end
319
391
 
320
392
  private