active_attr 0.4.1 → 0.5.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of active_attr might be problematic. Click here for more details.

Files changed (53) hide show
  1. data/.travis.yml +7 -0
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +57 -25
  4. data/Rakefile +2 -0
  5. data/active_attr.gemspec +3 -2
  6. data/gemfiles/rails_3_1.gemfile +6 -0
  7. data/lib/active_attr.rb +3 -2
  8. data/lib/active_attr/attribute_defaults.rb +120 -0
  9. data/lib/active_attr/attribute_definition.rb +25 -2
  10. data/lib/active_attr/attributes.rb +44 -33
  11. data/lib/active_attr/logger.rb +8 -8
  12. data/lib/active_attr/mass_assignment.rb +2 -1
  13. data/lib/active_attr/matchers/have_attribute_matcher.rb +28 -10
  14. data/lib/active_attr/model.rb +2 -0
  15. data/lib/active_attr/query_attributes.rb +2 -5
  16. data/lib/active_attr/typecasted_attributes.rb +77 -0
  17. data/lib/active_attr/typecasting.rb +60 -0
  18. data/lib/active_attr/typecasting/boolean.rb +6 -0
  19. data/lib/active_attr/typecasting/boolean_typecaster.rb +21 -0
  20. data/lib/active_attr/typecasting/date_time_typecaster.rb +14 -0
  21. data/lib/active_attr/typecasting/date_typecaster.rb +14 -0
  22. data/lib/active_attr/typecasting/float_typecaster.rb +11 -0
  23. data/lib/active_attr/typecasting/integer_typecaster.rb +12 -0
  24. data/lib/active_attr/typecasting/object_typecaster.rb +9 -0
  25. data/lib/active_attr/typecasting/string_typecaster.rb +11 -0
  26. data/lib/active_attr/version.rb +1 -1
  27. data/spec/functional/active_attr/attribute_defaults_spec.rb +136 -0
  28. data/spec/functional/active_attr/attributes_spec.rb +19 -5
  29. data/spec/functional/active_attr/model_spec.rb +34 -4
  30. data/spec/functional/active_attr/typecasted_attributes_spec.rb +116 -0
  31. data/spec/support/mass_assignment_shared_examples.rb +1 -1
  32. data/spec/unit/active_attr/attribute_defaults_spec.rb +43 -0
  33. data/spec/unit/active_attr/attribute_definition_spec.rb +12 -8
  34. data/spec/unit/active_attr/attributes_spec.rb +22 -10
  35. data/spec/unit/active_attr/mass_assignment_spec.rb +1 -0
  36. data/spec/unit/active_attr/matchers/have_attribute_matcher_spec.rb +94 -15
  37. data/spec/unit/active_attr/query_attributes_spec.rb +1 -1
  38. data/spec/unit/active_attr/typecasted_attributes_spec.rb +76 -0
  39. data/spec/unit/active_attr/typecasting/boolean_spec.rb +10 -0
  40. data/spec/unit/active_attr/typecasting/boolean_typecaster_spec.rb +147 -0
  41. data/spec/unit/active_attr/typecasting/date_time_typecaster_spec.rb +67 -0
  42. data/spec/unit/active_attr/typecasting/date_typecaster_spec.rb +55 -0
  43. data/spec/unit/active_attr/typecasting/float_typecaster_spec.rb +27 -0
  44. data/spec/unit/active_attr/typecasting/integer_typecaster_spec.rb +35 -0
  45. data/spec/unit/active_attr/typecasting/object_typecaster_spec.rb +15 -0
  46. data/spec/unit/active_attr/typecasting/string_typecaster_spec.rb +24 -0
  47. data/spec/unit/active_attr/typecasting_spec.rb +87 -0
  48. data/spec/unit/active_attr/version_spec.rb +11 -2
  49. metadata +75 -29
  50. data/lib/active_attr/strict_mass_assignment.rb +0 -44
  51. data/lib/active_attr/unknown_attributes_error.rb +0 -18
  52. data/spec/unit/active_attr/strict_mass_assignment_spec.rb +0 -38
  53. data/spec/unit/active_attr/unknown_attributes_error_spec.rb +0 -9
@@ -5,4 +5,11 @@ rvm:
5
5
  bundler_args: --without development
6
6
  gemfile:
7
7
  - Gemfile
8
+ - gemfiles/rails_3_1.gemfile
8
9
  - gemfiles/rails_head.gemfile
10
+ matrix:
11
+ exclude:
12
+ - rvm: ree
13
+ gemfile: gemfiles/rails_head.gemfile
14
+ - rvm: 1.9.2
15
+ gemfile: gemfiles/rails_head.gemfile
@@ -1,3 +1,17 @@
1
+ # ActiveAttr 0.5.0 (unreleased) #
2
+
3
+ * Added AttributeDefaults
4
+ * Added AttributeDefinition#[]
5
+ * Added Attributes.attribute_names
6
+ * Added Matchers::HaveAttributeMatcher#with_default_value_of
7
+ * Added TypecastedAttributes
8
+ * Added Typecasting
9
+ * Changed Attributes.attributes return value from an Array to a Hash
10
+ * Changed redefining an attribute to actually redefine the attribute
11
+ * Removed StrictMassAssignment, instead use MassAssignmentSecurity with
12
+ ActiveModel v3.2 which allows assigning mass_assignment_sanitizer to
13
+ :strict on the class
14
+
1
15
  # ActiveAttr 0.4.1 (November 27, 2011) #
2
16
 
3
17
  * Implemented ActiveModel serialization in Model
data/README.md CHANGED
@@ -2,13 +2,18 @@
2
2
 
3
3
  [![Build History][2]][1]
4
4
 
5
- ActiveAttr makes it easy to create plain old ruby models without reinventing
6
- the wheel.
5
+ ActiveAttr is a set of modules that makes it easy to create plain old ruby
6
+ models with functionality found in ORMs, like ActiveRecord, without
7
+ reinventing the wheel. Think of ActiveAttr as the stuff ActiveModel left out.
7
8
 
8
- [API Documentation](http://rubydoc.info/gems/active_attr)
9
+ ActiveAttr is distributed as a rubygem [on rubygems.org][3].
10
+
11
+ * [API Documentation](http://rubydoc.info/gems/active_attr)
12
+ * [Contributors](https://github.com/cgriego/active_attr/contributors)
9
13
 
10
14
  [1]: http://travis-ci.org/cgriego/active_attr
11
15
  [2]: https://secure.travis-ci.org/cgriego/active_attr.png?branch=master
16
+ [3]: http://rubygems.org/gems/active_attr
12
17
 
13
18
  ## Modules ##
14
19
 
@@ -39,6 +44,52 @@ the attributes of your model.
39
44
  person.last_name = "Griego"
40
45
  person.attributes #=> {"first_name"=>"Chris", "last_name"=>"Griego"}
41
46
 
47
+ #### AttributeDefaults ####
48
+
49
+ Including the AttributeDefaults module into your class builds on Attributes by
50
+ allowing defaults to be declared with attributes.
51
+
52
+ class Person
53
+ include ActiveAttr::AttributeDefaults
54
+
55
+ attribute :first_name, :default => "John"
56
+ attribute :last_name, :default => "Doe"
57
+ end
58
+
59
+ person = Person.new
60
+ person.first_name #=> "John"
61
+ person.last_name #=> "Doe"
62
+
63
+ #### QueryAttributes ####
64
+
65
+ Including the QueryAttributes module into your class builds on Attributes by
66
+ providing instance methods for querying your attributes.
67
+
68
+ class Person
69
+ include ActiveAttr::QueryAttributes
70
+
71
+ attribute :first_name
72
+ attribute :last_name
73
+ end
74
+
75
+ person = Person.new
76
+ person.first_name = "Chris"
77
+ person.first_name? #=> true
78
+ person.last_name? #=> false
79
+
80
+ #### TypecastedAttributes ####
81
+
82
+ TODO documentation
83
+
84
+ class Person
85
+ include ActiveAttr::TypecastedAttributes
86
+ attribute :age, :type => Integer
87
+ end
88
+
89
+ person = Person.new
90
+ person.age = "29"
91
+ person.age #=> 29
92
+
42
93
  ### BasicModel ###
43
94
 
44
95
  Including the BasicModel module into your class gives you the bare minimum
@@ -96,9 +147,7 @@ logger will be configured by default.
96
147
 
97
148
  Including the MassAssignment module into your class gives you methods for bulk
98
149
  initializing and updating the attributes of your model. Any unknown attributes
99
- are silently ignored unless you substitute the StrictMassAssignment module
100
- which will raise an exception if an attempt is made to assign an unknown
101
- attribute.
150
+ are silently ignored.
102
151
 
103
152
  class Person
104
153
  include ActiveAttr::MassAssignment
@@ -110,7 +159,7 @@ attribute.
110
159
  person.first_name #=> "Chris"
111
160
  person.last_name #=> "Griego"
112
161
 
113
- ### MassAssignmentSecurity ###
162
+ #### MassAssignmentSecurity ####
114
163
 
115
164
  Including the MassAssignmentSecurity module into your class extends the
116
165
  MassAssignment methods to honor any declared mass assignment permission
@@ -126,23 +175,6 @@ blacklists or whitelists including support for mass assignment roles.
126
175
  person.first_name #=> "Chris"
127
176
  person.last_name #=> nil
128
177
 
129
- ### QueryAttributes ###
130
-
131
- Including the QueryAttributes module into your class builds on Attributes by
132
- providing instance methods for querying your attributes
133
-
134
- class Person
135
- include ActiveAttr::QueryAttributes
136
-
137
- attribute :first_name
138
- attribute :last_name
139
- end
140
-
141
- person = Person.new
142
- person.first_name = "Chris"
143
- person.first_name? #=> true
144
- person.last_name? #=> false
145
-
146
178
  ## Integrations ##
147
179
 
148
180
  ### Ruby on Rails ###
@@ -161,5 +193,5 @@ your models. The matchers also work with compatible frameworks like Shoulda.
161
193
  require "active_attr/rspec"
162
194
 
163
195
  describe Person do
164
- it { should have_attribute(:first_name) }
196
+ it { should have_attribute(:first_name).with_default_value_of("John") }
165
197
  end
data/Rakefile CHANGED
@@ -15,10 +15,12 @@ namespace :spec do
15
15
  desc "Run RSpec unit specs"
16
16
  RSpec::Core::RakeTask.new(:units) do |spec|
17
17
  spec.pattern = "spec/unit/**/*_spec.rb"
18
+ spec.ruby_opts = "-w"
18
19
  end
19
20
 
20
21
  desc "Run RSpec functional specs"
21
22
  RSpec::Core::RakeTask.new(:functionals) do |spec|
22
23
  spec.pattern = "spec/functional/**/*_spec.rb"
24
+ spec.ruby_opts = "-w"
23
25
  end
24
26
  end
@@ -18,11 +18,12 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.add_runtime_dependency "activemodel", "~> 3.1"
22
- s.add_runtime_dependency "activesupport", "~> 3.1"
21
+ s.add_runtime_dependency "activemodel", ">= 3.1", "< 4.1"
22
+ s.add_runtime_dependency "activesupport", ">= 3.1", "< 4.1"
23
23
 
24
24
  s.add_development_dependency "bundler", "~> 1.0"
25
25
  s.add_development_dependency "factory_girl", "~> 2.2"
26
26
  s.add_development_dependency "rake", "~> 0.9.0"
27
27
  s.add_development_dependency "rspec", "~> 2.6"
28
+ s.add_development_dependency "tzinfo", "~> 0.3.29"
28
29
  end
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec :development_group => :test, :path => ".."
4
+
5
+ gem "activemodel", "~> 3.1.0"
6
+ gem "activesupport", "~> 3.1.0"
@@ -9,6 +9,7 @@ require "active_support/dependencies/autoload"
9
9
  module ActiveAttr
10
10
  extend ActiveSupport::Autoload
11
11
 
12
+ autoload :AttributeDefaults
12
13
  autoload :AttributeDefinition
13
14
  autoload :Attributes
14
15
  autoload :BasicModel
@@ -19,8 +20,8 @@ module ActiveAttr
19
20
  autoload :MassAssignmentSecurity
20
21
  autoload :Model
21
22
  autoload :QueryAttributes
22
- autoload :StrictMassAssignment
23
+ autoload :TypecastedAttributes
24
+ autoload :Typecasting
23
25
  autoload :UnknownAttributeError
24
- autoload :UnknownAttributesError
25
26
  autoload :VERSION
26
27
  end
@@ -0,0 +1,120 @@
1
+ require "active_attr/attributes"
2
+ require "active_attr/chainable_initialization"
3
+ require "active_support/concern"
4
+ require "active_support/core_ext/object/duplicable"
5
+
6
+ module ActiveAttr
7
+ # AttributeDefaults allows defaults to be declared for your attributes
8
+ #
9
+ # Defaults are declared by passing the :default option to the attribute
10
+ # class method. If you need the default to be dynamic, pass a lambda, Proc,
11
+ # or any object that responds to #call as the value to the :default option
12
+ # and the result will calculated on initialization. These dynamic defaults
13
+ # can depend on the values of other attributes when those attributes are
14
+ # assigned using MassAssignment or BlockInitialization.
15
+ #
16
+ # @example Usage
17
+ # class Person
18
+ # include ActiveAttr::AttributeDefaults
19
+ #
20
+ # attribute :first_name, :default => "John"
21
+ # attribute :last_name, :default => "Doe"
22
+ # end
23
+ #
24
+ # person = Person.new
25
+ # person.first_name #=> "John"
26
+ # person.last_name #=> "Doe"
27
+ #
28
+ # @example Dynamic Default
29
+ # class Event
30
+ # include ActiveAttr::MassAssignment
31
+ # include ActiveAttr::AttributeDefaults
32
+ #
33
+ # attribute :start_date
34
+ # attribute :end_date, :default => lambda { start_date }
35
+ # end
36
+ #
37
+ # event = Event.new(:start_date => Date.parse("2012-01-01"))
38
+ # event.end_date.to_s #=> "2012-01-01"
39
+ #
40
+ # @since 0.5.0
41
+ module AttributeDefaults
42
+ extend ActiveSupport::Concern
43
+ include ActiveAttr::ChainableInitialization
44
+ include Attributes
45
+
46
+ # Applies the attribute defaults
47
+ #
48
+ # Applies all the default values to any attributes not yet set, avoiding
49
+ # any attribute setter logic, such as dirty tracking.
50
+ #
51
+ # @example Usage
52
+ # class Person
53
+ # include ActiveAttr::AttributeDefaults
54
+ #
55
+ # attribute :first_name, :default => "John"
56
+ #
57
+ # def reset!
58
+ # @attributes = {}
59
+ # apply_defaults
60
+ # end
61
+ # end
62
+ #
63
+ # person = Person.new(:first_name => "Chris")
64
+ # person.reset!
65
+ # person.first_name #=> "John"
66
+ #
67
+ # @param [Hash{String => Object}, #each] defaults The defaults to apply
68
+ #
69
+ # @since 0.5.0
70
+ def apply_defaults(defaults=attribute_defaults)
71
+ @attributes ||= {}
72
+ defaults.each do |name, value|
73
+ # instance variable is used here to avoid any dirty tracking in attribute setter methods
74
+ @attributes[name] = value unless @attributes.has_key? name
75
+ end
76
+ end
77
+
78
+ # Calculates the attribute defaults from the attribute definitions
79
+ #
80
+ # @example Usage
81
+ # class Person
82
+ # include ActiveAttr::AttributeDefaults
83
+ #
84
+ # attribute :first_name, :default => "John"
85
+ # end
86
+ #
87
+ # Person.new.attribute_defaults #=> {"first_name"=>"John"}
88
+ #
89
+ # @return [Hash{String => Object}] the attribute defaults
90
+ #
91
+ # @since 0.5.0
92
+ def attribute_defaults
93
+ Hash[ self.class.attribute_names.map { |name| [name, _attribute_default(name)] } ]
94
+ end
95
+
96
+ # Applies attribute default values
97
+ #
98
+ # @since 0.5.0
99
+ def initialize(*)
100
+ super
101
+ apply_defaults
102
+ end
103
+
104
+ private
105
+
106
+ # Calculates an attribute default
107
+ #
108
+ # @private
109
+ # @since 0.5.0
110
+ def _attribute_default(attribute_name)
111
+ default = self.class.attributes[attribute_name][:default]
112
+
113
+ case
114
+ when default.respond_to?(:call) then instance_exec(&default)
115
+ when default.duplicable? then default.dup
116
+ else default
117
+ end
118
+ end
119
+ end
120
+ end
@@ -1,3 +1,6 @@
1
+ require 'active_support/core_ext/hash/reverse_merge'
2
+ require 'active_support/core_ext/string/inflections'
3
+
1
4
  module ActiveAttr
2
5
  # Represents an attribute for reflection
3
6
  #
@@ -17,16 +20,30 @@ module ActiveAttr
17
20
  # @example
18
21
  # attribute_definition <=> other
19
22
  #
20
- # @param [ActiveAttr::AttributeDefinition, Object] other The other attribute definition to compare with.
23
+ # @param [ActiveAttr::AttributeDefinition, Object] other The other
24
+ # attribute definition to compare with.
21
25
  #
22
26
  # @return [-1, 0, 1, nil]
23
27
  #
24
28
  # @since 0.2.1
25
29
  def <=>(other)
26
30
  return nil unless other.instance_of? self.class
31
+ return nil if name == other.name && options != other.options
27
32
  self.name.to_s <=> other.name.to_s
28
33
  end
29
34
 
35
+ # Read an attribute option
36
+ #
37
+ # @example
38
+ # attribute_definition[:type]
39
+ #
40
+ # @param [Symbol] key The option key
41
+ #
42
+ # @since 0.5.0
43
+ def [](key)
44
+ @options[key]
45
+ end
46
+
30
47
  # Creates a new AttributeDefinition
31
48
  #
32
49
  # @example Create an attribute defintion
@@ -40,6 +57,7 @@ module ActiveAttr
40
57
  def initialize(name, options={})
41
58
  raise TypeError, "can't convert #{name.class} into Symbol" unless name.respond_to? :to_sym
42
59
  @name = name.to_sym
60
+ @options = options
43
61
  end
44
62
 
45
63
  # The attribute name
@@ -50,7 +68,6 @@ module ActiveAttr
50
68
  def to_s
51
69
  name.to_s
52
70
  end
53
- alias_method :inspect, :to_s
54
71
 
55
72
  # The attribute name
56
73
  #
@@ -60,5 +77,11 @@ module ActiveAttr
60
77
  def to_sym
61
78
  name
62
79
  end
80
+
81
+ protected
82
+
83
+ # The attribute options
84
+ # @since 0.5.0
85
+ attr_reader :options
63
86
  end
64
87
  end
@@ -1,9 +1,9 @@
1
1
  require "active_attr/attribute_definition"
2
- require "active_attr/chainable_initialization"
3
2
  require "active_attr/dangerous_attribute_error"
4
3
  require "active_attr/unknown_attribute_error"
5
4
  require "active_model"
6
5
  require "active_support/concern"
6
+ require "active_support/hash_with_indifferent_access"
7
7
 
8
8
  module ActiveAttr
9
9
  # Attributes provides a set of class methods for defining an attributes
@@ -21,7 +21,6 @@ module ActiveAttr
21
21
  # @since 0.2.0
22
22
  module Attributes
23
23
  extend ActiveSupport::Concern
24
- include ActiveAttr::ChainableInitialization
25
24
  include ActiveModel::AttributeMethods
26
25
 
27
26
  # Methods deprecated on the Object class which can be safely overridden
@@ -38,9 +37,10 @@ module ActiveAttr
38
37
  # @example Compare for equality.
39
38
  # model == other
40
39
  #
41
- # @param [ActiveAttr::Attributes, Object] other The other model to compare with.
40
+ # @param [ActiveAttr::Attributes, Object] other The other model to compare
42
41
  #
43
- # @return [true, false] True if attributes are equal and other is instance of the same Class, false if not.
42
+ # @return [true, false] True if attributes are equal and other is instance
43
+ # of the same Class, false if not.
44
44
  #
45
45
  # @since 0.2.0
46
46
  def ==(other)
@@ -53,18 +53,11 @@ module ActiveAttr
53
53
  # @example Get attributes
54
54
  # person.attributes # => {"name"=>"Ben Poweski"}
55
55
  #
56
- # @return [Hash] The Hash of all attributes
56
+ # @return [Hash{String => Object}] The Hash of all attributes
57
57
  #
58
58
  # @since 0.2.0
59
59
  def attributes
60
- attribute_names = self.class.attributes.map { |definition| definition.name.to_s }
61
- Hash[attribute_names.map { |key| [key, send(key)] }]
62
- end
63
-
64
- # @since 0.2.1
65
- def initialize(*)
66
- @attributes ||= {}
67
- super
60
+ Hash[self.class.attribute_names.map { |key| [key, send(key)] }]
68
61
  end
69
62
 
70
63
  # Returns the class name plus its attributes
@@ -72,11 +65,12 @@ module ActiveAttr
72
65
  # @example Inspect the model.
73
66
  # person.inspect
74
67
  #
75
- # @return [String] A nice pretty string to look at.
68
+ # @return [String] Human-readable presentation of the attribute
69
+ # definitions
76
70
  #
77
71
  # @since 0.2.0
78
72
  def inspect
79
- attribute_descriptions = self.attributes.sort.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
73
+ attribute_descriptions = attributes.sort.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
80
74
  separator = " " unless attribute_descriptions.empty?
81
75
  "#<#{self.class.name}#{separator}#{attribute_descriptions}>"
82
76
  end
@@ -131,7 +125,7 @@ module ActiveAttr
131
125
  # Overrides ActiveModel::AttributeMethods
132
126
  # @private
133
127
  def attribute_method?(attr_name)
134
- self.class.attributes.map { |definition| definition.name.to_s }.include? attr_name.to_s
128
+ self.class.attribute_names.include? attr_name.to_s
135
129
  end
136
130
 
137
131
  private
@@ -140,61 +134,77 @@ module ActiveAttr
140
134
  #
141
135
  # @since 0.2.1
142
136
  def attribute(name)
143
- @attributes[name.to_s]
137
+ @attributes ||= {}
138
+ @attributes[name]
144
139
  end
145
140
 
146
141
  # Write an attribute to the attributes hash
147
142
  #
148
143
  # @since 0.2.1
149
144
  def attribute=(name, value)
150
- @attributes[name.to_s] = value
145
+ @attributes ||= {}
146
+ @attributes[name] = value
151
147
  end
152
148
 
153
149
  module ClassMethods
154
- # Defines all the attributes that are to be returned from the attributes instance method.
150
+ # Defines an attribute
151
+ #
155
152
  # For each attribute that is defined, a getter and setter will be
156
- # added as an instance method to the model. An AttributeDefinition instance will be
157
- # added to result of the attributes class method.
153
+ # added as an instance method to the model. An AttributeDefinition
154
+ # instance will be added to result of the attributes class method.
158
155
  #
159
156
  # @example Define an attribute.
160
157
  # attribute :name
161
158
  #
162
159
  # @param (see AttributeDefinition#initialize)
163
160
  #
164
- # @raise [DangerousAttributeError] if the attribute name conflicts with existing methods
161
+ # @raise [DangerousAttributeError] if the attribute name conflicts with
162
+ # existing methods
165
163
  #
166
164
  # @since 0.2.0
167
165
  def attribute(name, options={})
168
166
  AttributeDefinition.new(name, options).tap do |attribute_definition|
169
- unless attributes.include? attribute_definition
170
- define_attribute_method attribute_definition.name
171
- attributes << attribute_definition
172
- end
167
+ attribute_name = attribute_definition.name.to_s
168
+ define_attribute_method attribute_definition.name unless attribute_names.include? attribute_name
169
+ attributes[attribute_name] = attribute_definition
173
170
  end
174
171
  end
175
172
 
176
- # Returns an Array of AttributeDefinition instances
173
+ # Returns an Array of attribute names as Strings
174
+ #
175
+ # @example Get attribute names
176
+ # Person.attribute_names
177
+ #
178
+ # @return [Array<String>] The attribute names
179
+ #
180
+ # @since 0.5.0
181
+ def attribute_names
182
+ attributes.keys
183
+ end
184
+
185
+ # Returns a Hash of AttributeDefinition instances
177
186
  #
178
187
  # @example Get attribute definitions
179
188
  # Person.attributes
180
189
  #
181
- # @return [Array<ActiveAttr::AttributeDefinition>] The Array of AttributeDefinition instances
190
+ # @return [ActiveSupport::HashWithIndifferentAccess{String => ActiveAttr::AttributeDefinition}]
191
+ # The Hash of AttributeDefinition instances
182
192
  #
183
193
  # @since 0.2.0
184
194
  def attributes
185
- @attributes ||= []
195
+ @attributes ||= ActiveSupport::HashWithIndifferentAccess.new
186
196
  end
187
197
 
188
- # Returns the class name plus its attribute definitions
198
+ # Returns the class name plus its attribute names
189
199
  #
190
200
  # @example Inspect the model's definition.
191
201
  # Person.inspect
192
202
  #
193
- # @return [String] A nice pretty string to look at.
203
+ # @return [String] Human-readable presentation of the attributes
194
204
  #
195
205
  # @since 0.2.0
196
206
  def inspect
197
- inspected_attributes = attributes.sort.map { |attr| attr.inspect }
207
+ inspected_attributes = attribute_names.sort
198
208
  attributes_list = "(#{inspected_attributes.join(", ")})" unless inspected_attributes.empty?
199
209
  "#{self.name}#{attributes_list}"
200
210
  end
@@ -203,7 +213,8 @@ module ActiveAttr
203
213
 
204
214
  # Assign a set of attribute definitions, used when subclassing models
205
215
  #
206
- # @param [Array<ActiveAttr::AttributeDefinition>] The Array of AttributeDefinition instances
216
+ # @param [Array<ActiveAttr::AttributeDefinition>] The Array of
217
+ # AttributeDefinition instances
207
218
  #
208
219
  # @since 0.2.2
209
220
  def attributes=(attributes)