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.
- data/.travis.yml +7 -0
- data/CHANGELOG.md +14 -0
- data/README.md +57 -25
- data/Rakefile +2 -0
- data/active_attr.gemspec +3 -2
- data/gemfiles/rails_3_1.gemfile +6 -0
- data/lib/active_attr.rb +3 -2
- data/lib/active_attr/attribute_defaults.rb +120 -0
- data/lib/active_attr/attribute_definition.rb +25 -2
- data/lib/active_attr/attributes.rb +44 -33
- data/lib/active_attr/logger.rb +8 -8
- data/lib/active_attr/mass_assignment.rb +2 -1
- data/lib/active_attr/matchers/have_attribute_matcher.rb +28 -10
- data/lib/active_attr/model.rb +2 -0
- data/lib/active_attr/query_attributes.rb +2 -5
- data/lib/active_attr/typecasted_attributes.rb +77 -0
- data/lib/active_attr/typecasting.rb +60 -0
- data/lib/active_attr/typecasting/boolean.rb +6 -0
- data/lib/active_attr/typecasting/boolean_typecaster.rb +21 -0
- data/lib/active_attr/typecasting/date_time_typecaster.rb +14 -0
- data/lib/active_attr/typecasting/date_typecaster.rb +14 -0
- data/lib/active_attr/typecasting/float_typecaster.rb +11 -0
- data/lib/active_attr/typecasting/integer_typecaster.rb +12 -0
- data/lib/active_attr/typecasting/object_typecaster.rb +9 -0
- data/lib/active_attr/typecasting/string_typecaster.rb +11 -0
- data/lib/active_attr/version.rb +1 -1
- data/spec/functional/active_attr/attribute_defaults_spec.rb +136 -0
- data/spec/functional/active_attr/attributes_spec.rb +19 -5
- data/spec/functional/active_attr/model_spec.rb +34 -4
- data/spec/functional/active_attr/typecasted_attributes_spec.rb +116 -0
- data/spec/support/mass_assignment_shared_examples.rb +1 -1
- data/spec/unit/active_attr/attribute_defaults_spec.rb +43 -0
- data/spec/unit/active_attr/attribute_definition_spec.rb +12 -8
- data/spec/unit/active_attr/attributes_spec.rb +22 -10
- data/spec/unit/active_attr/mass_assignment_spec.rb +1 -0
- data/spec/unit/active_attr/matchers/have_attribute_matcher_spec.rb +94 -15
- data/spec/unit/active_attr/query_attributes_spec.rb +1 -1
- data/spec/unit/active_attr/typecasted_attributes_spec.rb +76 -0
- data/spec/unit/active_attr/typecasting/boolean_spec.rb +10 -0
- data/spec/unit/active_attr/typecasting/boolean_typecaster_spec.rb +147 -0
- data/spec/unit/active_attr/typecasting/date_time_typecaster_spec.rb +67 -0
- data/spec/unit/active_attr/typecasting/date_typecaster_spec.rb +55 -0
- data/spec/unit/active_attr/typecasting/float_typecaster_spec.rb +27 -0
- data/spec/unit/active_attr/typecasting/integer_typecaster_spec.rb +35 -0
- data/spec/unit/active_attr/typecasting/object_typecaster_spec.rb +15 -0
- data/spec/unit/active_attr/typecasting/string_typecaster_spec.rb +24 -0
- data/spec/unit/active_attr/typecasting_spec.rb +87 -0
- data/spec/unit/active_attr/version_spec.rb +11 -2
- metadata +75 -29
- data/lib/active_attr/strict_mass_assignment.rb +0 -44
- data/lib/active_attr/unknown_attributes_error.rb +0 -18
- data/spec/unit/active_attr/strict_mass_assignment_spec.rb +0 -38
- data/spec/unit/active_attr/unknown_attributes_error_spec.rb +0 -9
data/.travis.yml
CHANGED
@@ -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
|
data/CHANGELOG.md
CHANGED
@@ -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
|
6
|
-
|
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
|
-
[
|
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
|
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
|
-
|
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
|
data/active_attr.gemspec
CHANGED
@@ -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", "
|
22
|
-
s.add_runtime_dependency "activesupport", "
|
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
|
data/lib/active_attr.rb
CHANGED
@@ -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 :
|
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
|
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
|
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
|
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
|
-
|
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]
|
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 =
|
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.
|
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
|
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
|
145
|
+
@attributes ||= {}
|
146
|
+
@attributes[name] = value
|
151
147
|
end
|
152
148
|
|
153
149
|
module ClassMethods
|
154
|
-
# Defines
|
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
|
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
|
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
|
-
|
170
|
-
|
171
|
-
|
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
|
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 [
|
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
|
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]
|
203
|
+
# @return [String] Human-readable presentation of the attributes
|
194
204
|
#
|
195
205
|
# @since 0.2.0
|
196
206
|
def inspect
|
197
|
-
inspected_attributes =
|
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
|
216
|
+
# @param [Array<ActiveAttr::AttributeDefinition>] The Array of
|
217
|
+
# AttributeDefinition instances
|
207
218
|
#
|
208
219
|
# @since 0.2.2
|
209
220
|
def attributes=(attributes)
|