active_attr 0.4.1 → 0.5.0.alpha1
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.
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)
|