active_attr 0.5.0.alpha2 → 0.5.0
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/.rvmrc +29 -3
- data/.travis.yml +1 -0
- data/CHANGELOG.md +14 -1
- data/Gemfile +2 -1
- data/README.md +21 -14
- data/lib/active_attr/chainable_initialization.rb +2 -2
- data/lib/active_attr/matchers/have_attribute_matcher.rb +84 -22
- data/lib/active_attr/model.rb +2 -0
- data/lib/active_attr/query_attributes.rb +1 -2
- data/lib/active_attr/typecasted_attributes.rb +22 -6
- data/lib/active_attr/typecasting.rb +19 -7
- data/lib/active_attr/typecasting/big_decimal_typecaster.rb +36 -0
- data/lib/active_attr/typecasting/boolean.rb +2 -0
- data/lib/active_attr/typecasting/boolean_typecaster.rb +26 -4
- data/lib/active_attr/typecasting/date_time_typecaster.rb +19 -2
- data/lib/active_attr/typecasting/date_typecaster.rb +18 -2
- data/lib/active_attr/typecasting/float_typecaster.rb +18 -2
- data/lib/active_attr/typecasting/integer_typecaster.rb +19 -2
- data/lib/active_attr/typecasting/object_typecaster.rb +16 -0
- data/lib/active_attr/typecasting/string_typecaster.rb +18 -2
- data/lib/active_attr/version.rb +1 -1
- data/spec/functional/active_attr/matchers/have_attribute_matcher_spec.rb +364 -0
- data/spec/functional/active_attr/model_spec.rb +6 -0
- data/spec/functional/active_attr/typecasted_attributes_spec.rb +18 -8
- data/spec/unit/active_attr/matchers/have_attribute_matcher_spec.rb +19 -144
- data/spec/unit/active_attr/matchers_spec.rb +23 -0
- data/spec/unit/active_attr/typecasting/big_decimal_typecaster_spec.rb +39 -0
- data/spec/unit/active_attr/typecasting/date_time_typecaster_spec.rb +3 -2
- data/spec/unit/active_attr/typecasting_spec.rb +5 -0
- metadata +30 -20
data/.rvmrc
CHANGED
@@ -6,10 +6,25 @@
|
|
6
6
|
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
|
7
7
|
environment_id="ruby-1.9.2-p290@active_attr"
|
8
8
|
|
9
|
+
#
|
10
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
11
|
+
#
|
12
|
+
# rvmrc_rvm_version="1.10.1" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
|
9
19
|
#
|
10
20
|
# Uncomment following line if you want options to be set only for given project.
|
11
21
|
#
|
12
22
|
# PROJECT_JRUBY_OPTS=( --1.9 )
|
23
|
+
#
|
24
|
+
# The variable PROJECT_JRUBY_OPTS requires the following to be run in shell:
|
25
|
+
#
|
26
|
+
# chmod +x ${rvm_path}/hooks/after_use_jruby_opts
|
27
|
+
#
|
13
28
|
|
14
29
|
#
|
15
30
|
# First we attempt to load the desired environment directly from the environment
|
@@ -31,7 +46,7 @@ else
|
|
31
46
|
if ! rvm --create use "$environment_id"
|
32
47
|
then
|
33
48
|
echo "Failed to create RVM environment '${environment_id}'."
|
34
|
-
|
49
|
+
return 1
|
35
50
|
fi
|
36
51
|
fi
|
37
52
|
|
@@ -47,9 +62,20 @@ fi
|
|
47
62
|
# fi
|
48
63
|
|
49
64
|
# If you use bundler, this might be useful to you:
|
50
|
-
# if
|
65
|
+
# if [[ -s Gemfile ]] && ! command -v bundle >/dev/null
|
66
|
+
# then
|
67
|
+
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
68
|
+
# gem install bundler
|
69
|
+
# fi
|
70
|
+
# if [[ -s Gemfile ]] && command -v bundle
|
51
71
|
# then
|
52
|
-
# bundle
|
72
|
+
# bundle install
|
53
73
|
# fi
|
54
74
|
|
75
|
+
if [[ $- == *i* ]] # check for interactive shells
|
76
|
+
then
|
77
|
+
echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
|
78
|
+
else
|
79
|
+
echo "Using: $GEM_HOME" # don't use colors in interactive shells
|
80
|
+
fi
|
55
81
|
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,25 @@
|
|
1
|
-
# ActiveAttr 0.5.0 (
|
1
|
+
# ActiveAttr 0.5.0 (March 11, 2012) #
|
2
2
|
|
3
|
+
* ActiveAttr is now Ruby warning free
|
3
4
|
* Added AttributeDefaults
|
4
5
|
* Added AttributeDefinition#[]
|
5
6
|
* Added Attributes.attribute_names
|
7
|
+
* Added Matchers::HaveAttributeMatcher#of_type
|
6
8
|
* Added Matchers::HaveAttributeMatcher#with_default_value_of
|
7
9
|
* Added TypecastedAttributes
|
8
10
|
* Added Typecasting
|
11
|
+
* Added Typecasting::BigDecimalTypecaster
|
12
|
+
* Added Typecasting::Boolean
|
13
|
+
* Added Typecasting::BooleanTypecaster
|
14
|
+
* Added Typecasting::DateTimeTypecaster
|
15
|
+
* Added Typecasting::DateTypecaster
|
16
|
+
* Added Typecasting::FloatTypecaster
|
17
|
+
* Added Typecasting::IntegerTypecaster
|
18
|
+
* Added Typecasting::ObjectTypecaster
|
19
|
+
* Added Typecasting::StringTypecaster
|
9
20
|
* Changed Attributes.attributes return value from an Array to a Hash
|
21
|
+
* Changed HaveAttributeMatcher to return spec failures when the model is
|
22
|
+
missing ActiveAttr modules
|
10
23
|
* Changed redefining an attribute to actually redefine the attribute
|
11
24
|
* Removed StrictMassAssignment, instead use MassAssignmentSecurity with
|
12
25
|
ActiveModel v3.2 which allows assigning mass_assignment_sanitizer to
|
data/Gemfile
CHANGED
@@ -9,9 +9,10 @@ group :development do
|
|
9
9
|
gem "guard-rspec"
|
10
10
|
gem "rb-fsevent"
|
11
11
|
gem "rdiscount"
|
12
|
-
gem "rdoc"
|
12
|
+
gem "rdoc"
|
13
13
|
gem "ruby-debug", :platforms => :mri_18
|
14
14
|
gem "ruby-debug19", :platforms => :mri_19 if RUBY_VERSION < "1.9.3"
|
15
15
|
gem "spec_coverage", :platforms => :mri_19
|
16
|
+
gem "travis-lint"
|
16
17
|
gem "yard"
|
17
18
|
end
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# ActiveAttr #
|
2
2
|
|
3
|
-
[![Build History][2]][1]
|
3
|
+
[![Build History][2]][1] [![Dependency Status][5]][4]
|
4
4
|
|
5
5
|
ActiveAttr is a set of modules that makes it easy to create plain old ruby
|
6
6
|
models with functionality found in ORMs, like ActiveRecord, without
|
@@ -14,19 +14,11 @@ ActiveAttr is distributed as a rubygem [on rubygems.org][3].
|
|
14
14
|
[1]: http://travis-ci.org/cgriego/active_attr
|
15
15
|
[2]: https://secure.travis-ci.org/cgriego/active_attr.png?branch=master
|
16
16
|
[3]: http://rubygems.org/gems/active_attr
|
17
|
+
[4]: https://gemnasium.com/cgriego/active_attr
|
18
|
+
[5]: https://gemnasium.com/cgriego/active_attr.png
|
17
19
|
|
18
20
|
## Modules ##
|
19
21
|
|
20
|
-
### Model ###
|
21
|
-
|
22
|
-
The Model module is a shortcut for incorporating the most common model
|
23
|
-
functionality into your model with one include. All of the following modules
|
24
|
-
are included when you include Model.
|
25
|
-
|
26
|
-
class Person
|
27
|
-
include ActiveAttr::Model
|
28
|
-
end
|
29
|
-
|
30
22
|
### Attributes ###
|
31
23
|
|
32
24
|
Including the Attributes module into your class gives you a DSL for defining
|
@@ -79,7 +71,8 @@ providing instance methods for querying your attributes.
|
|
79
71
|
|
80
72
|
#### TypecastedAttributes ####
|
81
73
|
|
82
|
-
|
74
|
+
Including the TypecastedAttributes module into your class builds on Attributes
|
75
|
+
by providing type conversion for your attributes.
|
83
76
|
|
84
77
|
class Person
|
85
78
|
include ActiveAttr::TypecastedAttributes
|
@@ -166,7 +159,7 @@ MassAssignment methods to honor any declared mass assignment permission
|
|
166
159
|
blacklists or whitelists including support for mass assignment roles.
|
167
160
|
|
168
161
|
class Person
|
169
|
-
include ActiveAttr::
|
162
|
+
include ActiveAttr::MassAssignmentSecurity
|
170
163
|
attr_accessor :first_name, :last_name
|
171
164
|
attr_protected :last_name
|
172
165
|
end
|
@@ -175,6 +168,16 @@ blacklists or whitelists including support for mass assignment roles.
|
|
175
168
|
person.first_name #=> "Chris"
|
176
169
|
person.last_name #=> nil
|
177
170
|
|
171
|
+
### Model ###
|
172
|
+
|
173
|
+
The Model module is a shortcut for incorporating the most common model
|
174
|
+
functionality into your model with one include. All of the above modules
|
175
|
+
are included when you include Model.
|
176
|
+
|
177
|
+
class Person
|
178
|
+
include ActiveAttr::Model
|
179
|
+
end
|
180
|
+
|
178
181
|
## Integrations ##
|
179
182
|
|
180
183
|
### Ruby on Rails ###
|
@@ -193,5 +196,9 @@ your models. The matchers also work with compatible frameworks like Shoulda.
|
|
193
196
|
require "active_attr/rspec"
|
194
197
|
|
195
198
|
describe Person do
|
196
|
-
it
|
199
|
+
it do
|
200
|
+
should have_attribute(:first_name).
|
201
|
+
of_type(String).
|
202
|
+
with_default_value_of("John")
|
203
|
+
end
|
197
204
|
end
|
@@ -4,8 +4,8 @@ module ActiveAttr
|
|
4
4
|
# Many ActiveAttr modules enhance the behavior of the \#initialize method,
|
5
5
|
# and in doing so, these methods need to accept arguments. However, Ruby's
|
6
6
|
# Object and BasicObject classes, in most versions of Ruby, do not allow any
|
7
|
-
# arguments to be passed in. This module halts the
|
8
|
-
# initialization arguments before invoking the Object
|
7
|
+
# arguments to be passed in. This module halts the propagation of
|
8
|
+
# initialization arguments before invoking the Object class'
|
9
9
|
# initialization.
|
10
10
|
#
|
11
11
|
# In order to still allow a subclass mixing in this module (directly or
|
@@ -25,34 +25,24 @@ module ActiveAttr
|
|
25
25
|
attr_reader :attribute_name, :default_value
|
26
26
|
private :attribute_name, :default_value
|
27
27
|
|
28
|
-
# Specify that the attribute should have the given default value
|
29
|
-
#
|
30
|
-
# @example Person's first name should default to John
|
31
|
-
# describe Person do
|
32
|
-
# it do
|
33
|
-
# should have_attribute(:first_name).with_default_value_of("John")
|
34
|
-
# end
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
# @param [Object]
|
38
|
-
#
|
39
|
-
# @since 0.5.0
|
40
|
-
def with_default_value_of(default_value)
|
41
|
-
@default_value_set = true
|
42
|
-
@default_value = default_value
|
43
|
-
self
|
44
|
-
end
|
45
|
-
|
46
28
|
# @return [String] Description
|
47
29
|
# @private
|
48
30
|
def description
|
49
|
-
"
|
31
|
+
"has #{attribute_description}"
|
50
32
|
end
|
51
33
|
|
52
34
|
# @return [String] Failure message
|
53
35
|
# @private
|
54
36
|
def failure_message
|
55
|
-
|
37
|
+
if !includes_attributes?
|
38
|
+
"expected #{@model_class.name} to include ActiveAttr::Attributes"
|
39
|
+
elsif !includes_defaults?
|
40
|
+
"expected #{@model_class.name} to include ActiveAttr::AttributeDefaults"
|
41
|
+
elsif !includes_typecasting?
|
42
|
+
"expected #{@model_class.name} to include ActiveAttr::TypecastedAttributes"
|
43
|
+
else
|
44
|
+
"expected #{@model_class.name} to have #{attribute_description}"
|
45
|
+
end
|
56
46
|
end
|
57
47
|
|
58
48
|
# @param [Symbol, String, #to_sym] attribute_name
|
@@ -61,27 +51,99 @@ module ActiveAttr
|
|
61
51
|
raise TypeError, "can't convert #{attribute_name.class} into Symbol" unless attribute_name.respond_to? :to_sym
|
62
52
|
@attribute_name = attribute_name.to_sym
|
63
53
|
@default_value_set = false
|
54
|
+
@type = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# Specify that the attribute should have the given type
|
58
|
+
#
|
59
|
+
# @example Person's first name should be a String
|
60
|
+
# describe Person do
|
61
|
+
# it { should have_attribute(:first_name).of_type(String) }
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# @param [Class] type The expected type
|
65
|
+
#
|
66
|
+
# @return [HaveAttributeMatcher] The matcher
|
67
|
+
#
|
68
|
+
# @since 0.5.0
|
69
|
+
def of_type(type)
|
70
|
+
@type = type
|
71
|
+
self
|
64
72
|
end
|
65
73
|
|
66
74
|
# @private
|
67
75
|
def matches?(model_or_model_class)
|
68
76
|
@model_class = class_from(model_or_model_class)
|
77
|
+
|
78
|
+
return false if !includes_attributes? || !includes_defaults? || !includes_typecasting?
|
79
|
+
|
69
80
|
@attribute_definition = @model_class.attributes[attribute_name]
|
70
81
|
|
71
|
-
!!(@attribute_definition &&
|
82
|
+
!!(@attribute_definition && default_matches? && type_matches?)
|
72
83
|
end
|
73
84
|
|
74
85
|
# @return [String] Negative failure message
|
75
86
|
# @private
|
76
87
|
def negative_failure_message
|
77
|
-
"
|
88
|
+
"expected #{@model_class.name} to not have #{attribute_description}"
|
89
|
+
end
|
90
|
+
|
91
|
+
# Specify that the attribute should have the given default value
|
92
|
+
#
|
93
|
+
# @example Person's first name should default to John
|
94
|
+
# describe Person do
|
95
|
+
# it do
|
96
|
+
# should have_attribute(:first_name).with_default_value_of("John")
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# @param [Object] default_value The expected default value
|
101
|
+
#
|
102
|
+
# @return [HaveAttributeMatcher] The matcher
|
103
|
+
#
|
104
|
+
# @since 0.5.0
|
105
|
+
def with_default_value_of(default_value)
|
106
|
+
@default_value_set = true
|
107
|
+
@default_value = default_value
|
108
|
+
self
|
78
109
|
end
|
79
110
|
|
80
111
|
private
|
81
112
|
|
113
|
+
def attribute_description
|
114
|
+
"attribute named #{attribute_name}".tap do |result|
|
115
|
+
result << " of type #{@type}" if @type
|
116
|
+
result << " with a default value of #{default_value.inspect}" if @default_value_set
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def includes_attributes?
|
121
|
+
model_ancestor_names.include?("ActiveAttr::Attributes")
|
122
|
+
end
|
123
|
+
|
124
|
+
def includes_defaults?
|
125
|
+
!@default_value_set || model_ancestor_names.include?("ActiveAttr::AttributeDefaults")
|
126
|
+
end
|
127
|
+
|
128
|
+
def includes_typecasting?
|
129
|
+
!@type || model_ancestor_names.include?("ActiveAttr::TypecastedAttributes")
|
130
|
+
end
|
131
|
+
|
132
|
+
def model_ancestor_names
|
133
|
+
@model_class.ancestors.map(&:name)
|
134
|
+
end
|
135
|
+
|
82
136
|
def class_from(object)
|
83
137
|
Class === object ? object : object.class
|
84
138
|
end
|
139
|
+
|
140
|
+
def default_matches?
|
141
|
+
!@default_value_set || @attribute_definition[:default] == default_value
|
142
|
+
end
|
143
|
+
|
144
|
+
def type_matches?
|
145
|
+
!@type || @model_class._attribute_type(attribute_name) == @type
|
146
|
+
end
|
85
147
|
end
|
86
148
|
end
|
87
149
|
end
|
data/lib/active_attr/model.rb
CHANGED
@@ -4,6 +4,7 @@ require "active_attr/block_initialization"
|
|
4
4
|
require "active_attr/logger"
|
5
5
|
require "active_attr/mass_assignment_security"
|
6
6
|
require "active_attr/query_attributes"
|
7
|
+
require "active_attr/typecasted_attributes"
|
7
8
|
require "active_model"
|
8
9
|
require "active_support/concern"
|
9
10
|
|
@@ -27,6 +28,7 @@ module ActiveAttr
|
|
27
28
|
include MassAssignmentSecurity
|
28
29
|
include AttributeDefaults
|
29
30
|
include QueryAttributes
|
31
|
+
include TypecastedAttributes
|
30
32
|
|
31
33
|
if defined? ActiveModel::Serializable
|
32
34
|
include ActiveModel::Serializable::JSON
|
@@ -29,8 +29,7 @@ module ActiveAttr
|
|
29
29
|
|
30
30
|
# Test the presence of an attribute
|
31
31
|
#
|
32
|
-
#
|
33
|
-
# is a string that represents false, the method returns false.
|
32
|
+
# See {Typecasting::BooleanTypecaster#call} for more details.
|
34
33
|
#
|
35
34
|
# @example Query an attribute
|
36
35
|
# person.query_attribute(:name)
|
@@ -3,20 +3,26 @@ require "active_attr/typecasting"
|
|
3
3
|
require "active_support/concern"
|
4
4
|
|
5
5
|
module ActiveAttr
|
6
|
-
# TypecastedAttributes
|
6
|
+
# TypecastedAttributes allows types to be declared for your attributes
|
7
|
+
#
|
8
|
+
# Types are declared by passing the :type option to the attribute class
|
9
|
+
# method. After a type is declared, attribute readers will convert any
|
10
|
+
# assigned attribute value to the declared type. If the assigned value
|
11
|
+
# cannot be cast, nil will be returned instead. You can access the original
|
12
|
+
# assigned value using the before_type_cast methods.
|
13
|
+
#
|
14
|
+
# See {Typecasting} for the currently supported types.
|
7
15
|
#
|
8
16
|
# @example Usage
|
9
17
|
# class Person
|
10
18
|
# include ActiveAttr::TypecastedAttributes
|
11
|
-
# attribute :name
|
12
19
|
# attribute :age, :type => Integer
|
13
20
|
# end
|
14
21
|
#
|
15
22
|
# person = Person.new
|
16
|
-
# person.
|
17
|
-
# person.age = "29"
|
18
|
-
#
|
23
|
+
# person.age = "29"
|
19
24
|
# person.age #=> 29
|
25
|
+
# person.age_before_type_cast #=> "29"
|
20
26
|
#
|
21
27
|
# @since 0.5.0
|
22
28
|
module TypecastedAttributes
|
@@ -28,7 +34,17 @@ module ActiveAttr
|
|
28
34
|
attribute_method_suffix "_before_type_cast"
|
29
35
|
end
|
30
36
|
|
31
|
-
#
|
37
|
+
# Read the raw attribute value
|
38
|
+
#
|
39
|
+
# @example Reading a raw age value
|
40
|
+
# person.age = "29"
|
41
|
+
# person.attribute_before_type_cast(:age) #=> "29"
|
42
|
+
#
|
43
|
+
# @param [String, Symbol, #to_s] name Attribute name
|
44
|
+
#
|
45
|
+
# @return [Object, nil] The attribute value before typecasting
|
46
|
+
#
|
47
|
+
# @since 0.5.0
|
32
48
|
def attribute_before_type_cast(name)
|
33
49
|
@attributes[name.to_s]
|
34
50
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "active_attr/typecasting/big_decimal_typecaster"
|
1
2
|
require "active_attr/typecasting/boolean"
|
2
3
|
require "active_attr/typecasting/boolean_typecaster"
|
3
4
|
require "active_attr/typecasting/date_time_typecaster"
|
@@ -11,19 +12,30 @@ require "active_support/concern"
|
|
11
12
|
module ActiveAttr
|
12
13
|
# Typecasting provides methods to typecast a value to a different type
|
13
14
|
#
|
15
|
+
# The following types are supported for typecasting:
|
16
|
+
# * BigDecimal
|
17
|
+
# * Boolean
|
18
|
+
# * Date
|
19
|
+
# * DateTime
|
20
|
+
# * Float
|
21
|
+
# * Integer
|
22
|
+
# * Object
|
23
|
+
# * String
|
24
|
+
#
|
14
25
|
# @since 0.5.0
|
15
26
|
module Typecasting
|
16
27
|
extend ActiveSupport::Concern
|
17
28
|
|
18
29
|
# @private
|
19
30
|
TYPECASTERS = {
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
31
|
+
BigDecimal => BigDecimalTypecaster,
|
32
|
+
Boolean => BooleanTypecaster,
|
33
|
+
Date => DateTypecaster,
|
34
|
+
DateTime => DateTimeTypecaster,
|
35
|
+
Float => FloatTypecaster,
|
36
|
+
Integer => IntegerTypecaster,
|
37
|
+
Object => ObjectTypecaster,
|
38
|
+
String => StringTypecaster,
|
27
39
|
}
|
28
40
|
|
29
41
|
# Typecasts a value using a Class
|