active_attr 0.5.0.alpha2 → 0.5.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/.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
|