active_attr 0.1.0 → 0.2.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.

@@ -0,0 +1,13 @@
1
+ require "active_attr/matchers/have_attribute_matcher"
2
+
3
+ module ActiveAttr
4
+ # Matchers that can be used with RSpec and Shoulda to declaritively verify
5
+ # models based on ActiveAttr modules
6
+ #
7
+ # @example Integrate with RSPec
8
+ # require "active_attr/rspec"
9
+ #
10
+ # @since 0.2.0
11
+ module Matchers
12
+ end
13
+ end
@@ -0,0 +1,69 @@
1
+ module ActiveAttr
2
+ module Matchers
3
+ # Specify that a model should have an attribute matching the criteria
4
+ #
5
+ # @example Person should have a name attribute
6
+ # describe Person do
7
+ # it { should have_attribute(:name) }
8
+ # end
9
+ #
10
+ # @param [Symbol, String, #to_sym] attribute_name
11
+ #
12
+ # @return [HaveAttributeMatcher]
13
+ #
14
+ # @since 0.2.0
15
+ def have_attribute(attribute_name)
16
+ HaveAttributeMatcher.new(attribute_name)
17
+ end
18
+
19
+ # Verifies that an ActiveAttr-based model has an attribute matching the
20
+ # given criteria
21
+ #
22
+ # @since 0.2.0
23
+ class HaveAttributeMatcher
24
+ # @return [Symbol]
25
+ # @api private
26
+ attr_reader :attribute_name
27
+
28
+ # @return [String] Description
29
+ # @api private
30
+ def description
31
+ "have attribute named #{attribute_name}"
32
+ end
33
+
34
+ # @return [String] Failure message
35
+ # @api private
36
+ def failure_message
37
+ "Expected #{@model_class} to #{description}"
38
+ end
39
+
40
+ # @param [Symbol, String, #to_sym] attribute_name
41
+ # @api private
42
+ def initialize(attribute_name)
43
+ raise TypeError, "can't convert #{attribute_name.class} into Symbol" unless attribute_name.respond_to? :to_sym
44
+ @attribute_name = attribute_name.to_sym
45
+ end
46
+
47
+ # @api private
48
+ def matches?(model_or_model_class)
49
+ @model_class = class_from(model_or_model_class)
50
+
51
+ @model_class.attributes.any? do |attribute|
52
+ attribute.name == attribute_name
53
+ end
54
+ end
55
+
56
+ # @return [String] Negative failure message
57
+ # @api private
58
+ def negative_failure_message
59
+ "Expected #{@model_class} to not #{description}"
60
+ end
61
+
62
+ private
63
+
64
+ def class_from(object)
65
+ Class === object ? object : object.class
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ require "active_attr/matchers"
2
+
3
+ RSpec.configure do |config|
4
+ config.include ActiveAttr::Matchers
5
+ end
@@ -0,0 +1,44 @@
1
+ require "active_attr/mass_assignment"
2
+ require "active_attr/unknown_attributes_error"
3
+ require "active_support/concern"
4
+
5
+ module ActiveAttr
6
+ # StrictMassAssignment allows mass assignment of attributes, but raises an
7
+ # exception when assigning unknown attributes
8
+ #
9
+ # Attempting to assign any unknown or private attribute through any of the
10
+ # mass assignment methods ({#assign_attributes}, {#attributes=}, and
11
+ # {#initialize}) will raise an {ActiveAttr::UnknownAttributesError}
12
+ # exception.
13
+ #
14
+ # @example Usage
15
+ # class Person
16
+ # include ActiveAttr::StrictMassAssignment
17
+ # end
18
+ #
19
+ # @since 0.2.0
20
+ module StrictMassAssignment
21
+ extend ActiveSupport::Concern
22
+ include MassAssignment
23
+
24
+ # Mass update a model's attributes, but raise an error if an attempt is
25
+ # made to assign an unknown attribute
26
+ #
27
+ # @param (see MassAssignment#assign_attributes)
28
+ #
29
+ # @raise [ActiveAttr::UnknownAttributesError]
30
+ #
31
+ # @since 0.2.0
32
+ def assign_attributes(new_attributes, options={})
33
+ unknown_attribute_names = (new_attributes || {}).reject do |name, value|
34
+ respond_to? "#{name}="
35
+ end.map { |name, value| name.to_s }.sort
36
+
37
+ if unknown_attribute_names.any?
38
+ raise UnknownAttributesError, "unknown attribute(s): #{unknown_attribute_names.join(", ")}"
39
+ else
40
+ super
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ require "active_attr/error"
2
+
3
+ module ActiveAttr
4
+ # This exception is raised if attempting to mass assign unknown attributes
5
+ # when using {StrictMassAssignment}
6
+ #
7
+ # @example Rescuing an UnknownAttributesError error
8
+ # begin
9
+ # Person.new(attributes)
10
+ # rescue ActiveAttr::UnknownAttributesError
11
+ # Person.new
12
+ # end
13
+ #
14
+ # @since 0.2.0
15
+ class UnknownAttributesError < NoMethodError
16
+ include Error
17
+ end
18
+ end
@@ -1,3 +1,5 @@
1
1
  module ActiveAttr
2
- VERSION = "0.1.0"
2
+ # Complete version string
3
+ # @since 0.1.0
4
+ VERSION = "0.2.0"
3
5
  end
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+ require "active_attr/attribute_definition"
3
+
4
+ module ActiveAttr
5
+ describe AttributeDefinition do
6
+ subject { described_class.new(:amount) }
7
+
8
+ describe "#==" do
9
+ it "returns true when the attribute name is equal" do
10
+ described_class.new(:amount).should == described_class.new(:amount)
11
+ end
12
+
13
+ it "returns false when another object is compared" do
14
+ described_class.new(:amount).should_not == Struct.new(:name).new(:amount)
15
+ end
16
+ end
17
+
18
+ describe "#initialize" do
19
+ it "raises an ArgumentError when no arguments" do
20
+ expect { described_class.new }.to raise_error ArgumentError
21
+ end
22
+
23
+ it "assigns the first argument to name" do
24
+ described_class.new(:amount).name.should == :amount
25
+ end
26
+
27
+ it "converts a String attribute name to a Symbol" do
28
+ described_class.new('amount').name.should == :amount
29
+ end
30
+
31
+ it "raises a TypeError when the attribute name does not respond to #to_sym" do
32
+ expect { described_class.new(Object.new) }.to raise_error(TypeError, "can't convert Object into Symbol")
33
+ end
34
+ end
35
+
36
+ describe "#name" do
37
+ it { should respond_to(:name) }
38
+ end
39
+
40
+ describe "#to_s" do
41
+ it "renders the name" do
42
+ subject.to_s.should == subject.name.to_s
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,141 @@
1
+ require "spec_helper"
2
+ require "active_attr/attributes"
3
+
4
+ module ActiveAttr
5
+ describe Attributes do
6
+ let(:model_class) do
7
+ Class.new do
8
+ include Attributes
9
+ attribute :name
10
+ end
11
+ end
12
+
13
+ subject do
14
+ Class.new do
15
+ include Attributes
16
+ attribute :name
17
+ attribute :amount
18
+
19
+ def self.name
20
+ "Foo"
21
+ end
22
+ end
23
+ end
24
+
25
+ describe ".attribute" do
26
+ let(:instance) { subject.new }
27
+
28
+ it "creates an attribute with no options" do
29
+ subject.attributes.should include(AttributeDefinition.new(:name))
30
+ end
31
+
32
+ it "defined an attribute reader that calls #read_attribute" do
33
+ instance.should_receive(:read_attribute).with(:name)
34
+ instance.name
35
+ end
36
+
37
+ it "defines an attribute writer method that calls #write_attribute" do
38
+ instance.should_receive(:write_attribute).with(:name, "Ben")
39
+ instance.name = "Ben"
40
+ end
41
+ end
42
+
43
+ describe ".attributes" do
44
+ it { should respond_to(:attributes) }
45
+
46
+ context "when no attributes exist" do
47
+ subject do
48
+ Class.new { include Attributes }.attributes
49
+ end
50
+
51
+ it "returns an empty Array" do
52
+ should == []
53
+ end
54
+ end
55
+ end
56
+
57
+ describe ".inspect" do
58
+ it "renders the class name" do
59
+ subject.inspect.should match "Foo"
60
+ end
61
+
62
+ it "renders the attribute name and type" do
63
+ subject.inspect.should match subject.attributes.map { |a| a.name }.join(", ")
64
+ end
65
+ end
66
+
67
+ describe "#==" do
68
+ let(:model_class) do
69
+ Class.new do
70
+ include Attributes
71
+ attribute :name
72
+
73
+ def initialize(name)
74
+ write_attribute(:name, name)
75
+ end
76
+ end
77
+ end
78
+
79
+ subject { model_class.new("Ben") }
80
+
81
+ it "returns true when all attributes are equal" do
82
+ should == model_class.new("Ben")
83
+ end
84
+
85
+ it "returns false when compared to another type" do
86
+ should_not == Struct.new(:attributes).new("name" => "Ben")
87
+ end
88
+ end
89
+
90
+ describe "#attributes" do
91
+ context "when no attributes are defined" do
92
+ subject { Class.new { include Attributes } }
93
+
94
+ it "returns an empty Hash" do
95
+ subject.new.attributes.should == {}
96
+ end
97
+ end
98
+ end
99
+
100
+ describe "#inspect" do
101
+ let(:instance) { subject.new.tap { |obj| obj.name = "Ben" } }
102
+
103
+ it "includes the class name and all attribute values" do
104
+ instance.inspect.should == %q{#<Foo name: "Ben", amount: nil>}
105
+ end
106
+ end
107
+
108
+ describe "#read_attribute" do
109
+ let(:name) { "Bob" }
110
+ subject { model_class.new.tap { |s| s.write_attribute(:name, name) } }
111
+
112
+ it "returns the attribute using a Symbol" do
113
+ subject.read_attribute(:name).should == name
114
+ end
115
+
116
+ it "returns the attribute using a String" do
117
+ subject.read_attribute('name').should == name
118
+ end
119
+ end
120
+
121
+ describe "#write_attribute" do
122
+ subject { model_class.new }
123
+
124
+ it "raises ArgumentError with one argument" do
125
+ expect { subject.write_attribute(:name) }.to raise_error(ArgumentError)
126
+ end
127
+
128
+ it "raises ArgumentError with no arguments" do
129
+ expect { subject.write_attribute }.to raise_error(ArgumentError)
130
+ end
131
+
132
+ it "assigns sets an attribute using a Symbol and value" do
133
+ expect { subject.write_attribute(:name, "Ben") }.to change(subject, :attributes).from({}).to("name" => "Ben")
134
+ end
135
+
136
+ it "assigns sets an attribute using a String and value" do
137
+ expect { subject.write_attribute('name', "Ben") }.to change(subject, :attributes).from({}).to("name" => "Ben")
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+ require "active_attr/basic_model"
3
+
4
+ module ActiveAttr
5
+ describe BasicModel do
6
+ let(:model_class) do
7
+ Class.new do
8
+ include BasicModel
9
+
10
+ def self.name
11
+ "Foo"
12
+ end
13
+ end
14
+ end
15
+
16
+ subject { model_class.new }
17
+ it_should_behave_like "ActiveModel"
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ require "spec_helper"
2
+ require "active_attr/error"
3
+
4
+ module ActiveAttr
5
+ describe Error do
6
+ it { should be_a_kind_of Module }
7
+ end
8
+ end
@@ -2,45 +2,15 @@ require "spec_helper"
2
2
  require "active_attr/mass_assignment"
3
3
 
4
4
  module ActiveAttr
5
- describe MassAssignment do
5
+ describe MassAssignment, :mass_assignment do
6
6
  subject do
7
- Class.new do
7
+ model_class.class_eval do
8
8
  include MassAssignment
9
-
10
- attr_accessor :first_name, :last_name, :middle_name
11
- private :middle_name=
12
-
13
- def name=(name)
14
- self.last_name, self.first_name = name.split(nil, 2).reverse
15
- end
16
9
  end
17
10
  end
18
11
 
19
- let(:person) { subject.new }
20
- let(:first_name) { "Chris" }
21
- let(:last_name) { "Griego" }
22
-
23
- def should_assign_names_to(person)
24
- person.first_name.should == first_name
25
- person.last_name.should == last_name
26
- end
27
-
28
- shared_examples_for "a mass assigning method" do
29
- it "does not raise when assigning nil attributes" do
30
- expect { mass_assign_attributes nil }.not_to raise_error
31
- end
32
-
33
- it "assigns all valid attributes when passed as a hash with string keys" do
34
- should_assign_names_to mass_assign_attributes('first_name' => first_name, 'last_name' => last_name)
35
- end
36
-
37
- it "assigns all valid attributes when passed as a hash with symbol keys" do
38
- should_assign_names_to mass_assign_attributes(:first_name => first_name, :last_name => last_name)
39
- end
40
-
41
- it "uses any available writer methods" do
42
- should_assign_names_to mass_assign_attributes(:name => "#{first_name} #{last_name}")
43
- end
12
+ shared_examples "lenient mass assignment method", :lenient_mass_assignment_method => true do
13
+ include_examples "mass assignment method"
44
14
 
45
15
  it "ignores attributes which do not have a writer" do
46
16
  person = mass_assign_attributes(:middle_initial => "J")
@@ -54,66 +24,8 @@ module ActiveAttr
54
24
  end
55
25
  end
56
26
 
57
- describe "#initialize" do
58
- it "raises ArgumentError when called with three arguments" do
59
- expect { subject.new({}, {}, nil) }.to raise_error ArgumentError
60
- end
61
-
62
- it "does not raise when called with two arguments" do
63
- expect { subject.new({}, {}) }.not_to raise_error
64
- end
65
-
66
- it "does not raise when called with a single argument" do
67
- expect { subject.new({}) }.not_to raise_error
68
- end
69
-
70
- it "does not raise when called with no arguments" do
71
- expect { subject.new }.not_to raise_error
72
- end
73
-
74
- def mass_assign_attributes(attributes)
75
- subject.new(attributes)
76
- end
77
-
78
- it_should_behave_like "a mass assigning method"
79
- end
80
-
81
- describe "#attributes=" do
82
- it "raises ArgumentError when called with two arguments" do
83
- expect { person.send(:attributes=, {}, {}) }.to raise_error ArgumentError
84
- end
85
-
86
- def mass_assign_attributes(attributes)
87
- person.attributes = attributes
88
- person
89
- end
90
-
91
- it_should_behave_like "a mass assigning method"
92
- end
93
-
94
- describe "#assign_attributes" do
95
- it "raises ArgumentError when called with three arguments" do
96
- expect { subject.new.assign_attributes({}, {}, nil) }.to raise_error ArgumentError
97
- end
98
-
99
- it "does not raise when called with two arguments" do
100
- expect { subject.new.assign_attributes({}, {}) }.not_to raise_error
101
- end
102
-
103
- it "does not raise when called with a single argument" do
104
- expect { subject.new.assign_attributes({}) }.not_to raise_error
105
- end
106
-
107
- it "raises ArgumentError when called with no arguments" do
108
- expect { subject.new.assign_attributes }.to raise_error ArgumentError
109
- end
110
-
111
- def mass_assign_attributes(attributes)
112
- person.assign_attributes attributes
113
- person
114
- end
115
-
116
- it_should_behave_like "a mass assigning method"
117
- end
27
+ describe "#assign_attributes", :assign_attributes, :lenient_mass_assignment_method
28
+ describe "#attributes=", :attributes=, :lenient_mass_assignment_method
29
+ describe "#initialize", :initialize, :lenient_mass_assignment_method
118
30
  end
119
31
  end