bitmask_attributes 0.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.
@@ -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