has_bitfield 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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