Skip to content

Commit

Permalink
Added QueryAttributes, closes #3
Browse files Browse the repository at this point in the history
  • Loading branch information
cgriego committed Nov 16, 2011
1 parent f0fc718 commit 6742614
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
@@ -1,6 +1,7 @@
# ActiveAttr 0.3.0 (unreleased) #

* Added BlockInitialization
* Added QueryAttributes
* Added UnknownAttributeError
* Attributes now honors getters/setters when calling #read_attribute,
#write_attribute, #[], and #[]=
Expand Down
17 changes: 17 additions & 0 deletions README.md
Expand Up @@ -79,6 +79,23 @@ attribute.
person.first_name #=> "Chris"
person.last_name #=> "Griego"

### QueryAttributes ###

Including the QueryAttributes module into your class builds on Attributes by
providing instance methods for querying your attributes

class Person
include ActiveAttr::QueryAttributes

attribute :first_name
attribute :last_name
end

person = Person.new
person.first_name = "Chris"
person.first_name? #=> true
person.last_name? #=> false

## RSpec Integration ##

ActiveAttr comes with matchers and RSpec integration to assist you in testing
Expand Down
1 change: 1 addition & 0 deletions lib/active_attr.rb
Expand Up @@ -14,6 +14,7 @@ module ActiveAttr
autoload :ChainableInitialization
autoload :Error
autoload :MassAssignment
autoload :QueryAttributes
autoload :StrictMassAssignment
autoload :UnknownAttributeError
autoload :UnknownAttributesError
Expand Down
60 changes: 60 additions & 0 deletions lib/active_attr/query_attributes.rb
@@ -0,0 +1,60 @@
require "active_attr/attributes"
require "active_attr/unknown_attribute_error"
require "active_support/concern"
require "active_support/core_ext/object/blank"

module ActiveAttr
# QueryAttributes provides instance methods for querying attributes.
#
# @example Usage
# class Person
# include ActiveAttr::QueryAttributes
# attribute :name
# end
#
# person = Person.new
# person.name? #=> false
# person.name = "Chris Griego"
# person.name? #=> true
#
# @since 0.3.0
module QueryAttributes
extend ActiveSupport::Concern
include Attributes

included do
attribute_method_suffix "?"
end

# Test the presence of an attribute
#
# Similar to an ActiveRecord model, when the attribute is a zero value or
# is a string that represents false, the method returns false.
#
# @example Query an attribute
# person.query_attribute(:name)
#
# @param [String, Symbol, #to_s] name The name of the attribute to query
#
# @return [true, false] The presence of the attribute
#
# @since 0.3.0
def query_attribute(name)
if respond_to? "#{name}?"
send "#{name}?"
else
raise UnknownAttributeError, "unknown attribute: #{name}"
end
end

private

def attribute?(name)
case value = read_attribute(name)
when "false", "FALSE", "f", "F" then false
when Numeric, /^\-?[0-9]/ then !value.to_f.zero?
else value.present?
end
end
end
end
2 changes: 1 addition & 1 deletion spec/unit/active_attr/attributes_spec.rb
Expand Up @@ -80,7 +80,7 @@ def self.name
subject.amount = 1
end

it "defining an attribute twice does not make appear in the" do
it "defining an attribute twice does not give the class two attribute definitions" do
Class.new do
include Attributes
attribute :name
Expand Down
225 changes: 225 additions & 0 deletions spec/unit/active_attr/query_attributes_spec.rb
@@ -0,0 +1,225 @@
require "spec_helper"
require "active_attr/query_attributes"
require "bigdecimal"

module ActiveAttr
describe QueryAttributes do
subject { model_class.new }

let :model_class do
Class.new do
include QueryAttributes

attribute :value
attribute :overridden

def overridden?
super
end

def true?
true
end

def false?
false
end
end
end

describe ".attribute" do
it "defines an attribute predicate method that calls #attribute?" do
subject.should_receive(:attribute?).with("value")
subject.value?
end

it "defines an attribute reader that can be called via super" do
subject.should_receive(:attribute?).with("overridden")
subject.overridden?
end
end

describe "#query_attribute" do
it "raises ArgumentError when called with two arguments" do
expect { subject.query_attribute(:a, :b) }.to raise_error ArgumentError
end

it "does not raise when called with a single argument" do
expect { subject.query_attribute(:a) }.not_to raise_error ArgumentError
end

it "raises ArgumentError when called with no arguments" do
expect { subject.query_attribute }.to raise_error ArgumentError
end

it "calls the predicate method if defined" do
subject.query_attribute(:true).should == true
subject.query_attribute(:false).should == false
end

it "raises when getting an undefined attribute" do
expect { subject.query_attribute(:initials) }.to raise_error UnknownAttributeError, "unknown attribute: initials"
end

it "is false when the attribute is false" do
subject.value = false
subject.value?.should == false
end

it "is true when the attribute is true" do
subject.value = true
subject.value?.should == true
end

it "is false when the attribute is nil" do
subject.value = nil
subject.value?.should == false
end

it "is true when the attribute is an Object" do
subject.value = Object.new
subject.value?.should == true
end

it "is false when the attribute is an empty string" do
subject.value = ""
subject.value?.should == false
end

it "is true when the attribute is a non-empty string" do
subject.value = "Chris"
subject.value?.should == true
end

it "is false when the attribute is 0" do
subject.value = 0
subject.value?.should == false
end

it "is true when the attribute is 1" do
subject.value = 1
subject.value?.should == true
end

it "is false when the attribute is 0.0" do
subject.value = 0.0
subject.value?.should == false
end

it "is true when the attribute is 0.1" do
subject.value = 0.1
subject.value?.should == true
end

it "is false when the attribute is a zero BigDecimal" do
subject.value = BigDecimal.new("0.0")
subject.value?.should == false
end

it "is true when the attribute is a non-zero BigDecimal" do
subject.value = BigDecimal.new("0.1")
subject.value?.should == true
end

it "is true when the attribute is -1" do
subject.value = -1
subject.value?.should == true
end

it "is false when the attribute is -0.0" do
subject.value = -0.0
subject.value?.should == false
end

it "is true when the attribute is -0.1" do
subject.value = -0.1
subject.value?.should == true
end

it "is false when the attribute is a negative zero BigDecimal" do
subject.value = BigDecimal.new("-0.0")
subject.value?.should == false
end

it "is true when the attribute is a negative BigDecimal" do
subject.value = BigDecimal.new("-0.1")
subject.value?.should == true
end

it "is false when the attribute is '0'" do
subject.value = "0"
subject.value?.should == false
end

it "is true when the attribute is '1'" do
subject.value = "1"
subject.value?.should == true
end

it "is false when the attribute is '0.0'" do
subject.value = "0.0"
subject.value?.should == false
end

it "is true when the attribute is '0.1'" do
subject.value = "0.1"
subject.value?.should == true
end

it "is true when the attribute is '-1'" do
subject.value = "-1"
subject.value?.should == true
end

it "is false when the attribute is '-0.0'" do
subject.value = "-0.0"
subject.value?.should == false
end

it "is true when the attribute is '-0.1'" do
subject.value = "-0.1"
subject.value?.should == true
end

it "is true when the attribute is 'true'" do
subject.value = "true"
subject.value?.should == true
end

it "is false when the attribute is 'false'" do
subject.value = "false"
subject.value?.should == false
end

it "is true when the attribute is 't'" do
subject.value = "t"
subject.value?.should == true
end

it "is false when the attribute is 'f'" do
subject.value = "f"
subject.value?.should == false
end

it "is true when the attribute is 'T'" do
subject.value = "T"
subject.value?.should == true
end

it "is false when the attribute is 'F'" do
subject.value = "F"
subject.value?.should == false
end

it "is true when the attribute is 'TRUE'" do
subject.value = "TRUE"
subject.value?.should == true
end

it "is false when the attribute is 'FALSE" do
subject.value = "FALSE"
subject.value?.should == false
end
end
end
end

0 comments on commit 6742614

Please sign in to comment.