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.

Files changed (30) hide show
  1. data/.rvmrc +29 -3
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +14 -1
  4. data/Gemfile +2 -1
  5. data/README.md +21 -14
  6. data/lib/active_attr/chainable_initialization.rb +2 -2
  7. data/lib/active_attr/matchers/have_attribute_matcher.rb +84 -22
  8. data/lib/active_attr/model.rb +2 -0
  9. data/lib/active_attr/query_attributes.rb +1 -2
  10. data/lib/active_attr/typecasted_attributes.rb +22 -6
  11. data/lib/active_attr/typecasting.rb +19 -7
  12. data/lib/active_attr/typecasting/big_decimal_typecaster.rb +36 -0
  13. data/lib/active_attr/typecasting/boolean.rb +2 -0
  14. data/lib/active_attr/typecasting/boolean_typecaster.rb +26 -4
  15. data/lib/active_attr/typecasting/date_time_typecaster.rb +19 -2
  16. data/lib/active_attr/typecasting/date_typecaster.rb +18 -2
  17. data/lib/active_attr/typecasting/float_typecaster.rb +18 -2
  18. data/lib/active_attr/typecasting/integer_typecaster.rb +19 -2
  19. data/lib/active_attr/typecasting/object_typecaster.rb +16 -0
  20. data/lib/active_attr/typecasting/string_typecaster.rb +18 -2
  21. data/lib/active_attr/version.rb +1 -1
  22. data/spec/functional/active_attr/matchers/have_attribute_matcher_spec.rb +364 -0
  23. data/spec/functional/active_attr/model_spec.rb +6 -0
  24. data/spec/functional/active_attr/typecasted_attributes_spec.rb +18 -8
  25. data/spec/unit/active_attr/matchers/have_attribute_matcher_spec.rb +19 -144
  26. data/spec/unit/active_attr/matchers_spec.rb +23 -0
  27. data/spec/unit/active_attr/typecasting/big_decimal_typecaster_spec.rb +39 -0
  28. data/spec/unit/active_attr/typecasting/date_time_typecaster_spec.rb +3 -2
  29. data/spec/unit/active_attr/typecasting_spec.rb +5 -0
  30. 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
- exit 1
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 command -v bundle && [[ -s Gemfile ]]
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
@@ -1,3 +1,4 @@
1
+ language: ruby
1
2
  rvm:
2
3
  - ree
3
4
  - 1.9.2
data/CHANGELOG.md CHANGED
@@ -1,12 +1,25 @@
1
- # ActiveAttr 0.5.0 (unreleased) #
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", "~> 3.9.4"
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
- TODO documentation
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::MassAssignment
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 { should have_attribute(:first_name).with_default_value_of("John") }
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 propgation of
8
- # initialization arguments before invoking the Object classes'
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
- "have attribute named #{attribute_name}#{ " with a default value of #{default_value.inspect}" if @default_value_set}"
31
+ "has #{attribute_description}"
50
32
  end
51
33
 
52
34
  # @return [String] Failure message
53
35
  # @private
54
36
  def failure_message
55
- "Expected #{@model_class} to #{description}"
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 && (!@default_value_set || @attribute_definition[:default] == default_value))
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
- "Expected #{@model_class} to not #{description}"
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
@@ -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
- # Similar to an ActiveRecord model, when the attribute is a zero value or
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 enhances attribute handling with typecasting
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.name = "Ben Poweski"
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
- # TODO Documentation
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
- Boolean => BooleanTypecaster,
21
- Date => DateTypecaster,
22
- DateTime => DateTimeTypecaster,
23
- Float => FloatTypecaster,
24
- Integer => IntegerTypecaster,
25
- Object => ObjectTypecaster,
26
- String => StringTypecaster,
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