active_attr 0.1.0 → 0.2.0
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/.document +1 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +14 -2
- data/Gemfile +4 -1
- data/Guardfile +5 -4
- data/MIT-LICENSE +1 -1
- data/README.md +52 -1
- data/active_attr.gemspec +2 -2
- data/lib/active_attr.rb +16 -1
- data/lib/active_attr/attribute_definition.rb +52 -0
- data/lib/active_attr/attributes.rb +138 -0
- data/lib/active_attr/basic_model.rb +25 -0
- data/lib/active_attr/error.rb +16 -0
- data/lib/active_attr/mass_assignment.rb +48 -7
- data/lib/active_attr/matchers.rb +13 -0
- data/lib/active_attr/matchers/have_attribute_matcher.rb +69 -0
- data/lib/active_attr/rspec.rb +5 -0
- data/lib/active_attr/strict_mass_assignment.rb +44 -0
- data/lib/active_attr/unknown_attributes_error.rb +18 -0
- data/lib/active_attr/version.rb +3 -1
- data/spec/active_attr/attribute_definition_spec.rb +46 -0
- data/spec/active_attr/attributes_spec.rb +141 -0
- data/spec/active_attr/basic_model_spec.rb +19 -0
- data/spec/active_attr/error_spec.rb +8 -0
- data/spec/active_attr/mass_assignment_spec.rb +7 -95
- data/spec/active_attr/matchers/have_attribute_matcher_spec.rb +103 -0
- data/spec/active_attr/strict_mass_assignment_spec.rb +38 -0
- data/spec/active_attr/unknown_attributes_error_spec.rb +9 -0
- data/spec/active_attr/version_spec.rb +35 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/active_model_lint.rb +1 -1
- data/spec/support/mass_assignment_shared_examples.rb +95 -0
- metadata +44 -14
data/.document
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
- CHANGELOG* *LICENSE*
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
|
+
# ActiveAttr 0.2.0 (October 3, 2011) #
|
2
|
+
|
3
|
+
* ActiveAttr now autoloads nested classes and modules
|
4
|
+
* Added AttributeDefinition
|
5
|
+
* Added Attributes
|
6
|
+
* Added BasicModel
|
7
|
+
* Added Error
|
8
|
+
* Added Matchers::HaveAttributeMatcher
|
9
|
+
* Added StrictMassAssignment
|
10
|
+
* Added UnknownAttributesError
|
11
|
+
* Documented everything
|
12
|
+
|
1
13
|
# ActiveAttr 0.1.0 (September 30, 2011) #
|
2
14
|
|
3
|
-
* Added MassAssignment#initialize
|
4
|
-
* Added MassAssignment#attributes=
|
5
15
|
* Added MassAssignment#assign_attributes
|
16
|
+
* Added MassAssignment#attributes=
|
17
|
+
* Added MassAssignment#initialize
|
data/Gemfile
CHANGED
@@ -8,7 +8,10 @@ group :development do
|
|
8
8
|
gem "guard-bundler"
|
9
9
|
gem "guard-rspec"
|
10
10
|
gem "rb-fsevent"
|
11
|
-
gem
|
11
|
+
gem "rdiscount"
|
12
|
+
gem "rdoc"
|
13
|
+
gem "ruby-debug", :platforms => :mri_18
|
12
14
|
gem "ruby-debug19", :platforms => :mri_19 if RUBY_VERSION < "1.9.3"
|
13
15
|
gem "spec_coverage", :platforms => :mri_19
|
16
|
+
gem "yard"
|
14
17
|
end
|
data/Guardfile
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
guard
|
2
|
-
watch(
|
1
|
+
guard "bundler" do
|
2
|
+
watch("Gemfile")
|
3
3
|
watch(/^.+\.gemspec/)
|
4
4
|
end
|
5
5
|
|
6
|
-
guard
|
6
|
+
guard "rspec", :version => 2, :cli=> "--format nested --debugger" do
|
7
7
|
watch(%r{^spec/.+_spec\.rb$})
|
8
8
|
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
9
|
-
watch(
|
9
|
+
watch("spec/spec_helper.rb") { "spec" }
|
10
|
+
watch(%r{^spec/support/(.+)\.rb$}) { |m| "spec" }
|
10
11
|
end
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,58 @@
|
|
2
2
|
|
3
3
|
[![Build History][2]][1]
|
4
4
|
|
5
|
-
ActiveAttr makes it easy to create plain old ruby models without reinventing
|
5
|
+
ActiveAttr makes it easy to create plain old ruby models without reinventing
|
6
|
+
the wheel.
|
7
|
+
|
8
|
+
[API Documentation](http://rubydoc.info/gems/active_attr)
|
6
9
|
|
7
10
|
[1]: http://travis-ci.org/cgriego/active_attr
|
8
11
|
[2]: https://secure.travis-ci.org/cgriego/active_attr.png?branch=master
|
12
|
+
|
13
|
+
## Attributes ##
|
14
|
+
|
15
|
+
Including the Attributes module into your class gives you a DSL for defining
|
16
|
+
the attributes of your model.
|
17
|
+
|
18
|
+
class Person
|
19
|
+
include ActiveAttr::Attributes
|
20
|
+
|
21
|
+
attribute :first_name
|
22
|
+
attribute :last_name
|
23
|
+
end
|
24
|
+
|
25
|
+
p = Person.new
|
26
|
+
p.first_name = "Chris"
|
27
|
+
p.last_name = "Griego"
|
28
|
+
p.attributes #=> {"first_name"=>"Chris", "last_name"=>"Griego"}
|
29
|
+
|
30
|
+
## BasicModel ##
|
31
|
+
|
32
|
+
Including the BasicModel module into your class gives you the bare minimum
|
33
|
+
required for your model to meet the ActiveModel API requirements.
|
34
|
+
|
35
|
+
class Person
|
36
|
+
include ActiveAttr::BasicModel
|
37
|
+
end
|
38
|
+
|
39
|
+
Person.model_name.plural #=> "people"
|
40
|
+
p = Person.new
|
41
|
+
p.valid? #=> true
|
42
|
+
p.errors.full_messages #=> []
|
43
|
+
|
44
|
+
## MassAssignment ##
|
45
|
+
|
46
|
+
Including the MassAssignment module into your class gives you methods for bulk
|
47
|
+
initializing and updating the attributes of your model. Any unknown attributes
|
48
|
+
are silently ignored unless you substitute the StrictMassAssignment module
|
49
|
+
which will raise an exception if an attempt is made to assign an unknown
|
50
|
+
attribute.
|
51
|
+
|
52
|
+
class Person
|
53
|
+
include ActiveAttr::MassAssignment
|
54
|
+
end
|
55
|
+
|
56
|
+
p = Person.new(:first_name => "Chris")
|
57
|
+
p.attributes = { :last_name => "Griego" }
|
58
|
+
p.first_name #=> "Chris"
|
59
|
+
p.last_name #=> "Griego"
|
data/active_attr.gemspec
CHANGED
@@ -5,8 +5,8 @@ require "active_attr/version"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "active_attr"
|
7
7
|
s.version = ActiveAttr::VERSION
|
8
|
-
s.authors = ["Chris Griego"]
|
9
|
-
s.email = ["cgriego@gmail.com"]
|
8
|
+
s.authors = ["Chris Griego", "Ben Poweski"]
|
9
|
+
s.email = ["cgriego@gmail.com", "bpoweski@gmail.com"]
|
10
10
|
s.homepage = "https://github.com/cgriego/active_attr"
|
11
11
|
s.summary = %q{What ActiveModel left out}
|
12
12
|
s.description = %q{Create plain old ruby models without reinventing the wheel.}
|
data/lib/active_attr.rb
CHANGED
@@ -1,4 +1,19 @@
|
|
1
|
-
require "
|
1
|
+
require "active_support/dependencies/autoload"
|
2
2
|
|
3
|
+
# ActiveAttr is a set of modules to enhance Plain Old Ruby Objects (POROs)
|
4
|
+
#
|
5
|
+
# These modules give your objects the type of features that are normally found
|
6
|
+
# in popular Object Relation Mappers (ORMs) like ActiveRecord, DataMapper, and
|
7
|
+
# Mongoid. The goal is to lower the bar for creating easy-to-use Ruby models.
|
3
8
|
module ActiveAttr
|
9
|
+
extend ActiveSupport::Autoload
|
10
|
+
|
11
|
+
autoload :AttributeDefinition
|
12
|
+
autoload :Attributes
|
13
|
+
autoload :BasicModel
|
14
|
+
autoload :Error
|
15
|
+
autoload :MassAssignment
|
16
|
+
autoload :StrictMassAssignment
|
17
|
+
autoload :UnknownAttributesError
|
18
|
+
autoload :VERSION
|
4
19
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ActiveAttr
|
2
|
+
# Represents an attribute for reflection
|
3
|
+
#
|
4
|
+
# @example Usage
|
5
|
+
# AttributeDefinition.new(:amount)
|
6
|
+
#
|
7
|
+
# @since 0.2.0
|
8
|
+
class AttributeDefinition
|
9
|
+
# The attribute name
|
10
|
+
# @since 0.2.0
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# Performs equality checking of the attribute definition
|
14
|
+
#
|
15
|
+
# @example Compare for equality.
|
16
|
+
# attribute_definition == other
|
17
|
+
#
|
18
|
+
# @param [AttributeDefinition, Object] other The other attribute definition to compare with.
|
19
|
+
#
|
20
|
+
# @return [true, false] True if attribute name is equal and other is instance of the same Class, false if not.
|
21
|
+
#
|
22
|
+
# @since 0.2.0
|
23
|
+
def ==(attribute)
|
24
|
+
return false unless attribute.instance_of? self.class
|
25
|
+
name == attribute.name
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a new AttributeDefinition
|
29
|
+
#
|
30
|
+
# @example Create an attribute defintion
|
31
|
+
# AttributeDefinition.new(:amount)
|
32
|
+
#
|
33
|
+
# @param [Symbol, String, #to_sym] attribute name
|
34
|
+
#
|
35
|
+
# @return [AttributeDefinition]
|
36
|
+
#
|
37
|
+
# @since 0.2.0
|
38
|
+
def initialize(name, options={})
|
39
|
+
raise TypeError, "can't convert #{name.class} into Symbol" unless name.respond_to? :to_sym
|
40
|
+
@name = name.to_sym
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the attribute name
|
44
|
+
#
|
45
|
+
# @return [String] the attribute name
|
46
|
+
#
|
47
|
+
# @since 0.2.0
|
48
|
+
def to_s
|
49
|
+
name.to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require "active_attr/attribute_definition"
|
2
|
+
require "active_support/concern"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
# Attributes provides a set of class methods for defining an attributes
|
6
|
+
# schema and instance methods for reading and writing attributes.
|
7
|
+
#
|
8
|
+
# @example Usage
|
9
|
+
# class Person
|
10
|
+
# include ActiveAttr::Attributes
|
11
|
+
# attribute :name
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# person = Person.new
|
15
|
+
# person.name = "Ben Poweski"
|
16
|
+
#
|
17
|
+
# @since 0.2.0
|
18
|
+
module Attributes
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
|
21
|
+
# Performs equality checking on the result of attributes and its type.
|
22
|
+
#
|
23
|
+
# @example Compare for equality.
|
24
|
+
# model == other
|
25
|
+
#
|
26
|
+
# @param [Attributes, Object] other The other model to compare with.
|
27
|
+
#
|
28
|
+
# @return [true, false] True if attributes are equal and other is instance of the same Class, false if not.
|
29
|
+
#
|
30
|
+
# @since 0.2.0
|
31
|
+
def ==(other)
|
32
|
+
return false unless other.instance_of? self.class
|
33
|
+
attributes == other.attributes
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the raw attributes Hash
|
37
|
+
#
|
38
|
+
# @example Get attributes
|
39
|
+
# person.attributes # => {"name"=>"Ben Poweski"}
|
40
|
+
#
|
41
|
+
# @return [Hash] The Hash of attributes
|
42
|
+
#
|
43
|
+
# @since 0.2.0
|
44
|
+
def attributes
|
45
|
+
@attributes ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the class name plus its attributes
|
49
|
+
#
|
50
|
+
# @example Inspect the model.
|
51
|
+
# person.inspect
|
52
|
+
#
|
53
|
+
# @return [String] A nice pretty string to look at.
|
54
|
+
#
|
55
|
+
# @since 0.2.0
|
56
|
+
def inspect
|
57
|
+
attribute_descriptions = self.class.attributes.map do |attribute|
|
58
|
+
"#{attribute.name.to_s}: #{read_attribute(attribute.name).inspect}"
|
59
|
+
end
|
60
|
+
|
61
|
+
"#<#{self.class.name} #{attribute_descriptions.join(", ")}>"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Read a value from the model's attributes. If the value does not exist
|
65
|
+
# it will return nil.
|
66
|
+
#
|
67
|
+
# @example Read an attribute.
|
68
|
+
# person.read_attribute(:name)
|
69
|
+
#
|
70
|
+
# @param [String, Symbol] name The name of the attribute to get.
|
71
|
+
#
|
72
|
+
# @return [Object] The value of the attribute.
|
73
|
+
#
|
74
|
+
# @since 0.2.0
|
75
|
+
def read_attribute(name)
|
76
|
+
attributes[name.to_s]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Write a single attribute to the model's attribute hash.
|
80
|
+
#
|
81
|
+
# @example Write the attribute.
|
82
|
+
# person.write_attribute(:name, "Benjamin")
|
83
|
+
#
|
84
|
+
# @param [String, Symbol] name The name of the attribute to update.
|
85
|
+
# @param [Object] value The value to set for the attribute.
|
86
|
+
#
|
87
|
+
# @since 0.2.0
|
88
|
+
def write_attribute(name, value)
|
89
|
+
attributes[name.to_s] = value
|
90
|
+
end
|
91
|
+
|
92
|
+
module ClassMethods
|
93
|
+
# Defines all the attributes that are to be returned from the attributes instance method.
|
94
|
+
# For each attribute that is defined, a getter and setter will be
|
95
|
+
# added as an instance method to the model. An AttributeDefinition instance will be
|
96
|
+
# added to result of the attributes class method.
|
97
|
+
#
|
98
|
+
# @example Define an attribute.
|
99
|
+
# attribute :name
|
100
|
+
#
|
101
|
+
# @param [Symbol] name The name of the attribute.
|
102
|
+
#
|
103
|
+
# @since 0.2.0
|
104
|
+
def attribute(name, options={})
|
105
|
+
attribute_definition = AttributeDefinition.new(name, options)
|
106
|
+
attributes << attribute_definition
|
107
|
+
method_name = attribute_definition.name
|
108
|
+
|
109
|
+
define_method("#{method_name}=") { |value| write_attribute(name, value) }
|
110
|
+
define_method(method_name) { read_attribute(name) }
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns an Array of AttributeDefinition instances
|
114
|
+
#
|
115
|
+
# @example Get attribute definitions
|
116
|
+
# Person.attributes
|
117
|
+
#
|
118
|
+
# @return [Array<AttributeDefinition>] The Array of AttributeDefinition instances
|
119
|
+
#
|
120
|
+
# @since 0.2.0
|
121
|
+
def attributes
|
122
|
+
@attributes ||= []
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns the class name plus its attribute definitions
|
126
|
+
#
|
127
|
+
# @example Inspect the model's definition.
|
128
|
+
# Person.inspect
|
129
|
+
#
|
130
|
+
# @return [String] A nice pretty string to look at.
|
131
|
+
#
|
132
|
+
# @since 0.2.0
|
133
|
+
def inspect
|
134
|
+
"#{self.name}(#{attributes.map { |a| a.to_s }.join(", ")})"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "active_model"
|
2
|
+
require "active_support/concern"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
# Provides the minimum functionality to pass the ActiveModel lint tests
|
6
|
+
#
|
7
|
+
# @example Usage
|
8
|
+
# class Person
|
9
|
+
# include ActiveAttr::BasicModel
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# @since 0.2.0
|
13
|
+
module BasicModel
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
extend ActiveModel::Naming
|
16
|
+
include ActiveModel::Conversion
|
17
|
+
include ActiveModel::Validations
|
18
|
+
|
19
|
+
# @return [false]
|
20
|
+
# @since 0.2.0
|
21
|
+
def persisted?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActiveAttr
|
2
|
+
# All exceptions defined by and raised directly by ActiveAttr include this
|
3
|
+
# module, if you want to rescue any of the exceptions raised by the
|
4
|
+
# ActiveAttr library, you can rescue {ActiveAttr::Error}
|
5
|
+
#
|
6
|
+
# @example Rescuing an ActiveAttr error
|
7
|
+
# begin
|
8
|
+
# Person.new(attributes)
|
9
|
+
# rescue ActiveAttr::Error
|
10
|
+
# Person.new
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# @since 0.2.0
|
14
|
+
module Error
|
15
|
+
end
|
16
|
+
end
|
@@ -1,18 +1,59 @@
|
|
1
1
|
module ActiveAttr
|
2
|
+
# MassAssignment allows you to bulk set and update attributes
|
3
|
+
#
|
4
|
+
# Including MassAssignment into your model gives it a set of mass assignment
|
5
|
+
# methods, similar to those found in ActiveRecord.
|
6
|
+
#
|
7
|
+
# @example Usage
|
8
|
+
# class Person
|
9
|
+
# include ActiveAttr::MassAssignment
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# @since 0.1.0
|
2
13
|
module MassAssignment
|
3
|
-
|
4
|
-
|
14
|
+
# Mass update a model's attributes
|
15
|
+
#
|
16
|
+
# @example Assigning a hash
|
17
|
+
# person.assign_attributes(:first_name => "Chris", :last_name => "Griego")
|
18
|
+
# person.first_name #=> "Chris"
|
19
|
+
# person.last_name #=> "Griego"
|
20
|
+
#
|
21
|
+
# @param [Hash, #each] attributes Attributes used to populate the model
|
22
|
+
#
|
23
|
+
# @since 0.1.0
|
24
|
+
def assign_attributes(new_attributes, options={})
|
25
|
+
new_attributes.each do |name, value|
|
26
|
+
writer = "#{name}="
|
27
|
+
send writer, value if respond_to? writer
|
28
|
+
end if new_attributes
|
5
29
|
end
|
6
30
|
|
31
|
+
# Mass update a model's attributes
|
32
|
+
#
|
33
|
+
# @example Assigning a hash
|
34
|
+
# person.attributes = { :first_name => "Chris", :last_name => "Griego" }
|
35
|
+
# person.first_name #=> "Chris"
|
36
|
+
# person.last_name #=> "Griego"
|
37
|
+
#
|
38
|
+
# @param (see #assign_attributes)
|
39
|
+
#
|
40
|
+
# @since 0.1.0
|
7
41
|
def attributes=(new_attributes)
|
8
42
|
assign_attributes new_attributes
|
9
43
|
end
|
10
44
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
45
|
+
# Initialize a model with a set of attributes
|
46
|
+
#
|
47
|
+
# @example Initializing with a hash
|
48
|
+
# person = Person.new(:first_name => "Chris", :last_name => "Griego")
|
49
|
+
# person.first_name #=> "Chris"
|
50
|
+
# person.last_name #=> "Griego"
|
51
|
+
#
|
52
|
+
# @param (see #assign_attributes)
|
53
|
+
#
|
54
|
+
# @since 0.1.0
|
55
|
+
def initialize(attributes=nil, options={})
|
56
|
+
assign_attributes attributes, options
|
16
57
|
end
|
17
58
|
end
|
18
59
|
end
|