bitmask_attributes 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.markdown
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,7 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ \#*
7
+ .#*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2@bitmask_attributes --create --install
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ if RUBY_VERSION < '1.9'
6
+ gem "ruby-debug", ">= 0.10.3"
7
+ end
8
+
9
+ gem 'shoulda'
10
+ gem 'sqlite3'
11
+ gem 'turn'
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bitmask_attributes (0.1.0)
5
+ activerecord (~> 3.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ activemodel (3.0.9)
11
+ activesupport (= 3.0.9)
12
+ builder (~> 2.1.2)
13
+ i18n (~> 0.5.0)
14
+ activerecord (3.0.9)
15
+ activemodel (= 3.0.9)
16
+ activesupport (= 3.0.9)
17
+ arel (~> 2.0.10)
18
+ tzinfo (~> 0.3.23)
19
+ activesupport (3.0.9)
20
+ ansi (1.3.0)
21
+ arel (2.0.10)
22
+ builder (2.1.2)
23
+ i18n (0.5.0)
24
+ shoulda (2.11.3)
25
+ sqlite3 (1.3.3)
26
+ turn (0.8.2)
27
+ ansi (>= 1.2.2)
28
+ tzinfo (0.3.29)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ bitmask_attributes!
35
+ shoulda
36
+ sqlite3
37
+ turn
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007-2009 Bruce Williams & 2011 Joel Moss
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,129 @@
1
+ BitmaskAttributes
2
+ =================
3
+
4
+ Transparent manipulation of bitmask attributes for ActiveRecord, based on
5
+ the bitmask-attribute gem, which has been dormant since 2009. This updated
6
+ gem work with Rails 3 and up (including Rails 3.1).
7
+
8
+ Installation
9
+ ------------
10
+
11
+ The best way to install is with RubyGems:
12
+
13
+ $ [sudo] gem install bitmask_attributes
14
+
15
+ Or better still, just add it to your Gemfile:
16
+
17
+ gem 'bitmask_attributes'
18
+
19
+ Example
20
+ -------
21
+
22
+ Simply declare an existing integer column as a bitmask with its possible
23
+ values.
24
+
25
+ class User < ActiveRecord::Base
26
+ bitmask :roles, :as => [:writer, :publisher, :editor, :proofreader]
27
+ end
28
+
29
+ You can then modify the column using the declared values without resorting
30
+ to manual bitmasks.
31
+
32
+ user = User.create(:name => "Bruce", :roles => [:publisher, :editor])
33
+ user.roles
34
+ # => [:publisher, :editor]
35
+ user.roles << :writer
36
+ user.roles
37
+ # => [:publisher, :editor, :writer]
38
+
39
+ It's easy to find out if a record has a given value:
40
+
41
+ user.roles?(:editor)
42
+ # => true
43
+
44
+ You can check for multiple values (uses an `and` boolean):
45
+
46
+ user.roles?(:editor, :publisher)
47
+ # => true
48
+ user.roles?(:editor, :proofreader)
49
+ # => false
50
+
51
+ Or, just check if any values are present:
52
+
53
+ user.roles?
54
+ # => true
55
+
56
+ Named Scopes
57
+ ------------
58
+
59
+ A couple useful named scopes are also generated when you use
60
+ `bitmask`:
61
+
62
+ User.with_roles
63
+ # => (all users with roles)
64
+ User.with_roles(:editor)
65
+ # => (all editors)
66
+ User.with_roles(:editor, :writer)
67
+ # => (all users who are BOTH editors and writers)
68
+
69
+ Later we'll support an `or` boolean; for now, do something like:
70
+
71
+ User.with_roles(:editor) + User.with_roles(:writer)
72
+ # => (all users who are EITHER editors and writers)
73
+
74
+ Find records without any bitmask set:
75
+
76
+ User.without_roles
77
+ # => (all users without a role)
78
+
79
+ Later we'll support finding records without a specific bitmask.
80
+
81
+ Adding Methods
82
+ --------------
83
+
84
+ You can add your own methods to the bitmasked attributes (similar to
85
+ named scopes):
86
+
87
+ bitmask :other_attribute, :as => [:value1, :value2] do
88
+ def worked?
89
+ true
90
+ end
91
+ end
92
+
93
+ user = User.first
94
+ user.other_attribute.worked?
95
+ # => true
96
+
97
+
98
+ Warning: Modifying possible values
99
+ ----------------------------------
100
+
101
+ IMPORTANT: Once you have data using a bitmask, don't change the order
102
+ of the values, remove any values, or insert any new values in the `:as`
103
+ array anywhere except at the end. You won't like the results.
104
+
105
+ Contributing
106
+ ------------
107
+
108
+ 1. Fork it.
109
+ 2. Create a branch (`git checkout -b new-feature`)
110
+ 3. Make your changes
111
+ 4. Run the tests (`bundle install` then `bundle exec rake`)
112
+ 5. Commit your changes (`git commit -am "Created new feature"`)
113
+ 6. Push to the branch (`git push origin new-feature`)
114
+ 7. Create a [Pull Request](http://help.github.com/pull-requests/) from your branch.
115
+ 8. Promote it. Get others to drop in and +1 it.
116
+
117
+ Credits
118
+ -------
119
+
120
+ Thanks to [Bruce Williams](https://github.com/bruce) and the following contributors
121
+ of the bitmask-attribute plugin:
122
+
123
+ * [Jason L Perry](http://github.com/ambethia)
124
+ * [Nicolas Fouché](http://github.com/nfo)
125
+
126
+ Copyright
127
+ ---------
128
+
129
+ Copyright (c) 2007-2009 Bruce Williams & 2011 Joel Moss. See LICENSE for details.
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ require "bundler"
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ require 'rake/testtask'
7
+
8
+ desc 'Default: run unit tests.'
9
+ task :default => :test
10
+
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = true
16
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "bitmask_attributes/version"
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "bitmask_attributes"
7
+ gem.summary = %Q{Simple bitmask attribute support for ActiveRecord}
8
+ gem.description = %Q{Simple bitmask attribute support for ActiveRecord}
9
+ gem.email = "joel@developwithstyle.com"
10
+ gem.homepage = "http://github.com/joelmoss/bitmask_attributes"
11
+ gem.authors = ['Joel Moss']
12
+
13
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ gem.files = `git ls-files`.split("\n")
15
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ gem.require_paths = ['lib']
17
+ gem.version = BitmaskAttributes::VERSION
18
+
19
+ gem.add_dependency 'activerecord', '~> 3.0'
20
+ end
@@ -0,0 +1,26 @@
1
+ require 'bitmask_attributes/definition'
2
+ require 'bitmask_attributes/value_proxy'
3
+
4
+ module BitmaskAttributes
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def bitmask(attribute, options={}, &extension)
9
+ unless options[:as] && options[:as].kind_of?(Array)
10
+ raise ArgumentError, "Must provide an Array :as option"
11
+ end
12
+ bitmask_definitions[attribute] = Definition.new(attribute, options[:as].to_a, &extension)
13
+ bitmask_definitions[attribute].install_on(self)
14
+ end
15
+
16
+ def bitmask_definitions
17
+ @bitmask_definitions ||= {}
18
+ end
19
+
20
+ def bitmasks
21
+ @bitmasks ||= {}
22
+ end
23
+ end
24
+ end
25
+
26
+ ActiveRecord::Base.send :include, BitmaskAttributes
@@ -0,0 +1,122 @@
1
+ module BitmaskAttributes
2
+ class Definition
3
+ attr_reader :attribute, :values, :extension
4
+
5
+ def initialize(attribute, values=[], &extension)
6
+ @attribute = attribute
7
+ @values = values
8
+ @extension = extension
9
+ end
10
+
11
+ def install_on(model)
12
+ validate_for model
13
+ generate_bitmasks_on model
14
+ override model
15
+ create_convenience_class_method_on model
16
+ create_convenience_instance_methods_on model
17
+ create_scopes_on model
18
+ end
19
+
20
+ private
21
+
22
+ def validate_for(model)
23
+ # The model cannot be validated if it is preloaded and the attribute/column is not in the
24
+ # database (the migration has not been run). This usually
25
+ # occurs in the 'test' and 'production' environments.
26
+ return if defined?(Rails) && Rails.configuration.cache_classes
27
+
28
+ unless model.columns.detect { |col| col.name == attribute.to_s }
29
+ raise ArgumentError, "`#{attribute}' is not an attribute of `#{model}'"
30
+ end
31
+ end
32
+
33
+ def generate_bitmasks_on(model)
34
+ model.bitmasks[attribute] = HashWithIndifferentAccess.new.tap do |mapping|
35
+ values.each_with_index do |value, index|
36
+ mapping[value] = 0b1 << index
37
+ end
38
+ end
39
+ end
40
+
41
+ def override(model)
42
+ override_getter_on(model)
43
+ override_setter_on(model)
44
+ end
45
+
46
+ def override_getter_on(model)
47
+ model.class_eval %(
48
+ def #{attribute}
49
+ @#{attribute} ||= BitmaskAttributes::ValueProxy.new(self, :#{attribute}, &self.class.bitmask_definitions[:#{attribute}].extension)
50
+ end
51
+ )
52
+ end
53
+
54
+ def override_setter_on(model)
55
+ model.class_eval %(
56
+ def #{attribute}=(raw_value)
57
+ values = raw_value.kind_of?(Array) ? raw_value : [raw_value]
58
+ self.#{attribute}.replace(values.reject(&:blank?))
59
+ end
60
+ )
61
+ end
62
+
63
+ def create_convenience_class_method_on(model)
64
+ model.class_eval %(
65
+ def self.bitmask_for_#{attribute}(*values)
66
+ values.inject(0) do |bitmask, value|
67
+ unless (bit = bitmasks[:#{attribute}][value])
68
+ raise ArgumentError, "Unsupported value for #{attribute}: \#{value.inspect}"
69
+ end
70
+ bitmask | bit
71
+ end
72
+ end
73
+ )
74
+ end
75
+
76
+ def create_convenience_instance_methods_on(model)
77
+ values.each do |value|
78
+ model.class_eval %(
79
+ def #{attribute}_for_#{value}?
80
+ self.#{attribute}?(:#{value})
81
+ end
82
+ )
83
+ end
84
+ model.class_eval %(
85
+ def #{attribute}?(*values)
86
+ if !values.blank?
87
+ values.all? do |value|
88
+ self.#{attribute}.include?(value)
89
+ end
90
+ else
91
+ self.#{attribute}.present?
92
+ end
93
+ end
94
+ )
95
+ end
96
+
97
+ def create_scopes_on(model)
98
+ model.class_eval %(
99
+ scope :with_#{attribute},
100
+ proc { |*values|
101
+ if values.blank?
102
+ {:conditions => '#{attribute} > 0 OR #{attribute} IS NOT NULL'}
103
+ else
104
+ sets = values.map do |value|
105
+ mask = #{model}.bitmask_for_#{attribute}(value)
106
+ "#{attribute} & \#{mask} <> 0"
107
+ end
108
+ {:conditions => sets.join(' AND ')}
109
+ end
110
+ }
111
+ scope :without_#{attribute}, :conditions => "#{attribute} == 0 OR #{attribute} IS NULL"
112
+ scope :no_#{attribute}, :conditions => "#{attribute} == 0 OR #{attribute} IS NULL"
113
+ )
114
+ values.each do |value|
115
+ model.class_eval %(
116
+ scope :#{attribute}_for_#{value},
117
+ :conditions => ['#{attribute} & ? <> 0', #{model}.bitmask_for_#{attribute}(:#{value})]
118
+ )
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,68 @@
1
+ module BitmaskAttributes
2
+ class ValueProxy < Array
3
+
4
+ def initialize(record, attribute, &extension)
5
+ @record = record
6
+ @attribute = attribute
7
+ find_mapping
8
+ instance_eval(&extension) if extension
9
+ super(extract_values)
10
+ end
11
+
12
+ # =========================
13
+ # = OVERRIDE TO SERIALIZE =
14
+ # =========================
15
+
16
+ %w(push << delete replace reject! select!).each do |override|
17
+ class_eval(<<-EOEVAL)
18
+ def #{override}(*args)
19
+ (super).tap do
20
+ updated!
21
+ end
22
+ end
23
+ EOEVAL
24
+ end
25
+
26
+ def to_i
27
+ inject(0) { |memo, value| memo | @mapping[value] }
28
+ end
29
+
30
+ private
31
+
32
+ def validate!
33
+ each do |value|
34
+ if @mapping.key? value
35
+ true
36
+ else
37
+ raise ArgumentError, "Unsupported value for `#{@attribute}': #{value.inspect}"
38
+ end
39
+ end
40
+ end
41
+
42
+ def updated!
43
+ validate!
44
+ uniq!
45
+ serialize!
46
+ end
47
+
48
+ def serialize!
49
+ @record.send(:write_attribute, @attribute, to_i)
50
+ end
51
+
52
+ def extract_values
53
+ stored = [@record.send(:read_attribute, @attribute) || 0, 0].max
54
+ @mapping.inject([]) do |values, (value, bitmask)|
55
+ values.tap do
56
+ values << value.to_sym if (stored & bitmask > 0)
57
+ end
58
+ end
59
+ end
60
+
61
+ def find_mapping
62
+ unless (@mapping = @record.class.bitmasks[@attribute])
63
+ raise ArgumentError, "Could not find mapping for bitmask attribute :#{@attribute}"
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ module BitmaskAttributes
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,205 @@
1
+ require 'test_helper'
2
+
3
+ class BitmaskAttributesTest < ActiveSupport::TestCase
4
+
5
+ context "Campaign" do
6
+ teardown do
7
+ Company.destroy_all
8
+ Campaign.destroy_all
9
+ end
10
+
11
+ should "can assign single value to bitmask" do
12
+ assert_stored Campaign.new(:medium => :web), :web
13
+ end
14
+
15
+ should "can assign multiple values to bitmask" do
16
+ assert_stored Campaign.new(:medium => [:web, :print]), :web, :print
17
+ end
18
+
19
+ should "can add single value to bitmask" do
20
+ campaign = Campaign.new(:medium => [:web, :print])
21
+ assert_stored campaign, :web, :print
22
+ campaign.medium << :phone
23
+ assert_stored campaign, :web, :print, :phone
24
+ end
25
+
26
+ should "ignores duplicate values added to bitmask" do
27
+ campaign = Campaign.new(:medium => [:web, :print])
28
+ assert_stored campaign, :web, :print
29
+ campaign.medium << :phone
30
+ assert_stored campaign, :web, :print, :phone
31
+ campaign.medium << :phone
32
+ assert_stored campaign, :web, :print, :phone
33
+ assert_equal 1, campaign.medium.select { |value| value == :phone }.size
34
+ end
35
+
36
+ should "can assign new values at once to bitmask" do
37
+ campaign = Campaign.new(:medium => [:web, :print])
38
+ assert_stored campaign, :web, :print
39
+ campaign.medium = [:phone, :email]
40
+ assert_stored campaign, :phone, :email
41
+ end
42
+
43
+ should "can save bitmask to db and retrieve values transparently" do
44
+ campaign = Campaign.new(:medium => [:web, :print])
45
+ assert_stored campaign, :web, :print
46
+ assert campaign.save
47
+ assert_stored Campaign.find(campaign.id), :web, :print
48
+ end
49
+
50
+ should "can add custom behavor to value proxies during bitmask definition" do
51
+ campaign = Campaign.new(:medium => [:web, :print])
52
+ assert_raises NoMethodError do
53
+ campaign.medium.worked?
54
+ end
55
+ assert_nothing_raised do
56
+ campaign.misc.worked?
57
+ end
58
+ assert campaign.misc.worked?
59
+ end
60
+
61
+ should "cannot use unsupported values" do
62
+ assert_unsupported { Campaign.new(:medium => [:web, :print, :this_will_fail]) }
63
+ campaign = Campaign.new(:medium => :web)
64
+ assert_unsupported { campaign.medium << :this_will_fail_also }
65
+ assert_unsupported { campaign.medium = [:so_will_this] }
66
+ end
67
+
68
+ should "can determine bitmasks using convenience method" do
69
+ assert Campaign.bitmask_for_medium(:web, :print)
70
+ assert_equal(
71
+ Campaign.bitmasks[:medium][:web] | Campaign.bitmasks[:medium][:print],
72
+ Campaign.bitmask_for_medium(:web, :print)
73
+ )
74
+ end
75
+
76
+ should "assert use of unknown value in convenience method will result in exception" do
77
+ assert_unsupported { Campaign.bitmask_for_medium(:web, :and_this_isnt_valid) }
78
+ end
79
+
80
+ should "hash of values is with indifferent access" do
81
+ string_bit = nil
82
+ assert_nothing_raised do
83
+ assert (string_bit = Campaign.bitmask_for_medium('web', 'print'))
84
+ end
85
+ assert_equal Campaign.bitmask_for_medium(:web, :print), string_bit
86
+ end
87
+
88
+ should "save bitmask with non-standard attribute names" do
89
+ campaign = Campaign.new(:Legacy => [:upper, :case])
90
+ assert campaign.save
91
+ assert_equal [:upper, :case], Campaign.find(campaign.id).Legacy
92
+ end
93
+
94
+ should "ignore blanks fed as values" do
95
+ campaign = Campaign.new(:medium => [:web, :print, ''])
96
+ assert_stored campaign, :web, :print
97
+ end
98
+
99
+ context "checking" do
100
+ setup { @campaign = Campaign.new(:medium => [:web, :print]) }
101
+
102
+ context "for a single value" do
103
+ should "be supported by an attribute_for_value convenience method" do
104
+ assert @campaign.medium_for_web?
105
+ assert @campaign.medium_for_print?
106
+ assert !@campaign.medium_for_email?
107
+ end
108
+
109
+ should "be supported by the simple predicate method" do
110
+ assert @campaign.medium?(:web)
111
+ assert @campaign.medium?(:print)
112
+ assert !@campaign.medium?(:email)
113
+ end
114
+ end
115
+
116
+ context "for multiple values" do
117
+ should "be supported by the simple predicate method" do
118
+ assert @campaign.medium?(:web, :print)
119
+ assert !@campaign.medium?(:web, :email)
120
+ end
121
+ end
122
+ end
123
+
124
+ context "named scopes" do
125
+ setup do
126
+ @company = Company.create(:name => "Test Co, Intl.")
127
+ @campaign1 = @company.campaigns.create :medium => [:web, :print]
128
+ @campaign2 = @company.campaigns.create
129
+ @campaign3 = @company.campaigns.create :medium => [:web, :email]
130
+ end
131
+
132
+ should "support retrieval by any value" do
133
+ assert_equal [@campaign1, @campaign3], @company.campaigns.with_medium
134
+ end
135
+
136
+ should "support retrieval by one matching value" do
137
+ assert_equal [@campaign1], @company.campaigns.with_medium(:print)
138
+ end
139
+
140
+ should "support retrieval by all matching values" do
141
+ assert_equal [@campaign1], @company.campaigns.with_medium(:web, :print)
142
+ assert_equal [@campaign3], @company.campaigns.with_medium(:web, :email)
143
+ end
144
+
145
+ should "support retrieval for no values" do
146
+ assert_equal [@campaign2], @company.campaigns.without_medium
147
+ end
148
+ end
149
+
150
+ should "can check if at least one value is set" do
151
+ campaign = Campaign.new(:medium => [:web, :print])
152
+
153
+ assert campaign.medium?
154
+
155
+ campaign = Campaign.new
156
+
157
+ assert !campaign.medium?
158
+ end
159
+
160
+ should "find by bitmask values" do
161
+ campaign = Campaign.new(:medium => [:web, :print])
162
+ assert campaign.save
163
+
164
+ assert_equal(
165
+ Campaign.find(:all, :conditions => ['medium & ? <> 0', Campaign.bitmask_for_medium(:print)]),
166
+ Campaign.medium_for_print
167
+ )
168
+
169
+ assert_equal Campaign.medium_for_print.first, Campaign.medium_for_print.medium_for_web.first
170
+
171
+ assert_equal [], Campaign.medium_for_email
172
+ assert_equal [], Campaign.medium_for_web.medium_for_email
173
+ end
174
+
175
+ should "find no values" do
176
+ campaign = Campaign.create(:medium => [:web, :print])
177
+ assert campaign.save
178
+
179
+ assert_equal [], Campaign.no_medium
180
+
181
+ campaign.medium = []
182
+ assert campaign.save
183
+
184
+ assert_equal [campaign], Campaign.no_medium
185
+ end
186
+
187
+
188
+ private
189
+
190
+ def assert_unsupported(&block)
191
+ assert_raises(ArgumentError, &block)
192
+ end
193
+
194
+ def assert_stored(record, *values)
195
+ values.each do |value|
196
+ assert record.medium.any? { |v| v.to_s == value.to_s }, "Values #{record.medium.inspect} does not include #{value.inspect}"
197
+ end
198
+ full_mask = values.inject(0) do |mask, value|
199
+ mask | Campaign.bitmasks[:medium][value]
200
+ end
201
+ assert_equal full_mask, record.medium.to_i
202
+ end
203
+
204
+ end
205
+ end
@@ -0,0 +1,17 @@
1
+ class ActiveSupport::TestCase
2
+
3
+ def assert_unsupported(&block)
4
+ assert_raises(ArgumentError, &block)
5
+ end
6
+
7
+ def assert_stored(record, *values)
8
+ values.each do |value|
9
+ assert record.medium.any? { |v| v.to_s == value.to_s }, "Values #{record.medium.inspect} does not include #{value.inspect}"
10
+ end
11
+ full_mask = values.inject(0) do |mask, value|
12
+ mask | Campaign.bitmasks[:medium][value]
13
+ end
14
+ assert_equal full_mask, record.medium.to_i
15
+ end
16
+
17
+ end
@@ -0,0 +1,26 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :campaigns do |t|
3
+ t.integer :company_id
4
+ t.integer :medium, :misc, :Legacy
5
+ end
6
+ create_table :companies do |t|
7
+ t.string :name
8
+ end
9
+ end
10
+
11
+
12
+ class Company < ActiveRecord::Base
13
+ has_many :campaigns
14
+ end
15
+
16
+ # Pseudo model for testing purposes
17
+ class Campaign < ActiveRecord::Base
18
+ belongs_to :company
19
+ bitmask :medium, :as => [:web, :print, :email, :phone]
20
+ bitmask :misc, :as => %w(some useless values) do
21
+ def worked?
22
+ true
23
+ end
24
+ end
25
+ bitmask :Legacy, :as => [:upper, :case]
26
+ end
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require 'bundler/setup'
3
+
4
+ require 'test/unit'
5
+ begin; require 'turn'; rescue LoadError; end
6
+ require 'shoulda'
7
+
8
+ require 'active_record'
9
+
10
+ $:.unshift File.expand_path("../../lib", __FILE__)
11
+ require 'bitmask_attributes'
12
+
13
+
14
+ ActiveRecord::Base.establish_connection(
15
+ :adapter => 'sqlite3',
16
+ :database => ':memory:'
17
+ )
18
+
19
+
20
+ # Load support files
21
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bitmask_attributes
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Joel Moss
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-04 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activerecord
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: "3.0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ description: Simple bitmask attribute support for ActiveRecord
28
+ email: joel@developwithstyle.com
29
+ executables: []
30
+
31
+ extensions: []
32
+
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - .document
37
+ - .gitignore
38
+ - .rvmrc
39
+ - Gemfile
40
+ - Gemfile.lock
41
+ - LICENSE
42
+ - README.md
43
+ - Rakefile
44
+ - VERSION
45
+ - bitmask_attributes.gemspec
46
+ - lib/bitmask_attributes.rb
47
+ - lib/bitmask_attributes/definition.rb
48
+ - lib/bitmask_attributes/value_proxy.rb
49
+ - lib/bitmask_attributes/version.rb
50
+ - test/bitmask_attributes_test.rb
51
+ - test/support/helpers.rb
52
+ - test/support/models.rb
53
+ - test/test_helper.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/joelmoss/bitmask_attributes
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.3.9.1
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Simple bitmask attribute support for ActiveRecord
82
+ test_files:
83
+ - test/bitmask_attributes_test.rb
84
+ - test/support/helpers.rb
85
+ - test/support/models.rb
86
+ - test/test_helper.rb