require File.expand_path(File.dirname(__FILE__) + '/spec_helper') describe DattsRight do before do reset_database @page = Page.create end describe ".add_dynamic_attribute(attr_key, object_type)" do it "should add a dynamic attribute with nil value" do @page.add_dynamic_attribute(:rocks, "string") @page.dynamic_attributes[:rocks].should be_nil end it "should ignore when trying to add same attribute" do @page.add_dynamic_attribute(:rocks, "string") @page.add_dynamic_attribute(:rocks, "integer") @page.dynamic_columns[:rocks][:object_type].should == "string" end it "should return false if the method that is being added already exists" do @page.add_dynamic_attribute(:name, "text").should be_false end it "should not make any changes to the original attribute" do @page.update_attribute(:name, "juno") @page.add_dynamic_attribute(:name, "text").should be_false @page.name.should == "juno" end end describe ".remove_dynamic_attribute(attr_key)" do it "should remove the attribute completely" do @page.add_dynamic_attribute(:rocks, "string") @page.remove_dynamic_attribute(:rocks) lambda { @page.rocks }.should raise_error(NoMethodError) end it "should not explode if the attribute being removed isn't there" do @page.add_dynamic_attribute(:rocks, "string") @page.remove_dynamic_attribute(:rocks) lambda { @page.remove_dynamic_attribute(:rocks) }.should_not raise_error(NoMethodError) end end describe ".dynamic_columns(reload)" do it "should return an array of symbols of the available dynamic columns" do @page.add_dynamic_attribute(:rocks, "string") @page.is_dynamic_attribute?(:rocks).should be_true end end it "should not allow arbitrary creation of attributes" do lambda { @page.some_field = "woot" }.should raise_error(NoMethodError) end it "should allow saving of integers" do @page.add_dynamic_attribute(:price, "integer") @page.price = 300 @page.save @page = Page.last @page.price.should == 300 end it "should allow saving of floats" do @page.add_dynamic_attribute(:price, "float") @page.price = 300.0 @page.save @page = Page.last @page.price.should == 300.0 end it "should allow saving of true" do @page.add_dynamic_attribute(:real, "boolean") @page.real = true @page.save @page = Page.last @page.real.should be_true end it "should allow saving of false" do @page.add_dynamic_attribute(:real, "boolean") @page.real = false @page.save @page = Page.last @page.real.should be_false end it "should allow saving of strings" do @page.add_dynamic_attribute(:real, "string") @page.real = "its real alright" @page.save @page = Page.last @page.real.should == "its real alright" end it "should allow saving of text" do @page.add_dynamic_attribute(:real, "text") @page.real = "t"*256 @page.save @page = Page.last @page.real.should == "t"*256 end it "should cache the attribute" do @page.add_dynamic_attribute(:price, "integer") @page.price = 200 @page.price.should == 200 end it "should not save the attribute if save on the attributable object isn't called" do @page.add_dynamic_attribute(:price, "integer") @page.price = 200 @page = Page.last @page.price.should be_nil end it "should allow overwriting of values" do @page.add_dynamic_attribute(:price, "integer") @page.price = 200 @page.save @page = Page.last @page.price = 300 @page.save @page = Page.last @page.price.should == 300 end it "should allow nullifying of attributes, but keeping the fields there" do @page.add_dynamic_attribute(:farce, "string") @page.farce = "Nothing here my friend" @page.save @page.farce = nil @page.is_dynamic_attribute?(:farce).should be_true end describe "on dynamic find_by methods" do it "should work" do @page.add_dynamic_attribute(:price, "integer") @page.price = 400 @page.save @page_2 = Page.create @page_2.add_dynamic_attribute(:price, "integer") @page_2.price = 500 @page_2.save Page.find_by_price(400).should include(@page) Page.find_by_price(400).should_not include(@page_2) end it "should allow chaining" do @page.add_dynamic_attribute(:price, "integer") @page.name = "fixed" @page.price = 400 @page.save @page_2 = Page.create @page_2.add_dynamic_attribute(:price, "integer") @page_2.price = 500 @page_2.save @pages = Page.where(:name => "fixed").find_by_price(400) @pages.should include(@page) @pages.should_not include(@page_2) end end it "should allow multiple attributes" do @page.add_dynamic_attribute(:price, "integer") @page.price = 400 @page.add_dynamic_attribute(:farce, "string") @page.farce = "hi" @page.save @page_2 = Page.create @page_2.add_dynamic_attribute(:price, "integer") @page_2.price = 400 @page_2.add_dynamic_attribute(:farce, "string") @page_2.farce = "hitt" @page_2.save @pages = Page.find_by_farce_and_price("hi", 400) @pages.should include(@page) @pages.should_not include(@page_2) end describe "when the value being assigned is not of the same type (with exceptions)" do before do @page.add_dynamic_attribute(:price, "integer") end it "should not save the value" do @page.price = 200 @page.save @page.price = "hi there" @page.save Page.last.price.should == 200 end end it "should allow mass assignment" do @page.add_dynamic_attribute(:price, "integer") @page.add_dynamic_attribute(:farce, "string") @page.update_attributes(:price => 200, :farce => "hi") @page.price.should == 200 @page.farce.should == "hi" @page.update_attributes!(:price => 300, :farce => "not farce!") @page.price.should == 300 @page.farce.should == "not farce!" end it "should allow use of update_attribute" do @page.add_dynamic_attribute(:price, "integer") @page.update_attribute(:price, 200) @page.price.should == 200 end describe "ordering" do it "should default to asc" do @page.add_dynamic_attribute(:price, "integer") @page.price = 2 @page.save @page_2 = Page.create @page_2.add_dynamic_attribute(:price, "integer") @page_2.price = 1 @page_2.save @page_3 = Page.create @page_3.add_dynamic_attribute(:price, "float") @page_3.price = 3.0 @page_3.save @page_4 = Page.create @page_4.add_dynamic_attribute(:price, "float") @page_4.price = 3.5 @page_4.save # What if different records have price but they are of different object_type? # page_1.price is an "integer" and it's saved in "integer_value" column # page_2.price is a "float" and it's saved in "float_value" # # How would you work on Page.order_datt("price")? # # Answer 1: we could require passing of the object_type in the order method # This way, we know which column to look at => order_datt("price", "integer") Page.order_datt("price", "integer").should == [@page_2, @page] Page.order_datt("price DESC", "float").should == [@page_4, @page_3] end end describe "where_datt" do it "should automatically join in the datts table" do @page.add_dynamic_attribute(:price, "float") @page.price = 200.0 @page.add_dynamic_attribute(:farce, "boolean") @page.farce = true @page.save @page_2 = Page.create @page_2.add_dynamic_attribute(:price, "integer") @page_2.add_dynamic_attribute(:farce, "boolean") @page_2.farce = true @page_2.price = 1 @page_2.save @result = Page.where_datt(:price => 200.0, :farce => true) @result.should include(@page) @result.should_not include(@page_2) #@result = Page.where_datt("price < :price", :price => 199.0) #@result.should_not include(@page) #@result.should include(@page_2) end it "should allow chaining with where scopes" do @page.add_dynamic_attribute(:price, "integer") @page.name = "aardvark" @page.price = 200 @page.save @page_2 = Page.create @page_2.add_dynamic_attribute(:price, "integer") @page_2.price = 200 @page_2.save @results = Page.where_datt(:price => 200).where(:name => "aardvark") @results.should include(@page) @results.should_not include(@page_2) end it "should return an empty array if we're looking for a field that doesn't exist" do Page.where_datt(:bogus_field => "none").should be_empty end end end