require File.expand_path(File.dirname(__FILE__) + '/helper') class TestPropertySets < ActiveSupport::TestCase context "property sets" do fixtures :accounts, :account_settings, :account_texts setup do @account = Account.create(:name => "Name") end should "construct the container class" do assert defined?(AccountSetting) assert defined?(AccountText) end should "register the property sets used on a class" do assert_equal [ :settings, :texts, :validations, :typed_data ], Account.property_set_index end should "support protecting attributes" do assert @account.settings.protected?(:pro) assert !@account.settings.protected?(:foo) end should "allow enabling/disabling a property" do assert @account.settings.hep? @account.settings.disable(:hep) assert !@account.settings.hep? @account.settings.enable(:hep) assert @account.settings.hep? account = Account.new assert !account.settings.foo? account.settings.enable(:foo) assert account.settings.foo? end should "be empty on a new account" do assert @account.settings.empty? assert @account.texts.empty? assert !@account.texts.foo? assert !@account.texts.bar? assert @account.texts.foo.nil? assert @account.texts.bar.nil? end should "respond with defaults" do assert_equal false, @account.settings.bar? assert_equal nil, @account.settings.bar assert_equal true, @account.settings.hep? assert_equal 'skep', @account.settings.hep end context 'querying for a setting that does not exist' do setup do assert_equal([], @account.settings) assert_equal(true, @account.settings.hep?) end should 'not add a new setting' do assert_equal([], @account.settings) end should 'give back the default value' do assert_equal('skep', @account.settings.hep) end end should "reject settings with an invalid name" do s = AccountSetting.new(:account => @account) [ 'hello', 'hel_lo', 'hell0' ].each do |valid| s.name = valid assert s.valid? end [ '_hello', :hello ].each do |invalid| s.name = invalid assert !s.valid? end end should "validate uniqueness of settings" do @account.settings.create!(:name => "unique") assert_raise ActiveRecord::RecordInvalid do @account.settings.create!(:name => "unique") end end should "be creatable using the = operator" do assert !@account.settings.foo? [ "1", "2" ].each do |value| assert @account.settings.foo = value assert @account.settings.foo? assert @account.settings.size == 1 end assert @account.texts.empty? end should "coerce everything but nil to string" do @account.settings.foo = 3 @account.save assert @account.settings.foo == "3" @account.settings.foo = nil @account.save assert @account.settings.foo.nil? end should "reference the owner instance when constructing a new record" do record = @account.settings.lookup(:baz) assert record.new_record? assert record.account.id == @account.id end should "reference the owner instance when constructing a new record ...on a new record" do account = Account.new(:name => "New") record = account.settings.lookup(:baz) assert record.new_record? assert record.account == account end context "validations" do should "add an error when violated" do @account.validations.validated = "hello" assert !@account.valid? assert_equal "Value BEEP", @account.errors.full_messages.first end end context "#set" do should "support writing multiple values to the association" do assert !@account.settings.foo? assert !@account.settings.bar? @account.settings.set(:foo => "123", :bar => "456") assert @account.settings.foo? assert @account.settings.bar? end should "convert string keys to symbols to ensure consistent lookup" do @account.settings.set(:foo => "123") @account.settings.set("foo" => "456") assert @account.save! end should "work identically for new and existing owner objects" do [ @account, Account.new(:name => "Mibble") ].each do |account| account.settings.set(:foo => "123", :bar => "456") assert_equal account.settings.size, 2 assert_equal account.settings.foo, "123" assert_equal account.settings.bar, "456" account.settings.set(:bar => "789", :baz => "012") assert_equal account.settings.size, 3 assert_equal account.settings.foo, "123" assert_equal account.settings.bar, "789" assert_equal account.settings.baz, "012" end end should "be updateable as AR nested attributes" do assert @account.texts_attributes = [{ :name => "foo", :value => "1" }, { :name => "bar", :value => "0" }] @account.save! assert @account.texts.foo == "1" assert @account.texts.bar == "0" @account.update_attributes!(:texts_attributes => [ { :id => @account.texts.lookup(:foo).id, :name => "foo", :value => "0" }, { :id => @account.texts.lookup(:bar).id, :name => "bar", :value => "1" } ]) assert @account.texts.foo == "0" assert @account.texts.bar == "1" end should "be updateable as a nested structure" do attribs = { :name => "Kim", :settings => { :foo => "1", :bar => "0" } } assert @account.update_attributes(attribs) @account.save! assert @account.settings.foo? assert !@account.settings.bar? assert @account.settings.foo == "1" assert @account.settings.bar == "0" assert !@account.settings.pro? attribs = { :name => "Kim", :settings => { :foo => "1", :bar => "1", :baz => "1", :pro => "1" } } assert @account.update_attributes!(attribs) assert @account.settings.foo? assert @account.settings.bar? assert @account.settings.baz? assert !@account.settings.pro? end end context "lookup" do context "with data" do setup { @account.texts.foo = "1" } should "return the data" do assert_equal "1", @account.texts.lookup(:foo).value end end context "without data" do should "create a new record, returning the default" do assert_equal nil, @account.texts.lookup(:foo).value assert @account.texts.detect { |p| p.name == "foo" } end end end context "lookup_without_default" do should "return the row if it exists" do @account.texts.foo = "1" assert_equal "1", @account.texts.lookup_without_default(:foo).value end should "return nil otherwise" do assert_equal nil, @account.texts.lookup_without_default(:foo) end end context "typed columns" do context "string data" do should "be writable and readable" do @account.typed_data.string_prop = "foo" assert_equal "foo", @account.typed_data.string_prop end end context "floating point data" do should "be writable and readable" do @account.typed_data.float_prop = 1.97898 assert_equal 1.97898, @account.typed_data.float_prop @account.save! assert_equal 1.97898, @account.typed_data.float_prop end end context "integer data" do should "be writable and readable" do @account.typed_data.int_prop = 25 assert_equal 25, @account.typed_data.int_prop @account.save! assert_equal 25, @account.typed_data.int_prop assert_equal "25", @account.typed_data.lookup("int_prop").value end end context "datetime data" do should "be writable and readable" do ts = Time.at(Time.now.to_i) @account.typed_data.datetime_prop = ts assert_equal ts, @account.typed_data.datetime_prop @account.save! assert_equal ts, @account.typed_data.datetime_prop end should "store data in UTC" do ts = Time.at(Time.now.to_i) string_rep = ts.in_time_zone("UTC").to_s @account.typed_data.datetime_prop = ts @account.save! assert_equal string_rep, @account.typed_data.lookup("datetime_prop").value end end end end end