diff --git a/.gitignore b/.gitignore index 09a4164..4b25654 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ rdoc pkg \#* .#* +*.gem \ No newline at end of file diff --git a/.rvmrc b/.rvmrc new file mode 100644 index 0000000..e10b4a2 --- /dev/null +++ b/.rvmrc @@ -0,0 +1 @@ +rvm use 1.9.2@bitmask_attributes --create --install \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..a3c3420 --- /dev/null +++ b/Gemfile @@ -0,0 +1,11 @@ +source "http://rubygems.org" + +gemspec + +if RUBY_VERSION < '1.9' + gem "ruby-debug", ">= 0.10.3" +end + +gem 'shoulda' +gem 'sqlite3' +gem 'turn' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..9cdeaea --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,37 @@ +PATH + remote: . + specs: + bitmask_attributes (0.2.1) + activerecord (~> 3.0) + +GEM + remote: http://rubygems.org/ + specs: + activemodel (3.0.9) + activesupport (= 3.0.9) + builder (~> 2.1.2) + i18n (~> 0.5.0) + activerecord (3.0.9) + activemodel (= 3.0.9) + activesupport (= 3.0.9) + arel (~> 2.0.10) + tzinfo (~> 0.3.23) + activesupport (3.0.9) + ansi (1.3.0) + arel (2.0.10) + builder (2.1.2) + i18n (0.5.0) + shoulda (2.11.3) + sqlite3 (1.3.3) + turn (0.8.2) + ansi (>= 1.2.2) + tzinfo (0.3.29) + +PLATFORMS + ruby + +DEPENDENCIES + bitmask_attributes! + shoulda + sqlite3 + turn diff --git a/LICENSE b/LICENSE index e304404..8ae5aa1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2007-2009 Bruce Williams +Copyright (c) 2007-2009 Bruce Williams & 2011 Joel Moss Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.markdown b/README.md similarity index 62% rename from README.markdown rename to README.md index 699ee5b..414bd0f 100644 --- a/README.markdown +++ b/README.md @@ -1,7 +1,20 @@ -bitmask-attribute +BitmaskAttributes ================= -Transparent manipulation of bitmask attributes. +Transparent manipulation of bitmask attributes for ActiveRecord, based on +the bitmask-attribute gem, which has been dormant since 2009. This updated +gem work with Rails 3 and up (including Rails 3.1). + +Installation +------------ + +The best way to install is with RubyGems: + + $ [sudo] gem install bitmask_attributes + +Or better still, just add it to your Gemfile: + + gem 'bitmask_attributes' Example ------- @@ -40,6 +53,11 @@ Or, just check if any values are present: user.roles? # => true +You can get the list of values for any given attribute: + + User.values_for_roles + # => [:writer, :publisher, :editor, :proofreader] + Named Scopes ------------ @@ -52,11 +70,8 @@ A couple useful named scopes are also generated when you use # => (all editors) User.with_roles(:editor, :writer) # => (all users who are BOTH editors and writers) - -Later we'll support an `or` boolean; for now, do something like: - - User.with_roles(:editor) + User.with_roles(:writer) - # => (all users who are EITHER editors and writers) + User.with_any_roles(:editor, :writer) + # => (all users who are editors OR writers) Find records without any bitmask set: @@ -89,20 +104,23 @@ IMPORTANT: Once you have data using a bitmask, don't change the order of the values, remove any values, or insert any new values in the `:as` array anywhere except at the end. You won't like the results. -Contributing and reporting issues ---------------------------------- - -Please feel free to fork & contribute fixes via GitHub pull requests. -The official repository for this project is -http://github.com/bruce/bitmask-attribute +Contributing +------------ -Issues can be reported at -http://github.com/bruce/bitmask-attribute/issues +1. Fork it. +2. Create a branch (`git checkout -b new-feature`) +3. Make your changes +4. Run the tests (`bundle install` then `bundle exec rake`) +5. Commit your changes (`git commit -am "Created new feature"`) +6. Push to the branch (`git push origin new-feature`) +7. Create a [Pull Request](http://help.github.com/pull-requests/) from your branch. +8. Promote it. Get others to drop in and +1 it. Credits ------- -Thanks to the following contributors: +Thanks to [Bruce Williams](https://github.com/bruce) and the following contributors +of the bitmask-attribute plugin: * [Jason L Perry](http://github.com/ambethia) * [Nicolas Fouché](http://github.com/nfo) @@ -110,4 +128,4 @@ Thanks to the following contributors: Copyright --------- -Copyright (c) 2007-2009 Bruce Williams. See LICENSE for details. +Copyright (c) 2007-2009 Bruce Williams & 2011 Joel Moss. See LICENSE for details. diff --git a/Rakefile b/Rakefile index 26d0807..272cdda 100644 --- a/Rakefile +++ b/Rakefile @@ -1,57 +1,16 @@ -require 'rubygems' -require 'rake' +# encoding: UTF-8 -begin - require 'jeweler' - Jeweler::Tasks.new do |gem| - gem.name = "bitmask-attribute" - gem.summary = %Q{Simple bitmask attribute support for ActiveRecord} - gem.email = "bruce@codefluency.com" - gem.homepage = "http://github.com/bruce/bitmask-attribute" - gem.authors = ["Bruce Williams"] - gem.add_dependency 'activerecord' - # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings - end - Jeweler::GemcutterTasks.new -rescue LoadError - puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" -end +require "bundler" +Bundler::GemHelper.install_tasks require 'rake/testtask' -Rake::TestTask.new(:test) do |test| - test.libs << 'lib' << 'test' - test.pattern = 'test/**/*_test.rb' - test.verbose = true -end - -begin - require 'rcov/rcovtask' - Rcov::RcovTask.new do |test| - test.libs << 'test' - test.pattern = 'test/**/*_test.rb' - test.verbose = true - end -rescue LoadError - task :rcov do - abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" - end -end - +desc 'Default: run unit tests.' task :default => :test -require 'rake/rdoctask' -Rake::RDocTask.new do |rdoc| - if File.exist?('VERSION.yml') - config = YAML.load(File.read('VERSION.yml')) - version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}" - else - version = "" - end - - rdoc.rdoc_dir = 'rdoc' - rdoc.title = "bitmask-attribute #{version}" - rdoc.rdoc_files.include('README*') - rdoc.rdoc_files.include('lib/**/*.rb') -end - +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end \ No newline at end of file diff --git a/VERSION b/VERSION deleted file mode 100644 index 9084fa2..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.1.0 diff --git a/bitmask_attributes.gemspec b/bitmask_attributes.gemspec new file mode 100644 index 0000000..12fcb41 --- /dev/null +++ b/bitmask_attributes.gemspec @@ -0,0 +1,20 @@ +# -*- encoding: utf-8 -*- +$:.push File.expand_path("../lib", __FILE__) +require "bitmask_attributes/version" + +Gem::Specification.new do |gem| + gem.name = "bitmask_attributes" + gem.summary = %Q{Simple bitmask attribute support for ActiveRecord} + gem.description = %Q{Simple bitmask attribute support for ActiveRecord} + gem.email = "joel@developwithstyle.com" + gem.homepage = "http://github.com/joelmoss/bitmask_attributes" + gem.authors = ['Joel Moss'] + + gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + gem.files = `git ls-files`.split("\n") + gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + gem.require_paths = ['lib'] + gem.version = BitmaskAttributes::VERSION + + gem.add_dependency 'activerecord', '~> 3.0' +end \ No newline at end of file diff --git a/lib/bitmask-attribute.rb b/lib/bitmask-attribute.rb deleted file mode 100644 index da25545..0000000 --- a/lib/bitmask-attribute.rb +++ /dev/null @@ -1,2 +0,0 @@ -# Stub for dash-style requires -require File.dirname(__FILE__) << "/bitmask_attribute" \ No newline at end of file diff --git a/lib/bitmask_attribute.rb b/lib/bitmask_attribute.rb deleted file mode 100644 index 0da85d1..0000000 --- a/lib/bitmask_attribute.rb +++ /dev/null @@ -1,154 +0,0 @@ -require 'bitmask_attribute/value_proxy' - -module BitmaskAttribute - - class Definition - - attr_reader :attribute, :values, :extension - def initialize(attribute, values=[], &extension) - @attribute = attribute - @values = values - @extension = extension - end - - def install_on(model) - validate_for model - generate_bitmasks_on model - override model - create_convenience_class_method_on(model) - create_convenience_instance_methods_on(model) - create_named_scopes_on(model) - end - - ####### - private - ####### - - def validate_for(model) - # The model cannot be validated if it is preloaded and the attribute/column is not in the - # database (the migration has not been run). This usually - # occurs in the 'test' and 'production' environments. - return if defined?(Rails) && Rails.configuration.cache_classes - - unless model.columns.detect { |col| col.name == attribute.to_s } - raise ArgumentError, "`#{attribute}' is not an attribute of `#{model}'" - end - end - - def generate_bitmasks_on(model) - model.bitmasks[attribute] = returning HashWithIndifferentAccess.new do |mapping| - values.each_with_index do |value, index| - mapping[value] = 0b1 << index - end - end - end - - def override(model) - override_getter_on(model) - override_setter_on(model) - end - - def override_getter_on(model) - model.class_eval %( - def #{attribute} - @#{attribute} ||= BitmaskAttribute::ValueProxy.new(self, :#{attribute}, &self.class.bitmask_definitions[:#{attribute}].extension) - end - ) - end - - def override_setter_on(model) - model.class_eval %( - def #{attribute}=(raw_value) - values = raw_value.kind_of?(Array) ? raw_value : [raw_value] - self.#{attribute}.replace(values.reject(&:blank?)) - end - ) - end - - def create_convenience_class_method_on(model) - model.class_eval %( - def self.bitmask_for_#{attribute}(*values) - values.inject(0) do |bitmask, value| - unless (bit = bitmasks[:#{attribute}][value]) - raise ArgumentError, "Unsupported value for #{attribute}: \#{value.inspect}" - end - bitmask | bit - end - end - ) - end - - - def create_convenience_instance_methods_on(model) - values.each do |value| - model.class_eval %( - def #{attribute}_for_#{value}? - self.#{attribute}?(:#{value}) - end - ) - end - model.class_eval %( - def #{attribute}?(*values) - if !values.blank? - values.all? do |value| - self.#{attribute}.include?(value) - end - else - self.#{attribute}.present? - end - end - ) - end - - def create_named_scopes_on(model) - model.class_eval %( - named_scope :with_#{attribute}, - proc { |*values| - if values.blank? - {:conditions => '#{attribute} > 0 OR #{attribute} IS NOT NULL'} - else - sets = values.map do |value| - mask = #{model}.bitmask_for_#{attribute}(value) - "#{attribute} & \#{mask} <> 0" - end - {:conditions => sets.join(' AND ')} - end - } - named_scope :without_#{attribute}, :conditions => "#{attribute} == 0 OR #{attribute} IS NULL" - named_scope :no_#{attribute}, :conditions => "#{attribute} == 0 OR #{attribute} IS NULL" - ) - values.each do |value| - model.class_eval %( - named_scope :#{attribute}_for_#{value}, - :conditions => ['#{attribute} & ? <> 0', #{model}.bitmask_for_#{attribute}(:#{value})] - ) - end - end - - end - - def self.included(model) - model.extend ClassMethods - end - - module ClassMethods - - def bitmask(attribute, options={}, &extension) - unless options[:as] && options[:as].kind_of?(Array) - raise ArgumentError, "Must provide an Array :as option" - end - bitmask_definitions[attribute] = BitmaskAttribute::Definition.new(attribute, options[:as].to_a, &extension) - bitmask_definitions[attribute].install_on(self) - end - - def bitmask_definitions - @bitmask_definitions ||= {} - end - - def bitmasks - @bitmasks ||= {} - end - - end - -end diff --git a/lib/bitmask_attribute/value_proxy.rb b/lib/bitmask_attribute/value_proxy.rb deleted file mode 100644 index a2e36be..0000000 --- a/lib/bitmask_attribute/value_proxy.rb +++ /dev/null @@ -1,72 +0,0 @@ -module BitmaskAttribute - - class ValueProxy < Array - - def initialize(record, attribute, &extension) - @record = record - @attribute = attribute - find_mapping - instance_eval(&extension) if extension - super(extract_values) - end - - # ========================= - # = OVERRIDE TO SERIALIZE = - # ========================= - - %w(push << delete replace reject! select!).each do |override| - class_eval(<<-EOEVAL) - def #{override}(*args) - returning(super) do - updated! - end - end - EOEVAL - end - - def to_i - inject(0) { |memo, value| memo | @mapping[value] } - end - - ####### - private - ####### - - def validate! - each do |value| - if @mapping.key? value - true - else - raise ArgumentError, "Unsupported value for `#{@attribute}': #{value.inspect}" - end - end - end - - def updated! - validate! - uniq! - serialize! - end - - def serialize! - @record.send(:write_attribute, @attribute, to_i) - end - - def extract_values - stored = [@record.send(:read_attribute, @attribute) || 0, 0].max - @mapping.inject([]) do |values, (value, bitmask)| - returning values do - values << value.to_sym if (stored & bitmask > 0) - end - end - end - - def find_mapping - unless (@mapping = @record.class.bitmasks[@attribute]) - raise ArgumentError, "Could not find mapping for bitmask attribute :#{@attribute}" - end - end - - end - -end diff --git a/lib/bitmask_attributes.rb b/lib/bitmask_attributes.rb new file mode 100644 index 0000000..6e0cbfb --- /dev/null +++ b/lib/bitmask_attributes.rb @@ -0,0 +1,26 @@ +require 'bitmask_attributes/definition' +require 'bitmask_attributes/value_proxy' + +module BitmaskAttributes + extend ActiveSupport::Concern + + module ClassMethods + def bitmask(attribute, options={}, &extension) + unless options[:as] && options[:as].kind_of?(Array) + raise ArgumentError, "Must provide an Array :as option" + end + bitmask_definitions[attribute] = Definition.new(attribute, options[:as].to_a, &extension) + bitmask_definitions[attribute].install_on(self) + end + + def bitmask_definitions + @bitmask_definitions ||= {} + end + + def bitmasks + @bitmasks ||= {} + end + end +end + +ActiveRecord::Base.send :include, BitmaskAttributes \ No newline at end of file diff --git a/lib/bitmask_attributes/definition.rb b/lib/bitmask_attributes/definition.rb new file mode 100644 index 0000000..d0870f8 --- /dev/null +++ b/lib/bitmask_attributes/definition.rb @@ -0,0 +1,145 @@ +module BitmaskAttributes + class Definition + attr_reader :attribute, :values, :extension + + def initialize(attribute, values=[], &extension) + @attribute = attribute + @values = values + @extension = extension + end + + def install_on(model) + validate_for model + generate_bitmasks_on model + override model + create_convenience_class_method_on model + create_convenience_instance_methods_on model + create_scopes_on model + create_attribute_methods_on model + end + + private + + def validate_for(model) + # The model cannot be validated if it is preloaded and the attribute/column is not in the + # database (the migration has not been run). This usually + # occurs in the 'test' and 'production' environments. + return if defined?(Rails) && Rails.configuration.cache_classes + + unless model.columns.detect { |col| col.name == attribute.to_s } + raise ArgumentError, "`#{attribute}' is not an attribute of `#{model}'" + end + end + + def generate_bitmasks_on(model) + model.bitmasks[attribute] = HashWithIndifferentAccess.new.tap do |mapping| + values.each_with_index do |value, index| + mapping[value] = 0b1 << index + end + end + end + + def override(model) + override_getter_on(model) + override_setter_on(model) + end + + def override_getter_on(model) + model.class_eval %( + def #{attribute} + @#{attribute} ||= BitmaskAttributes::ValueProxy.new(self, :#{attribute}, &self.class.bitmask_definitions[:#{attribute}].extension) + end + ) + end + + def override_setter_on(model) + model.class_eval %( + def #{attribute}=(raw_value) + values = raw_value.kind_of?(Array) ? raw_value : [raw_value] + self.#{attribute}.replace(values.reject(&:blank?)) + end + ) + end + + # Returns the defined values as an Array. + def create_attribute_methods_on(model) + model.class_eval %( + def self.values_for_#{attribute} # def self.values_for_numbers + #{values} # [:one, :two, :three] + end # end + ) + end + + def create_convenience_class_method_on(model) + model.class_eval %( + def self.bitmask_for_#{attribute}(*values) + values.inject(0) do |bitmask, value| + unless (bit = bitmasks[:#{attribute}][value]) + raise ArgumentError, "Unsupported value for #{attribute}: \#{value.inspect}" + end + bitmask | bit + end + end + ) + end + + def create_convenience_instance_methods_on(model) + values.each do |value| + model.class_eval %( + def #{attribute}_for_#{value}? + self.#{attribute}?(:#{value}) + end + ) + end + model.class_eval %( + def #{attribute}?(*values) + if !values.blank? + values.all? do |value| + self.#{attribute}.include?(value) + end + else + self.#{attribute}.present? + end + end + ) + end + + def create_scopes_on(model) + model.class_eval %( + scope :with_#{attribute}, + proc { |*values| + if values.blank? + {:conditions => '#{attribute} > 0 OR #{attribute} IS NOT NULL'} + else + sets = values.map do |value| + mask = #{model}.bitmask_for_#{attribute}(value) + "#{attribute} & \#{mask} <> 0" + end + {:conditions => sets.join(' AND ')} + end + } + scope :without_#{attribute}, :conditions => "#{attribute} = 0 OR #{attribute} IS NULL" + scope :no_#{attribute}, :conditions => "#{attribute} = 0 OR #{attribute} IS NULL" + + scope :with_any_#{attribute}, + proc { |*values| + if values.blank? + {:conditions => '#{attribute} > 0 OR #{attribute} IS NOT NULL'} + else + sets = values.map do |value| + mask = #{model}.bitmask_for_#{attribute}(value) + "#{attribute} & \#{mask} <> 0" + end + {:conditions => sets.join(' OR ')} + end + } + ) + values.each do |value| + model.class_eval %( + scope :#{attribute}_for_#{value}, + :conditions => ['#{attribute} & ? <> 0', #{model}.bitmask_for_#{attribute}(:#{value})] + ) + end + end + end +end \ No newline at end of file diff --git a/lib/bitmask_attributes/value_proxy.rb b/lib/bitmask_attributes/value_proxy.rb new file mode 100644 index 0000000..ea7d691 --- /dev/null +++ b/lib/bitmask_attributes/value_proxy.rb @@ -0,0 +1,74 @@ +module BitmaskAttributes + class ValueProxy < Array + + def initialize(record, attribute, &extension) + @record = record + @attribute = attribute + find_mapping + instance_eval(&extension) if extension + super(extract_values) + end + + # ========================= + # = OVERRIDE TO SERIALIZE = + # ========================= + + alias_method :orig_replace, :replace + %w(push << delete replace reject! select!).each do |override| + class_eval(<<-EOEVAL) + def #{override}(*args) + (super).tap do + updated! + end + end + EOEVAL + end + + def to_i + inject(0) { |memo, value| memo | @mapping[value] } + end + + private + + def validate! + each do |value| + if @mapping.key? value + true + else + raise ArgumentError, "Unsupported value for `#{@attribute}': #{value.inspect}" + end + end + end + + def symbolize! + orig_replace(map(&:to_sym)) + end + + def updated! + validate! + symbolize! + uniq! + serialize! + end + + def serialize! + @record.send(:write_attribute, @attribute, to_i) + end + + def extract_values + stored = [@record.send(:read_attribute, @attribute) || 0, 0].max + @mapping.inject([]) do |values, (value, bitmask)| + values.tap do + values << value.to_sym if (stored & bitmask > 0) + end + end + end + + def find_mapping + unless (@mapping = @record.class.bitmasks[@attribute]) + raise ArgumentError, "Could not find mapping for bitmask attribute :#{@attribute}" + end + end + + end +end diff --git a/lib/bitmask_attributes/version.rb b/lib/bitmask_attributes/version.rb new file mode 100644 index 0000000..c01b03c --- /dev/null +++ b/lib/bitmask_attributes/version.rb @@ -0,0 +1,3 @@ +module BitmaskAttributes + VERSION = "0.2.2" +end diff --git a/rails/init.rb b/rails/init.rb deleted file mode 100644 index c2f71d7..0000000 --- a/rails/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -ActiveRecord::Base.instance_eval do - include BitmaskAttribute -end \ No newline at end of file diff --git a/test/bitmask_attribute_test.rb b/test/bitmask_attributes_test.rb similarity index 84% rename from test/bitmask_attribute_test.rb rename to test/bitmask_attributes_test.rb index 3303bfc..1545645 100644 --- a/test/bitmask_attribute_test.rb +++ b/test/bitmask_attributes_test.rb @@ -1,14 +1,17 @@ require 'test_helper' -class BitmaskAttributeTest < Test::Unit::TestCase +class BitmaskAttributesTest < ActiveSupport::TestCase context "Campaign" do - teardown do Company.destroy_all Campaign.destroy_all end + should "return all defined values of a given bitmask attribute" do + assert_equal Campaign.values_for_medium, [:web, :print, :email, :phone] + end + should "can assign single value to bitmask" do assert_stored Campaign.new(:medium => :web), :web end @@ -31,7 +34,10 @@ class BitmaskAttributeTest < Test::Unit::TestCase assert_stored campaign, :web, :print, :phone campaign.medium << :phone assert_stored campaign, :web, :print, :phone + campaign.medium << "phone" + assert_stored campaign, :web, :print, :phone assert_equal 1, campaign.medium.select { |value| value == :phone }.size + assert_equal 0, campaign.medium.select { |value| value == "phone" }.size end should "can assign new values at once to bitmask" do @@ -98,11 +104,9 @@ class BitmaskAttributeTest < Test::Unit::TestCase end context "checking" do - setup { @campaign = Campaign.new(:medium => [:web, :print]) } context "for a single value" do - should "be supported by an attribute_for_value convenience method" do assert @campaign.medium_for_web? assert @campaign.medium_for_print? @@ -114,22 +118,17 @@ class BitmaskAttributeTest < Test::Unit::TestCase assert @campaign.medium?(:print) assert !@campaign.medium?(:email) end - end context "for multiple values" do - should "be supported by the simple predicate method" do assert @campaign.medium?(:web, :print) assert !@campaign.medium?(:web, :email) end - end - end context "named scopes" do - setup do @company = Company.create(:name => "Test Co, Intl.") @campaign1 = @company.campaigns.create :medium => [:web, :print] @@ -145,6 +144,10 @@ class BitmaskAttributeTest < Test::Unit::TestCase assert_equal [@campaign1], @company.campaigns.with_medium(:print) end + should "support retrieval by any matching value (OR)" do + assert_equal [@campaign1, @campaign3], @company.campaigns.with_any_medium(:print, :email) + end + should "support retrieval by all matching values" do assert_equal [@campaign1], @company.campaigns.with_medium(:web, :print) assert_equal [@campaign3], @company.campaigns.with_medium(:web, :email) @@ -153,16 +156,13 @@ class BitmaskAttributeTest < Test::Unit::TestCase should "support retrieval for no values" do assert_equal [@campaign2], @company.campaigns.without_medium end - end should "can check if at least one value is set" do campaign = Campaign.new(:medium => [:web, :print]) - assert campaign.medium? campaign = Campaign.new - assert !campaign.medium? end @@ -175,7 +175,7 @@ class BitmaskAttributeTest < Test::Unit::TestCase Campaign.medium_for_print ) - assert_equal Campaign.medium_for_print, Campaign.medium_for_print.medium_for_web + assert_equal Campaign.medium_for_print.first, Campaign.medium_for_print.medium_for_web.first assert_equal [], Campaign.medium_for_email assert_equal [], Campaign.medium_for_web.medium_for_email @@ -193,24 +193,22 @@ class BitmaskAttributeTest < Test::Unit::TestCase assert_equal [campaign], Campaign.no_medium end - ####### - private - ####### - def assert_unsupported(&block) - assert_raises(ArgumentError, &block) - end + private - def assert_stored(record, *values) - values.each do |value| - assert record.medium.any? { |v| v.to_s == value.to_s }, "Values #{record.medium.inspect} does not include #{value.inspect}" + def assert_unsupported(&block) + assert_raises(ArgumentError, &block) end - full_mask = values.inject(0) do |mask, value| - mask | Campaign.bitmasks[:medium][value] + + def assert_stored(record, *values) + values.each do |value| + assert record.medium.any? { |v| v.to_s == value.to_s }, "Values #{record.medium.inspect} does not include #{value.inspect}" + end + full_mask = values.inject(0) do |mask, value| + mask | Campaign.bitmasks[:medium][value] + end + assert_equal full_mask, record.medium.to_i end - assert_equal full_mask, record.medium.to_i - end end - -end +end \ No newline at end of file diff --git a/test/support/helpers.rb b/test/support/helpers.rb new file mode 100644 index 0000000..54eb8c1 --- /dev/null +++ b/test/support/helpers.rb @@ -0,0 +1,17 @@ +class ActiveSupport::TestCase + + def assert_unsupported(&block) + assert_raises(ArgumentError, &block) + end + + def assert_stored(record, *values) + values.each do |value| + assert record.medium.any? { |v| v.to_s == value.to_s }, "Values #{record.medium.inspect} does not include #{value.inspect}" + end + full_mask = values.inject(0) do |mask, value| + mask | Campaign.bitmasks[:medium][value] + end + assert_equal full_mask, record.medium.to_i + end + +end \ No newline at end of file diff --git a/test/support/models.rb b/test/support/models.rb new file mode 100644 index 0000000..73c0fd5 --- /dev/null +++ b/test/support/models.rb @@ -0,0 +1,26 @@ +ActiveRecord::Schema.define do + create_table :campaigns do |t| + t.integer :company_id + t.integer :medium, :misc, :Legacy + end + create_table :companies do |t| + t.string :name + end +end + + +class Company < ActiveRecord::Base + has_many :campaigns +end + +# Pseudo model for testing purposes +class Campaign < ActiveRecord::Base + belongs_to :company + bitmask :medium, :as => [:web, :print, :email, :phone] + bitmask :misc, :as => %w(some useless values) do + def worked? + true + end + end + bitmask :Legacy, :as => [:upper, :case] +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index e57434a..ccc54d0 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,66 +1,21 @@ -require 'rubygems' +require "rubygems" +require 'bundler/setup' + require 'test/unit' +begin; require 'turn'; rescue LoadError; end require 'shoulda' -begin - require 'redgreen' -rescue LoadError -end -require 'active_support' require 'active_record' -$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) -$LOAD_PATH.unshift(File.dirname(__FILE__)) -require 'bitmask-attribute' -require File.dirname(__FILE__) + '/../rails/init' +$:.unshift File.expand_path("../../lib", __FILE__) +require 'bitmask_attributes' -# ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => ':memory:' ) -ActiveRecord::Schema.define do - create_table :campaigns do |t| - t.integer :company_id - t.integer :medium, :misc, :Legacy - end - create_table :companies do |t| - t.string :name - end -end - -class Company < ActiveRecord::Base - has_many :campaigns -end - -# Pseudo model for testing purposes -class Campaign < ActiveRecord::Base - belongs_to :company - bitmask :medium, :as => [:web, :print, :email, :phone] - bitmask :misc, :as => %w(some useless values) do - def worked? - true - end - end - bitmask :Legacy, :as => [:upper, :case] -end -class Test::Unit::TestCase - - def assert_unsupported(&block) - assert_raises(ArgumentError, &block) - end - - def assert_stored(record, *values) - values.each do |value| - assert record.medium.any? { |v| v.to_s == value.to_s }, "Values #{record.medium.inspect} does not include #{value.inspect}" - end - full_mask = values.inject(0) do |mask, value| - mask | Campaign.bitmasks[:medium][value] - end - assert_equal full_mask, record.medium.to_i - end - -end +# Load support files +Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }