bitfield_attribute 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1da85dce3a7dee9f30ae1f56db09a16e2cf58098
4
+ data.tar.gz: 3b30bfaf3943ffe7dff701bdcb931cce8173b8ed
5
+ SHA512:
6
+ metadata.gz: 6649c773b5257f37c07856865fb1b2e6c970247624839ed418b6091afef7e8ffd9d23984e9e006392add8f3b085796f1985ddb28e06f1fe16fcb912041aa1e81
7
+ data.tar.gz: 561f47334f78e9e7b9c04050bf4dea2da9cbae9333c3575f2312e32e6429654ac4b99eefae7689a37a93211d35a0880a9f79b25922e0457cf71633cf4a7b8f7f
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bitfield_attribute.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Viktor Sokolov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # BitfieldAttribute
2
+
3
+ Bitfield value object for ActiveModel. No hidden definitions. No callbacks. Magicless.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'bitfield_attribute'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install bitfield_attribute
18
+
19
+ ## Usage
20
+
21
+ Add integer column to your model:
22
+
23
+ ```ruby
24
+ class AddNotificationSettingsToUsers < ActiveRecord::Migration
25
+ def change
26
+ add_column :users, :notification_settings, :integer, default: 0
27
+ end
28
+ end
29
+ ```
30
+
31
+ Define bitfield class:
32
+
33
+ ```ruby
34
+ class User::NotificationSettings
35
+ include BitfieldAttribute::Base
36
+
37
+ define_bits :weekly_digest, :announces, :events
38
+ end
39
+ ```
40
+
41
+ Define accessor in the model:
42
+
43
+ ```ruby
44
+ class User < ActiveRecord::Base
45
+ def notification_settings
46
+ @notification_settings ||= User::NotificationSettings.new(self, :notification_settings)
47
+ end
48
+
49
+ def notification_settings=(value)
50
+ notification_settings.attributes = value
51
+ end
52
+ end
53
+ ```
54
+
55
+ Use it:
56
+
57
+ ```ruby
58
+ user = User.new(notification_settings: {weekly_digest: true})
59
+ user.notification_settings.announces = true
60
+
61
+ user.notification_settings.weekly_digest? # => true
62
+ user.notification_settings.to_a # => [:weekly_digest, :announces]
63
+ user.notification_settings.attributes # => { weekly_digest: true, announces: true, events: false }
64
+ user[:notification_settings] # => 3
65
+ ```
66
+
67
+ ## ActiveModel I18n & forms
68
+
69
+ Modify your bitfield class:
70
+
71
+ ```ruby
72
+ class User::NotificationSettings
73
+ include BitfieldAttribute::Base
74
+ extend ActiveModel::Naming
75
+ extend ActiveModel::Translation
76
+
77
+ define_bits :weekly_digest, :announces, :events
78
+ end
79
+ ```
80
+
81
+ Add this to your locale file:
82
+
83
+ ```ruby
84
+ en:
85
+ activemodel:
86
+ "user/notification_settings":
87
+ weekly_digest: 'Weekly top sellers digest'
88
+ announces: 'Announces'
89
+ events: 'Invitations'
90
+ ```
91
+
92
+ Use forms:
93
+
94
+ ```slim
95
+ = form_for @user do |form|
96
+ = form.fields_for :notification_settings, form.object.notification_settings do |f|
97
+ = f.check_box :weekly_digest
98
+ = f.label :weekly_digest
99
+
100
+ ...
101
+
102
+ = form.submit 'Save'
103
+ ```
104
+
105
+ ## Contributing
106
+
107
+ 1. Fork it ( https://github.com/gzigzigzeo/bitfield_attribute/fork )
108
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
109
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
110
+ 4. Push to the branch (`git push origin my-new-feature`)
111
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bitfield_attribute/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bitfield_attribute"
8
+ spec.version = BitfieldAttribute::VERSION
9
+ spec.authors = ["Viktor Sokolov"]
10
+ spec.email = ["gzigzigzeo@evilmartians.com"]
11
+ spec.summary = %q{Bitfield value object for ActiveModel}
12
+ spec.description = %q{Bitfield value object for ActiveModel. No hidden definitions. No callbacks. Magicless.}
13
+ spec.homepage = "https://github.com/gzigzigzeo/bitfield_attribute"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "simplecov"
25
+
26
+ spec.add_dependency "activemodel"
27
+ spec.add_dependency "activerecord"
28
+ end
@@ -0,0 +1,5 @@
1
+ require "bitfield_attribute/version"
2
+ require "bitfield_attribute/base"
3
+
4
+ module BitfieldAttribute
5
+ end
@@ -0,0 +1,123 @@
1
+ require 'active_model'
2
+ require 'active_support/hash_with_indifferent_access'
3
+ require 'active_support/core_ext/object/inclusion'
4
+ require 'active_support/concern'
5
+ require 'active_record/connection_adapters/column'
6
+
7
+ module BitfieldAttribute
8
+ module Base
9
+ extend ActiveSupport::Concern
10
+
11
+ module ClassMethods
12
+ def define_bits(*keys)
13
+ if @keys.present?
14
+ raise ArgumentError, 'Define all your bits with a single #define_bits statement'
15
+ end
16
+
17
+ @keys = keys.map(&:to_sym)
18
+
19
+ if @keys.uniq.size != @keys.size
20
+ raise ArgumentError, "Bit names are not uniq"
21
+ end
22
+
23
+ if @keys.size > INTEGER_SIZE
24
+ raise ArgumentError, "Too many bit names for #{INTEGER_SIZE}-bit integer"
25
+ end
26
+
27
+ define_bit_methods
28
+ end
29
+
30
+ def keys
31
+ @keys
32
+ end
33
+
34
+ private
35
+ def define_bit_methods
36
+ keys.each do |key|
37
+ define_setter(key)
38
+ define_getter(key)
39
+ end
40
+ end
41
+
42
+ def define_setter(key)
43
+ define_method :"#{key}=" do |value|
44
+ @values[key] = value
45
+ write_bits
46
+ end
47
+ end
48
+
49
+ def define_getter(key)
50
+ define_method :"#{key}?" do
51
+ @values[key] || false
52
+ end
53
+
54
+ alias_method key, :"#{key}?"
55
+ end
56
+ end
57
+
58
+ def initialize(instance, attribute)
59
+ @instance = instance
60
+ @attribute = attribute
61
+
62
+ keys = self.class.keys
63
+
64
+ @values = keys.zip([false] * keys.size)
65
+ @values = Hash[@values]
66
+
67
+ read_bits
68
+ end
69
+
70
+ def to_a
71
+ @values.map { |key, value| key if value }.compact
72
+ end
73
+
74
+ def value
75
+ @instance[@attribute].to_i
76
+ end
77
+
78
+ def attributes
79
+ @values.freeze
80
+ end
81
+
82
+ def attributes=(hash)
83
+ @values.each { |key, _| @values[key] = false }
84
+ update(hash)
85
+ end
86
+
87
+ def update(hash)
88
+ hash.symbolize_keys.each do |key, value|
89
+ if @values.keys.include?(key)
90
+ @values[key] = true_value?(value)
91
+ end
92
+ end
93
+
94
+ write_bits
95
+ end
96
+
97
+ private
98
+ def read_bits
99
+ bit_value = @instance[@attribute].to_i
100
+
101
+ @values.keys.each.with_index do |name, index|
102
+ bit = 2 ** index
103
+ @values[name] = true if bit_value & bit == bit
104
+ end
105
+ end
106
+
107
+ def write_bits
108
+ 0.tap do |bits|
109
+ @values.keys.each.with_index do |name, index|
110
+ bits = bits | (2 ** index) if @values[name]
111
+ end
112
+
113
+ @instance[@attribute] = bits
114
+ end
115
+ end
116
+
117
+ def true_value?(value)
118
+ value.in?(ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES)
119
+ end
120
+
121
+ INTEGER_SIZE = 32
122
+ end
123
+ end
@@ -0,0 +1,3 @@
1
+ module BitfieldAttribute
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ describe TestBitfield do
4
+ let(:storage) { { bitfield: 0 } }
5
+ subject { described_class.new(storage, :bitfield) }
6
+
7
+ context '#initialize' do
8
+ let(:storage) { { bitfield: 6 } }
9
+
10
+ it 'raises if too many values given' do
11
+ expect {
12
+ class WrongBitfield1
13
+ include BitfieldAttribute::Base
14
+
15
+ define_bits *(:A..:z).to_a
16
+ end
17
+ }.to raise_error(ArgumentError, 'Too many bit names for 32-bit integer')
18
+ end
19
+
20
+ it 'raises if non-uniq values given' do
21
+ expect {
22
+ class WrongBitfield2
23
+ include BitfieldAttribute::Base
24
+
25
+ define_bits :a, :a, :a
26
+ end
27
+ }.to raise_error(ArgumentError, 'Bit names are not uniq')
28
+ end
29
+
30
+ it 'raises if bits are defining with multiple statements' do
31
+ expect {
32
+ class WrongBitfield3
33
+ include BitfieldAttribute::Base
34
+
35
+ define_bits :a, :b, :c
36
+ define_bits :d, :e, :f
37
+ end
38
+ }.to raise_error(ArgumentError, 'Define all your bits with a single #define_bits statement')
39
+ end
40
+
41
+ it 'gets initial bits from value' do
42
+ expect(subject.first).to eq(false)
43
+ expect(subject.first?).to eq(false)
44
+
45
+ expect(subject.second).to eq(true)
46
+ expect(subject.second?).to eq(true)
47
+
48
+ expect(subject.last).to eq(true)
49
+ expect(subject.last?).to eq(true)
50
+
51
+ expect(subject.to_a).to eq([:second, :last])
52
+ end
53
+ end
54
+
55
+ context 'setters' do
56
+ it 'sets value on storage' do
57
+ expect(storage[:bitfield]).to eq(0)
58
+
59
+ subject.first = true
60
+ subject.last = true
61
+
62
+ expect(subject.value).to eq(5)
63
+ expect(storage[:bitfield]).to eq(5)
64
+ end
65
+
66
+ it 'sets values from hash' do
67
+ subject.attributes = { 'first' => '1', 'second' => '0', 'last' => '1' }
68
+
69
+ expect(subject.value).to eq(5)
70
+
71
+ subject.attributes = { first: 1 }
72
+
73
+ expect(subject.value).to eq(1)
74
+
75
+ expect(subject.attributes).to eq({ first: true, second: false, last: false })
76
+ end
77
+ end
78
+
79
+ context '#update' do
80
+ it 'updates attributes with provided hash' do
81
+ subject.attributes = { 'first' => '1', 'second' => '0', 'last' => '1' }
82
+
83
+ subject.update( first: 0, second: 1 )
84
+
85
+ expect(subject.value).to eq 6
86
+ end
87
+ end
88
+
89
+ context 'internalization' do
90
+ before do
91
+ I18n.enforce_available_locales = false
92
+ I18n.backend.store_translations(I18n.default_locale,
93
+ activemodel: {
94
+ attributes: {
95
+ test_bitfield: {
96
+ first: 'First field'
97
+ },
98
+ 'bit_field/user/notification_settings' => {
99
+ second: 'Second field'
100
+ }
101
+ }
102
+ }
103
+ )
104
+ end
105
+
106
+ context 'non-namespaced model' do
107
+ it 'returns correct field name' do
108
+ expect(described_class.human_attribute_name(:first)).to eq('First field')
109
+ end
110
+ end
111
+
112
+ context 'namespaced model' do
113
+ subject { BitField::User::NotificationSettings.new(storage, :bitfield) }
114
+
115
+ it 'returns correct field name' do
116
+ expect(subject.class.human_attribute_name(:second)).to eq('Second field')
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,20 @@
1
+ $LOAD_PATH << "." unless $LOAD_PATH.include?(".")
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'simplecov'
6
+
7
+ SimpleCov.start do
8
+ add_filter 'spec'
9
+ end
10
+
11
+ require 'bitfield_attribute'
12
+ require 'support/test_bitfield'
13
+
14
+ RSpec.configure do |config|
15
+ config.order = :random
16
+ config.filter_run(:focus)
17
+ config.run_all_when_everything_filtered = true
18
+ end
19
+
20
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
@@ -0,0 +1,20 @@
1
+ class TestBitfield
2
+ include BitfieldAttribute::Base
3
+ extend ActiveModel::Naming
4
+ extend ActiveModel::Translation
5
+
6
+ define_bits :first, :second, :last
7
+ end
8
+
9
+ module BitField
10
+ class User
11
+ class NotificationSettings
12
+ extend ActiveModel::Naming
13
+ extend ActiveModel::Translation
14
+
15
+ include BitfieldAttribute::Base
16
+
17
+ define_bits :second
18
+ end
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bitfield_attribute
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Viktor Sokolov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activemodel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: activerecord
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Bitfield value object for ActiveModel. No hidden definitions. No callbacks.
98
+ Magicless.
99
+ email:
100
+ - gzigzigzeo@evilmartians.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bitfield_attribute.gemspec
111
+ - lib/bitfield_attribute.rb
112
+ - lib/bitfield_attribute/base.rb
113
+ - lib/bitfield_attribute/version.rb
114
+ - spec/lib/base_spec.rb
115
+ - spec/spec_helper.rb
116
+ - spec/support/test_bitfield.rb
117
+ homepage: https://github.com/gzigzigzeo/bitfield_attribute
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.2.2
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Bitfield value object for ActiveModel
141
+ test_files:
142
+ - spec/lib/base_spec.rb
143
+ - spec/spec_helper.rb
144
+ - spec/support/test_bitfield.rb