has_bitfield 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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Hugo Duncan
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.rdoc ADDED
@@ -0,0 +1,60 @@
1
+ = has_bitfield
2
+
3
+ A rails gem for easy bitfield support.
4
+
5
+ Has support form multiple bitfield columns. See http://github.com/aka47/has_flags for a gem with a different feature set.
6
+
7
+ == Install
8
+
9
+ gem install has_bitfield
10
+
11
+ == Usage
12
+ class MyClass << ActiveRecord::Base
13
+ has_bitfield :column_name,
14
+ 1 => :flag1,
15
+ 2 => :flag2,
16
+ 3 => :flag3
17
+ end
18
+
19
+ == Attribute Accessors
20
+ readers: name, name?
21
+ writer: name=(v)
22
+ where 'name' is the name of the bit field. the (v) parameter can be true/false, "true"/"false", 0/1, 'yes'/'no',
23
+ or :yes/:no. specifically, the following (v) inputs evaluate to true:
24
+ [ true, 'true', 'yes', :yes, 'ok', :ok, 1, '1' ]
25
+ all others, including nil evaluate false.
26
+ groups_name = [Array of flags] , not the plural
27
+ groups_name << name
28
+ groups_name
29
+ group_ids
30
+ group_ids = [Array of flag-ids]
31
+
32
+
33
+ The database table must include the column 'column_name' as an integer.
34
+
35
+ Defaults are set in per column 'after_initialized' callbacks. If your model needs to use this
36
+ callback, define it in the class and invoke: initialize_column_name.
37
+
38
+ class MyClass << ActiveRecord::Base
39
+ has_bitfield :my_flags,
40
+ 1 => {:flag1 => { :default => false }},
41
+ 2 => :flag2,
42
+ 3 => :flag3
43
+
44
+ after_initialize :initialize_my_flags
45
+ end
46
+
47
+
48
+ == Note on Patches/Pull Requests
49
+
50
+ * Fork the project.
51
+ * Make your feature addition or bug fix.
52
+ * Add tests for it. This is important so I don't break it in a
53
+ future version unintentionally.
54
+ * Commit, do not mess with rakefile, version, or history.
55
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
56
+ * Send me a pull request. Bonus points for topic branches.
57
+
58
+ == Copyright
59
+
60
+ Copyright (c) 2009 Hugo Duncan. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "has_bitfield"
8
+ gem.summary = %Q{A rails gem for easy bitfield support.}
9
+ gem.description = %Q{Enable bitfield properties in ActiveRecord models.}
10
+ gem.email = "hugo_duncan@yahoo.com"
11
+ gem.homepage = "http://github.com/hugoduncan/has_bitfield"
12
+ gem.authors = ["Hugo Duncan"]
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
17
+ end
18
+
19
+ require 'rake/testtask'
20
+ Rake::TestTask.new(:test) do |test|
21
+ test.libs << 'lib' << 'test'
22
+ test.pattern = 'test/**/test_*.rb'
23
+ test.verbose = true
24
+ end
25
+
26
+ begin
27
+ require 'rcov/rcovtask'
28
+ Rcov::RcovTask.new do |test|
29
+ test.libs << 'test'
30
+ test.pattern = 'test/**/test_*.rb'
31
+ test.verbose = true
32
+ end
33
+ rescue LoadError
34
+ task :rcov do
35
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
36
+ end
37
+ end
38
+
39
+ task :test => :check_dependencies
40
+
41
+ task :default => :test
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "has_bitfield #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,137 @@
1
+ module HasBitfield
2
+
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ FLAG_VALUE_OPTIONS = [:default] unless defined? FLAG_VALUE_OPTIONS
8
+ FLAG_OPTIONS = [:as,:name] unless defined? FLAG_OPTIONS
9
+
10
+ module ClassMethods
11
+ def has_bitfield(name, flags_hash)
12
+ @has_bitfield ||= {}
13
+ values = {}
14
+ bits = {}
15
+ defaults = {}
16
+ bit_names = {}
17
+ field = flags_hash.delete(:as) || "#{name}_flags".to_sym
18
+ @has_bitfield[name] = {
19
+ :as => field,
20
+ :values => values,
21
+ :bit_names => bit_names,
22
+ :defaults => defaults
23
+ }
24
+
25
+ default_mask = 0
26
+
27
+ flags_hash.each do |key, flag|
28
+ flag_name, options = hash_for_flag(flag)
29
+ raise ArgumentError, "has_bitfield: keys should be positive integers less than or equal to 32.
30
+ '#{key}' is invalid as a key for '#{flag_name}'." unless is_valid_bit?(key)
31
+
32
+ value = 2**(key - 1)
33
+ values[flag] = value
34
+ bit_names[key] = flag
35
+ default = options.has_key?(:default) ? options[:default] : false;
36
+ defaults[key] = default
37
+ default_mask = default_mask | value if default
38
+ methods_for_flag(flag_name, value, field)
39
+ end
40
+
41
+ method_for_name(name, field)
42
+ method_for_initialize(name, default_mask, field)
43
+ end
44
+
45
+ protected
46
+
47
+ def methods_for_flag(flag, value, field)
48
+ class_eval <<-EVAL
49
+ def #{flag}?
50
+ flag_set?(:#{field}, #{value})
51
+ end
52
+
53
+ def #{flag}
54
+ flag_set?(:#{field}, #{value})
55
+ end
56
+
57
+ def #{flag}=(value)
58
+ if value.is_a?(String)
59
+ value = Integer(value)
60
+ value = (value!=0)
61
+ end
62
+ value ? set_flag(:#{field}, #{value}) : clear_flag(:#{field}, #{value})
63
+ end
64
+
65
+ def self.#{flag}_condition
66
+ condition_for_flag(:#{field}, #{value}, true)
67
+ end
68
+
69
+ def self.not_#{flag}_condition
70
+ condition_for_flag(:#{field}, #{value}, false)
71
+ end
72
+ EVAL
73
+ end
74
+
75
+ def method_for_initialize(name, mask, field)
76
+ class_eval <<-EVAL
77
+ def initialize_#{name}
78
+ self[:#{field}] = #{mask}
79
+ end
80
+ EVAL
81
+ end
82
+
83
+ def method_for_name(name, field)
84
+ class_eval <<-EVAL
85
+ def #{name}
86
+ self[:#{field}]
87
+ end
88
+ EVAL
89
+ end
90
+
91
+ def flag_metadata
92
+ @has_bitfield
93
+ end
94
+
95
+ def has_flag?(flag)
96
+ @has_bitfield.any? { |name, meta| meta.values.keys.include?(flag) }
97
+ end
98
+
99
+ private
100
+ def condition_for_flag(field, value, enabled)
101
+ "(#{table_name}.#{field} & #{value} = #{enabled ? '1': '0'})"
102
+ end
103
+
104
+ def is_valid_bit?(key)
105
+ key > 0 && key == key.to_i && key < 33
106
+ end
107
+
108
+ def hash_for_flag(value)
109
+ if value.is_a?(Symbol)
110
+ [ value, {} ]
111
+ else
112
+ if value.size != 1
113
+ raise ArgumentError("invalid flag specification for #{value.inspect}")
114
+ end
115
+ [ value.keys[0], value.values[0] ]
116
+ end
117
+ end
118
+ end # module
119
+
120
+ private
121
+ def set_flag(field, value)
122
+ self[field] = self[field] | value
123
+ end
124
+
125
+ def clear_flag(field, value)
126
+ self[field] = self[field] & ~value
127
+ end
128
+
129
+ def flag_set?(field, value)
130
+ self[field] & value != 0
131
+ end
132
+
133
+ def flag_clear?(field, value)
134
+ self[field] & value == 0
135
+ end
136
+
137
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'has_bitfield'
7
+
8
+ class Test::Unit::TestCase
9
+ end
@@ -0,0 +1,171 @@
1
+ require 'helper'
2
+ require 'active_support/core_ext'
3
+ require 'active_support/test_case'
4
+
5
+ # ActiveRecord proxies
6
+ module Proxy
7
+ class Column
8
+ def name
9
+ 'flags'
10
+ end
11
+ def type
12
+ :integer
13
+ end
14
+ end
15
+
16
+ class Base < Hash
17
+ def self.columns
18
+ [Column.new]
19
+ end
20
+
21
+ def flags=(value)
22
+ self[:flags] = value
23
+ end
24
+
25
+ def flags
26
+ self[:flags]
27
+ end
28
+
29
+ end
30
+ end
31
+
32
+ class Foo < Proxy::Base
33
+ include ::HasBitfield
34
+
35
+ def self.table_name
36
+ "foos"
37
+ end
38
+
39
+ has_bitfield(:my_flags,
40
+ :as => :flags,
41
+ 1 => :active,
42
+ 2 => :suspended,
43
+ 3 => { :another => { :default => true } } )
44
+ end
45
+
46
+ class HasBitfieldTest < ActiveSupport::TestCase
47
+
48
+ test "should raise an exception when a negative bit is specified" do
49
+ assert_raises ArgumentError do
50
+ eval(<<-EOF
51
+ class Invalid < Proxy::Base
52
+ include ::HasBitfield
53
+ has_bitfield({ -1 => :error })
54
+ end
55
+ EOF
56
+ )
57
+ end
58
+ end
59
+
60
+ test "should raise an exception when a zero bit is specified" do
61
+ assert_raises ArgumentError do
62
+ eval(<<-EOF
63
+ class Invalid < Proxy::Base
64
+ include ::HasBitfield
65
+ has_bitfield({ 0 => :error })
66
+ end
67
+ EOF
68
+ )
69
+ end
70
+ end
71
+
72
+ test "should raise an exception when a bit above 32 is specified" do
73
+ assert_raises ArgumentError do
74
+ eval(<<-EOF
75
+ class Invalid < Proxy::Base
76
+ include ::HasBitfield
77
+ has_bitfield({ 33 => :error })
78
+ end
79
+ EOF
80
+ )
81
+ end
82
+ end
83
+
84
+ def setup
85
+ @foo = Foo.new
86
+ end
87
+
88
+ test "test class should have flags field" do
89
+ @foo.flags = 1
90
+ assert_equal 1, @foo.flags
91
+ assert_equal 1, @foo[:flags]
92
+
93
+ @foo[:flags] = 2
94
+ assert_equal 2, @foo.flags
95
+ assert_equal 2, @foo[:flags]
96
+ end
97
+
98
+ test "should have my_flags reader" do
99
+ @foo.flags = 1
100
+ assert_equal 1, @foo.my_flags
101
+
102
+ @foo[:flags] = 2
103
+ assert_equal 2, @foo.my_flags
104
+ end
105
+
106
+ test "should return the initialized flag field" do
107
+ @foo.initialize_my_flags
108
+ assert_equal 4, @foo.my_flags
109
+ end
110
+
111
+ test "should correctly define predicate" do
112
+ @foo[:flags] = 4
113
+ assert !@foo.active?
114
+ assert !@foo.suspended?
115
+ assert @foo.another?
116
+ end
117
+
118
+ test "should correctly define reader" do
119
+ @foo[:flags] = 4
120
+ assert !@foo.active
121
+ assert !@foo.suspended
122
+ assert @foo.another
123
+ end
124
+
125
+ test "should correctly define assignment" do
126
+ @foo[:flags] = 4
127
+ @foo.active = true
128
+ assert @foo.active?
129
+ assert !@foo.suspended?
130
+ assert @foo.another?
131
+
132
+ @foo.active = false
133
+ assert !@foo.active?
134
+ assert !@foo.suspended?
135
+ assert @foo.another?
136
+ end
137
+
138
+ test "should correctly assign from string" do
139
+ @foo[:flags] = 4
140
+ @foo.active="1"
141
+ assert @foo.active
142
+ @foo.active="0"
143
+ assert !@foo.active
144
+ end
145
+
146
+ test "should define condition" do
147
+ assert_equal "(foos.flags & 1 = 1)", Foo.active_condition
148
+ end
149
+
150
+ test "should define negated condition" do
151
+ assert_equal "(foos.flags & 1 = 0)", Foo.not_active_condition
152
+ end
153
+
154
+ test "should correctly assign current value" do
155
+ @foo[:flags] = 4
156
+ @foo.active = true
157
+ 2.times do
158
+ @foo.active = false
159
+ assert !@foo.active?
160
+ end
161
+ end
162
+
163
+ test "should have an initial value" do
164
+ @foo.initialize_my_flags
165
+ assert_equal false, @foo.active?
166
+ assert_equal false, @foo.suspended?
167
+ assert_equal true, @foo.another?
168
+ end
169
+
170
+
171
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_bitfield
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hugo Duncan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-16 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Enable bitfield properties in ActiveRecord models.
17
+ email: hugo_duncan@yahoo.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - .document
27
+ - .gitignore
28
+ - LICENSE
29
+ - README.rdoc
30
+ - Rakefile
31
+ - VERSION
32
+ - lib/has_bitfield.rb
33
+ - test/helper.rb
34
+ - test/test_has_bitfield.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/hugoduncan/has_bitfield
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.5
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: A rails gem for easy bitfield support.
63
+ test_files:
64
+ - test/helper.rb
65
+ - test/test_has_bitfield.rb