r3trofitted-bitmask-attribute 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.markdown
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ \#*
7
+ .#*
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007-2009 Bruce Williams
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.
data/README.markdown ADDED
@@ -0,0 +1,113 @@
1
+ bitmask-attribute
2
+ =================
3
+
4
+ Transparent manipulation of bitmask attributes.
5
+
6
+ Example
7
+ -------
8
+
9
+ Simply declare an existing integer column as a bitmask with its possible
10
+ values.
11
+
12
+ class User < ActiveRecord::Base
13
+ bitmask :roles, :as => [:writer, :publisher, :editor, :proofreader]
14
+ end
15
+
16
+ You can then modify the column using the declared values without resorting
17
+ to manual bitmasks.
18
+
19
+ user = User.create(:name => "Bruce", :roles => [:publisher, :editor])
20
+ user.roles
21
+ # => [:publisher, :editor]
22
+ user.roles << :writer
23
+ user.roles
24
+ # => [:publisher, :editor, :writer]
25
+
26
+ It's easy to find out if a record has a given value:
27
+
28
+ user.roles?(:editor)
29
+ # => true
30
+
31
+ You can check for multiple values (uses an `and` boolean):
32
+
33
+ user.roles?(:editor, :publisher)
34
+ # => true
35
+ user.roles?(:editor, :proofreader)
36
+ # => false
37
+
38
+ Or, just check if any values are present:
39
+
40
+ user.roles?
41
+ # => true
42
+
43
+ Named Scopes
44
+ ------------
45
+
46
+ A couple useful named scopes are also generated when you use
47
+ `bitmask`:
48
+
49
+ User.with_roles
50
+ # => (all users with roles)
51
+ User.with_roles(:editor)
52
+ # => (all editors)
53
+ User.with_roles(:editor, :writer)
54
+ # => (all users who are BOTH editors and writers)
55
+
56
+ Later we'll support an `or` boolean; for now, do something like:
57
+
58
+ User.with_roles(:editor) + User.with_roles(:writer)
59
+ # => (all users who are EITHER editors and writers)
60
+
61
+ Find records without any bitmask set:
62
+
63
+ User.without_roles
64
+ # => (all users without a role)
65
+
66
+ Later we'll support finding records without a specific bitmask.
67
+
68
+ Adding Methods
69
+ --------------
70
+
71
+ You can add your own methods to the bitmasked attributes (similar to
72
+ named scopes):
73
+
74
+ bitmask :other_attribute, :as => [:value1, :value2] do
75
+ def worked?
76
+ true
77
+ end
78
+ end
79
+
80
+ user = User.first
81
+ user.other_attribute.worked?
82
+ # => true
83
+
84
+
85
+ Warning: Modifying possible values
86
+ ----------------------------------
87
+
88
+ IMPORTANT: Once you have data using a bitmask, don't change the order
89
+ of the values, remove any values, or insert any new values in the `:as`
90
+ array anywhere except at the end. You won't like the results.
91
+
92
+ Contributing and reporting issues
93
+ ---------------------------------
94
+
95
+ Please feel free to fork & contribute fixes via GitHub pull requests.
96
+ The official repository for this project is
97
+ http://github.com/bruce/bitmask-attribute
98
+
99
+ Issues can be reported at
100
+ http://github.com/bruce/bitmask-attribute/issues
101
+
102
+ Credits
103
+ -------
104
+
105
+ Thanks to the following contributors:
106
+
107
+ * [Jason L Perry](http://github.com/ambethia)
108
+ * [Nicolas Fouché](http://github.com/nfo)
109
+
110
+ Copyright
111
+ ---------
112
+
113
+ Copyright (c) 2007-2009 Bruce Williams. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "bitmask-attribute"
8
+ gem.summary = %Q{Simple bitmask attribute support for ActiveRecord}
9
+ gem.email = "bruce@codefluency.com"
10
+ gem.homepage = "http://github.com/bruce/bitmask-attribute"
11
+ gem.authors = ["Bruce Williams"]
12
+ gem.add_dependency 'activerecord'
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/*_test.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+
41
+ task :default => :test
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ if File.exist?('VERSION.yml')
46
+ config = YAML.load(File.read('VERSION.yml'))
47
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
48
+ else
49
+ version = ""
50
+ end
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "bitmask-attribute #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
57
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
@@ -0,0 +1,2 @@
1
+ # Stub for dash-style requires
2
+ require File.dirname(__FILE__) << "/bitmask_attribute"
@@ -0,0 +1,77 @@
1
+ module BitmaskAttribute
2
+
3
+ class ValueProxy < Array
4
+
5
+ def initialize(record, attribute, &extension)
6
+ @record = record
7
+ @attribute = attribute
8
+ find_mapping
9
+ instance_eval(&extension) if extension
10
+ super(extract_values)
11
+ end
12
+
13
+ # =========================
14
+ # = OVERRIDE TO SERIALIZE =
15
+ # =========================
16
+
17
+ %w(push << delete replace reject! select!).each do |override|
18
+ class_eval(<<-EOEVAL)
19
+ def #{override}(*args)
20
+ returning(super) do
21
+ updated!
22
+ end
23
+ end
24
+ EOEVAL
25
+ end
26
+
27
+ def to_i
28
+ inject(0) { |memo, value| memo | @mapping[value] }
29
+ end
30
+
31
+ #######
32
+ private
33
+ #######
34
+
35
+ def validate!
36
+ each do |value|
37
+ if @mapping.key? value
38
+ true
39
+ else
40
+ raise ArgumentError, "Unsupported value for `#{@attribute}': #{value.inspect}"
41
+ end
42
+ end
43
+ end
44
+
45
+ def symbolize!
46
+ map!(&:to_sym)
47
+ end
48
+
49
+ def updated!
50
+ symbolize!
51
+ validate!
52
+ uniq!
53
+ serialize!
54
+ end
55
+
56
+ def serialize!
57
+ @record.send(:write_attribute, @attribute, to_i)
58
+ end
59
+
60
+ def extract_values
61
+ stored = [@record.send(:read_attribute, @attribute) || 0, 0].max
62
+ @mapping.inject([]) do |values, (value, bitmask)|
63
+ returning values do
64
+ values << value.to_sym if (stored & bitmask > 0)
65
+ end
66
+ end
67
+ end
68
+
69
+ def find_mapping
70
+ unless (@mapping = @record.class.bitmasks[@attribute])
71
+ raise ArgumentError, "Could not find mapping for bitmask attribute :#{@attribute}"
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,154 @@
1
+ require 'bitmask_attribute/value_proxy'
2
+
3
+ module BitmaskAttribute
4
+
5
+ class Definition
6
+
7
+ attr_reader :attribute, :values, :extension
8
+ def initialize(attribute, values=[], &extension)
9
+ @attribute = attribute
10
+ @values = values
11
+ @extension = extension
12
+ end
13
+
14
+ def install_on(model)
15
+ validate_for model
16
+ generate_bitmasks_on model
17
+ override model
18
+ create_convenience_class_method_on(model)
19
+ create_convenience_instance_methods_on(model)
20
+ create_named_scopes_on(model)
21
+ end
22
+
23
+ #######
24
+ private
25
+ #######
26
+
27
+ def validate_for(model)
28
+ # The model cannot be validated if it is preloaded and the attribute/column is not in the
29
+ # database (the migration has not been run). This usually
30
+ # occurs in the 'test' and 'production' environments.
31
+ return if defined?(Rails) && Rails.configuration.cache_classes
32
+
33
+ unless model.columns.detect { |col| col.name == attribute.to_s }
34
+ raise ArgumentError, "`#{attribute}' is not an attribute of `#{model}'"
35
+ end
36
+ end
37
+
38
+ def generate_bitmasks_on(model)
39
+ model.bitmasks[attribute] = returning HashWithIndifferentAccess.new do |mapping|
40
+ values.each_with_index do |value, index|
41
+ mapping[value] = 0b1 << index
42
+ end
43
+ end
44
+ end
45
+
46
+ def override(model)
47
+ override_getter_on(model)
48
+ override_setter_on(model)
49
+ end
50
+
51
+ def override_getter_on(model)
52
+ model.class_eval %(
53
+ def #{attribute}
54
+ @#{attribute} ||= BitmaskAttribute::ValueProxy.new(self, :#{attribute}, &self.class.bitmask_definitions[:#{attribute}].extension)
55
+ end
56
+ )
57
+ end
58
+
59
+ def override_setter_on(model)
60
+ model.class_eval %(
61
+ def #{attribute}=(raw_value)
62
+ values = raw_value.kind_of?(Array) ? raw_value : [raw_value]
63
+ self.#{attribute}.replace(values.reject(&:blank?))
64
+ end
65
+ )
66
+ end
67
+
68
+ def create_convenience_class_method_on(model)
69
+ model.class_eval %(
70
+ def self.bitmask_for_#{attribute}(*values)
71
+ values.inject(0) do |bitmask, value|
72
+ unless (bit = bitmasks[:#{attribute}][value])
73
+ raise ArgumentError, "Unsupported value for #{attribute}: \#{value.inspect}"
74
+ end
75
+ bitmask | bit
76
+ end
77
+ end
78
+ )
79
+ end
80
+
81
+
82
+ def create_convenience_instance_methods_on(model)
83
+ values.each do |value|
84
+ model.class_eval %(
85
+ def #{attribute}_for_#{value}?
86
+ self.#{attribute}?(:#{value})
87
+ end
88
+ )
89
+ end
90
+ model.class_eval %(
91
+ def #{attribute}?(*values)
92
+ if !values.blank?
93
+ values.all? do |value|
94
+ self.#{attribute}.include?(value)
95
+ end
96
+ else
97
+ self.#{attribute}.present?
98
+ end
99
+ end
100
+ )
101
+ end
102
+
103
+ def create_named_scopes_on(model)
104
+ model.class_eval %(
105
+ named_scope :with_#{attribute},
106
+ proc { |*values|
107
+ if values.blank?
108
+ {:conditions => '#{attribute} > 0 OR #{attribute} IS NOT NULL'}
109
+ else
110
+ sets = values.map do |value|
111
+ mask = #{model}.bitmask_for_#{attribute}(value)
112
+ "#{attribute} & \#{mask} <> 0"
113
+ end
114
+ {:conditions => sets.join(' AND ')}
115
+ end
116
+ }
117
+ named_scope :without_#{attribute}, :conditions => "#{attribute} == 0 OR #{attribute} IS NULL"
118
+ named_scope :no_#{attribute}, :conditions => "#{attribute} == 0 OR #{attribute} IS NULL"
119
+ )
120
+ values.each do |value|
121
+ model.class_eval %(
122
+ named_scope :#{attribute}_for_#{value},
123
+ :conditions => ['#{attribute} & ? <> 0', #{model}.bitmask_for_#{attribute}(:#{value})]
124
+ )
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ def self.included(model)
131
+ model.extend ClassMethods
132
+ end
133
+
134
+ module ClassMethods
135
+
136
+ def bitmask(attribute, options={}, &extension)
137
+ unless options[:as] && options[:as].kind_of?(Array)
138
+ raise ArgumentError, "Must provide an Array :as option"
139
+ end
140
+ bitmask_definitions[attribute] = BitmaskAttribute::Definition.new(attribute, options[:as].to_a, &extension)
141
+ bitmask_definitions[attribute].install_on(self)
142
+ end
143
+
144
+ def bitmask_definitions
145
+ @bitmask_definitions ||= {}
146
+ end
147
+
148
+ def bitmasks
149
+ @bitmasks ||= {}
150
+ end
151
+
152
+ end
153
+
154
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ ActiveRecord::Base.instance_eval do
2
+ include BitmaskAttribute
3
+ end
@@ -0,0 +1,222 @@
1
+ require 'test_helper'
2
+
3
+ class BitmaskAttributeTest < Test::Unit::TestCase
4
+
5
+ context "Campaign" do
6
+
7
+ teardown do
8
+ Company.destroy_all
9
+ Campaign.destroy_all
10
+ end
11
+
12
+ should "can assign single value to bitmask" do
13
+ assert_stored Campaign.new(:medium => :web), :web
14
+ end
15
+
16
+ should "can assign multiple values to bitmask" do
17
+ assert_stored Campaign.new(:medium => [:web, :print]), :web, :print
18
+ end
19
+
20
+ should "can add single value to bitmask" do
21
+ campaign = Campaign.new(:medium => [:web, :print])
22
+ assert_stored campaign, :web, :print
23
+ campaign.medium << :phone
24
+ assert_stored campaign, :web, :print, :phone
25
+ end
26
+
27
+ should "ignores duplicate values added to bitmask" do
28
+ campaign = Campaign.new(:medium => [:web, :print])
29
+ assert_stored campaign, :web, :print
30
+ campaign.medium << :phone
31
+ assert_stored campaign, :web, :print, :phone
32
+ campaign.medium << :phone
33
+ assert_stored campaign, :web, :print, :phone
34
+ assert_equal 1, campaign.medium.select { |value| value == :phone }.size
35
+ end
36
+
37
+ should "can assign new values at once to bitmask" do
38
+ campaign = Campaign.new(:medium => [:web, :print])
39
+ assert_stored campaign, :web, :print
40
+ campaign.medium = [:phone, :email]
41
+ assert_stored campaign, :phone, :email
42
+ end
43
+
44
+ should "can save bitmask to db and retrieve values transparently" do
45
+ campaign = Campaign.new(:medium => [:web, :print])
46
+ assert_stored campaign, :web, :print
47
+ assert campaign.save
48
+ assert_stored Campaign.find(campaign.id), :web, :print
49
+ end
50
+
51
+ should "can add custom behavor to value proxies during bitmask definition" do
52
+ campaign = Campaign.new(:medium => [:web, :print])
53
+ assert_raises NoMethodError do
54
+ campaign.medium.worked?
55
+ end
56
+ assert_nothing_raised do
57
+ campaign.misc.worked?
58
+ end
59
+ assert campaign.misc.worked?
60
+ end
61
+
62
+ should "cannot use unsupported values" do
63
+ assert_unsupported { Campaign.new(:medium => [:web, :print, :this_will_fail]) }
64
+ campaign = Campaign.new(:medium => :web)
65
+ assert_unsupported { campaign.medium << :this_will_fail_also }
66
+ assert_unsupported { campaign.medium = [:so_will_this] }
67
+ end
68
+
69
+ should "can determine bitmasks using convenience method" do
70
+ assert Campaign.bitmask_for_medium(:web, :print)
71
+ assert_equal(
72
+ Campaign.bitmasks[:medium][:web] | Campaign.bitmasks[:medium][:print],
73
+ Campaign.bitmask_for_medium(:web, :print)
74
+ )
75
+ end
76
+
77
+ should "assert use of unknown value in convenience method will result in exception" do
78
+ assert_unsupported { Campaign.bitmask_for_medium(:web, :and_this_isnt_valid) }
79
+ end
80
+
81
+ should "hash of values is with indifferent access" do
82
+ string_bit = nil
83
+ assert_nothing_raised do
84
+ assert (string_bit = Campaign.bitmask_for_medium('web', 'print'))
85
+ end
86
+ assert_equal Campaign.bitmask_for_medium(:web, :print), string_bit
87
+ end
88
+
89
+ should "save bitmask with non-standard attribute names" do
90
+ campaign = Campaign.new(:Legacy => [:upper, :case])
91
+ assert campaign.save
92
+ assert_equal [:upper, :case], Campaign.find(campaign.id).Legacy
93
+ end
94
+
95
+ should "ignore blanks fed as values" do
96
+ campaign = Campaign.new(:medium => [:web, :print, ''])
97
+ assert_stored campaign, :web, :print
98
+ end
99
+
100
+ should "convert values passed as strings to symbols" do
101
+ campaign = Campaign.new
102
+ campaign.medium << "web"
103
+ assert_equal [:web], campaign.medium
104
+ end
105
+
106
+ context "checking" do
107
+
108
+ setup { @campaign = Campaign.new(:medium => [:web, :print]) }
109
+
110
+ context "for a single value" do
111
+
112
+ should "be supported by an attribute_for_value convenience method" do
113
+ assert @campaign.medium_for_web?
114
+ assert @campaign.medium_for_print?
115
+ assert !@campaign.medium_for_email?
116
+ end
117
+
118
+ should "be supported by the simple predicate method" do
119
+ assert @campaign.medium?(:web)
120
+ assert @campaign.medium?(:print)
121
+ assert !@campaign.medium?(:email)
122
+ end
123
+
124
+ end
125
+
126
+ context "for multiple values" do
127
+
128
+ should "be supported by the simple predicate method" do
129
+ assert @campaign.medium?(:web, :print)
130
+ assert !@campaign.medium?(:web, :email)
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+
137
+ context "named scopes" do
138
+
139
+ setup do
140
+ @company = Company.create(:name => "Test Co, Intl.")
141
+ @campaign1 = @company.campaigns.create :medium => [:web, :print]
142
+ @campaign2 = @company.campaigns.create
143
+ @campaign3 = @company.campaigns.create :medium => [:web, :email]
144
+ end
145
+
146
+ should "support retrieval by any value" do
147
+ assert_equal [@campaign1, @campaign3], @company.campaigns.with_medium
148
+ end
149
+
150
+ should "support retrieval by one matching value" do
151
+ assert_equal [@campaign1], @company.campaigns.with_medium(:print)
152
+ end
153
+
154
+ should "support retrieval by all matching values" do
155
+ assert_equal [@campaign1], @company.campaigns.with_medium(:web, :print)
156
+ assert_equal [@campaign3], @company.campaigns.with_medium(:web, :email)
157
+ end
158
+
159
+ should "support retrieval for no values" do
160
+ assert_equal [@campaign2], @company.campaigns.without_medium
161
+ end
162
+
163
+ end
164
+
165
+ should "can check if at least one value is set" do
166
+ campaign = Campaign.new(:medium => [:web, :print])
167
+
168
+ assert campaign.medium?
169
+
170
+ campaign = Campaign.new
171
+
172
+ assert !campaign.medium?
173
+ end
174
+
175
+ should "find by bitmask values" do
176
+ campaign = Campaign.new(:medium => [:web, :print])
177
+ assert campaign.save
178
+
179
+ assert_equal(
180
+ Campaign.find(:all, :conditions => ['medium & ? <> 0', Campaign.bitmask_for_medium(:print)]),
181
+ Campaign.medium_for_print
182
+ )
183
+
184
+ assert_equal Campaign.medium_for_print, Campaign.medium_for_print.medium_for_web
185
+
186
+ assert_equal [], Campaign.medium_for_email
187
+ assert_equal [], Campaign.medium_for_web.medium_for_email
188
+ end
189
+
190
+ should "find no values" do
191
+ campaign = Campaign.create(:medium => [:web, :print])
192
+ assert campaign.save
193
+
194
+ assert_equal [], Campaign.no_medium
195
+
196
+ campaign.medium = []
197
+ assert campaign.save
198
+
199
+ assert_equal [campaign], Campaign.no_medium
200
+ end
201
+
202
+ #######
203
+ private
204
+ #######
205
+
206
+ def assert_unsupported(&block)
207
+ assert_raises(ArgumentError, &block)
208
+ end
209
+
210
+ def assert_stored(record, *values)
211
+ values.each do |value|
212
+ assert record.medium.any? { |v| v.to_s == value.to_s }, "Values #{record.medium.inspect} does not include #{value.inspect}"
213
+ end
214
+ full_mask = values.inject(0) do |mask, value|
215
+ mask | Campaign.bitmasks[:medium][value]
216
+ end
217
+ assert_equal full_mask, record.medium.to_i
218
+ end
219
+
220
+ end
221
+
222
+ end
@@ -0,0 +1,66 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ begin
5
+ require 'redgreen'
6
+ rescue LoadError
7
+ end
8
+
9
+ require 'active_support'
10
+ require 'active_record'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'bitmask-attribute'
15
+ require File.dirname(__FILE__) + '/../rails/init'
16
+
17
+ # ActiveRecord::Base.logger = Logger.new(STDOUT)
18
+
19
+ ActiveRecord::Base.establish_connection(
20
+ :adapter => 'sqlite3',
21
+ :database => ':memory:'
22
+ )
23
+
24
+ ActiveRecord::Schema.define do
25
+ create_table :campaigns do |t|
26
+ t.integer :company_id
27
+ t.integer :medium, :misc, :Legacy
28
+ end
29
+ create_table :companies do |t|
30
+ t.string :name
31
+ end
32
+ end
33
+
34
+ class Company < ActiveRecord::Base
35
+ has_many :campaigns
36
+ end
37
+
38
+ # Pseudo model for testing purposes
39
+ class Campaign < ActiveRecord::Base
40
+ belongs_to :company
41
+ bitmask :medium, :as => [:web, :print, :email, :phone]
42
+ bitmask :misc, :as => %w(some useless values) do
43
+ def worked?
44
+ true
45
+ end
46
+ end
47
+ bitmask :Legacy, :as => [:upper, :case]
48
+ end
49
+
50
+ class Test::Unit::TestCase
51
+
52
+ def assert_unsupported(&block)
53
+ assert_raises(ArgumentError, &block)
54
+ end
55
+
56
+ def assert_stored(record, *values)
57
+ values.each do |value|
58
+ assert record.medium.any? { |v| v.to_s == value.to_s }, "Values #{record.medium.inspect} does not include #{value.inspect}"
59
+ end
60
+ full_mask = values.inject(0) do |mask, value|
61
+ mask | Campaign.bitmasks[:medium][value]
62
+ end
63
+ assert_equal full_mask, record.medium.to_i
64
+ end
65
+
66
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: r3trofitted-bitmask-attribute
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 1
8
+ - 0
9
+ version: 1.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Bruce Williams
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-12 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ description:
33
+ email: bruce@codefluency.com
34
+ executables: []
35
+
36
+ extensions: []
37
+
38
+ extra_rdoc_files:
39
+ - LICENSE
40
+ - README.markdown
41
+ files:
42
+ - .document
43
+ - .gitignore
44
+ - LICENSE
45
+ - README.markdown
46
+ - Rakefile
47
+ - VERSION
48
+ - lib/bitmask-attribute.rb
49
+ - lib/bitmask_attribute.rb
50
+ - lib/bitmask_attribute/value_proxy.rb
51
+ - rails/init.rb
52
+ - test/bitmask_attribute_test.rb
53
+ - test/test_helper.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/bruce/bitmask-attribute
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --charset=UTF-8
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.6
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Simple bitmask attribute support for ActiveRecord
84
+ test_files:
85
+ - test/bitmask_attribute_test.rb
86
+ - test/test_helper.rb