simple_enum-multiple 0.1.0

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: c89b6aec44c60fd0f92340755e106fae10546925
4
+ data.tar.gz: 1a1b9dd5c830ccb0db9de912f3894dc0ab3924f0
5
+ SHA512:
6
+ metadata.gz: 14472b37550061066af8ac2eaefc98972ae007871e174b5d6299b3a40044207f7d9f827dce4378037d9d50339fd3c9b457320ee438f015a8d363af1e5a86ff1e
7
+ data.tar.gz: 99d65fa4d15fd14dfb5b0e78f2d3d720d1250f5a3d25718b77ca40386936af6f6b6188827bc2cb2e89a4c92d0e3bde175dde8b1063ad362577ff562dcf319c41
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in simple_enum-multiple.gemspec
4
+ gemspec
5
+
6
+ # some development deps
7
+ gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby
8
+ gem 'sqlite3', platform: :ruby
9
+ gem 'bson_ext', platform: :ruby
10
+
11
+ # Code coverage on CI only
12
+ gem 'codeclimate-test-reporter', group: :test, require: nil
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Theo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # SimpleEnum::Multiple
2
+
3
+ SimpleEnum::Multiple is extension of SimpleEnum, which brings multi-select enum support to SimpleEnum.
4
+
5
+ ## ActiveRecord Quick start
6
+
7
+ Add this to a model:
8
+ ```ruby
9
+ as_enum :tags, [:iphone, :ipad, :macbook], accessor: :join_table
10
+ ```
11
+
12
+ Then create the required tag_cds column using migrations:
13
+ ```ruby
14
+ class AddTagsColumnToPost < ActiveRecord::Migration
15
+ def change
16
+ create_join_table :posts, :tags
17
+ end
18
+ end
19
+ ```
20
+
21
+ It will store multi-enums data in a join table, if you don't want a join table, you may use `bitwise` accessor:
22
+
23
+ ```ruby
24
+ # Migration
25
+ add_column :posts, :tag_cds, :integer
26
+
27
+ # Model
28
+ as_enum :tags, [:iphone, :ipad, :macbook], accessor: :bitwise
29
+ ```
30
+
31
+ It will store multi-enums data in a integer column, and if you don't want SimpleEnum::Multiple manage how you store your data, you can use `multiple` accessor:
32
+
33
+ ```ruby
34
+ # Model
35
+ as_enum :tags, [:iphone, :ipad, :macbook], accessor: :multiple
36
+ ```
37
+
38
+ This accessor will not handle how the data saved in the database, so you have to use something like `serialize :tag_cds`, or implement your own [Coder](https://github.com/rails/rails/blob/master/activerecord/lib/active_record/coders/json.rb).
39
+
40
+
41
+ ## License
42
+
43
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
44
+
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc 'Default: run all unit tests for both ActiveRecord & Mongoid.'
4
+ task :default => :spec
5
+
6
+ desc 'Run rspec test suite'
7
+ task :spec do
8
+ sh 'bundle exec rspec spec/'
9
+ end
10
+
11
+ # Mongodb
12
+ directory "tmp/mongodb.data"
13
+ desc 'Run mongodb in tmp/'
14
+ task :mongodb => [:'tmp/mongodb.data'] do |t|
15
+ system "mongod", "--dbpath", "tmp/mongodb.data"
16
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "simple_enum/multiple"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,23 @@
1
+ require 'simple_enum/multiple/accessors/multiple_accessor'
2
+ require 'simple_enum/multiple/coders/bitwise'
3
+
4
+ module SimpleEnum
5
+ module Multiple
6
+ module Accessors
7
+ class BitwiseAccessor < MultipleAccessor
8
+ def init(klass)
9
+ source = self.source
10
+
11
+ klass.class_eval do
12
+ serialize source, SimpleEnum::Multiple::Coders::Bitwise
13
+ end
14
+ end
15
+
16
+ def scope(collection, value)
17
+ column = Arel::Nodes::Group.new(collection.arel_table[source]).to_sql
18
+ collection.where("#{column} >> ? & 1 = 1", value)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,97 @@
1
+ require 'simple_enum/multiple/accessors/multiple_accessor'
2
+
3
+ module SimpleEnum
4
+ module Multiple
5
+ module Accessors
6
+ class JoinTableAccessor < MultipleAccessor
7
+ attr_accessor :table, :foreign_key, :remote_key
8
+
9
+ def init(klass)
10
+ klass_table_name = klass.table_name.to_s
11
+ join_table_name = [klass_table_name, name.pluralize].sort.join("_").to_sym
12
+ table = self.table = Arel::Table.new(join_table_name)
13
+
14
+ source = self.source
15
+ name = self.name
16
+
17
+ foreign_key = self.foreign_key = klass_table_name.singularize.foreign_key
18
+ remote_key = self.remote_key = name.singularize.foreign_key
19
+
20
+ connection = ActiveRecord::Base.connection
21
+
22
+ klass.class_eval do
23
+ attr_accessor source
24
+
25
+ define_method :"#{source}_changed?" do
26
+ instance_variable_get(:"@#{source}") !=
27
+ instance_variable_get(:"@#{source}_was")
28
+ end
29
+
30
+ define_method :"#{source}_was" do
31
+ instance_variable = instance_variable_get(:"@#{source}_was")
32
+ return instance_variable if instance_variable
33
+ sql = table.where(table[foreign_key].eq(self.id))
34
+ .project(table[remote_key])
35
+ .to_sql
36
+ original_cds = connection.send(:select, sql).rows.map(&:first)
37
+ instance_variable_set(:"@#{source}_was", original_cds)
38
+ end
39
+
40
+ define_method source do
41
+ instance_variable = instance_variable_get(:"@#{source}")
42
+ return instance_variable if instance_variable
43
+ instance_variable_set(:"@#{source}", send(:"#{source}_was").dup)
44
+ end
45
+
46
+ define_method :"update_#{source}!" do
47
+ return unless send(:"#{source}_changed?")
48
+ original_cds = send(:"#{source}_was")
49
+ current_cds = send(source)
50
+
51
+ # if any enum been removed
52
+ if (original_cds - current_cds).any?
53
+ delete_sql = table.where(table[foreign_key].eq(self.id))
54
+ .where(table[remote_key].in(original_cds - current_cds))
55
+ .compile_delete
56
+ .to_sql
57
+ connection.send(:delete, delete_sql)
58
+ end
59
+
60
+ # if any enum been added
61
+ if (current_cds - original_cds).any?
62
+ insert_sql = table.create_insert.tap do |insert_manager|
63
+ insert_manager.into table
64
+ insert_manager.columns << table[foreign_key]
65
+ insert_manager.columns << table[remote_key]
66
+
67
+ values = (current_cds - original_cds).map do |id|
68
+ "(#{self.id}, #{id})"
69
+ end.join(", ")
70
+ insert_manager.values = Arel::Nodes::SqlLiteral.new("VALUES #{values}")
71
+ end.to_sql
72
+ connection.send(:insert, insert_sql)
73
+ end
74
+ end
75
+
76
+ after_save :"update_#{source}!"
77
+ end
78
+ end
79
+
80
+ def was(object)
81
+ fetch_keys(object.send(:"#{source}_was").to_a)
82
+ end
83
+
84
+ def changed?(object)
85
+ object.send(:"#{source}_changed?")
86
+ end
87
+
88
+ def scope(collection, value)
89
+ join = Arel::Nodes::Group.new(table).to_sql
90
+ on = collection.arel_table[collection.primary_key].eq(table[foreign_key]).to_sql
91
+ collection.joins("INNER JOIN #{join} ON #{on}")
92
+ .where(table[foreign_key].eq(value))
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,72 @@
1
+ require 'simple_enum/accessors/accessor'
2
+ require 'simple_enum/multiple/collection_proxy'
3
+
4
+ module SimpleEnum
5
+ module Multiple
6
+ module Accessors
7
+ class MultipleAccessor < SimpleEnum::Accessors::Accessor
8
+ def initialize(name, enum, source = nil, prefix = nil)
9
+ super
10
+ @source = source.to_s.presence || "#{@name.singularize}#{SimpleEnum.suffix.pluralize}"
11
+ end
12
+
13
+ def read(object)
14
+ CollectionProxy.new(read_before_type_cast(object), self)
15
+ end
16
+
17
+ def write(object, keys)
18
+ keys = filter_keys(Array.wrap(keys))
19
+ write_after_type_cast(object, fetch_values(keys)) && keys
20
+ end
21
+
22
+ def selected?(object, key = nil)
23
+ current = read_before_type_cast(object)
24
+ current.any? && if key
25
+ current.all? do
26
+ current.include?(enum.value(key))
27
+ end
28
+ else
29
+ true
30
+ end
31
+ end
32
+
33
+ def was(object)
34
+ fetch_keys(object.send(:attribute_was, source).to_a)
35
+ end
36
+
37
+ def scope(collection, value)
38
+ raise "scopes for accessor: :multiple are not supported!"
39
+ end
40
+
41
+ def filter_keys(keys)
42
+ keys.select do |key|
43
+ enum.value(key)
44
+ end
45
+ end
46
+
47
+ def fetch_keys(values)
48
+ values.map do |value|
49
+ enum.key(value)
50
+ end
51
+ end
52
+
53
+ def fetch_values(keys)
54
+ keys.map do |key|
55
+ enum.value(key)
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def read_before_type_cast(object)
62
+ result = super
63
+ unless result
64
+ result = []
65
+ write_after_type_cast(object, result)
66
+ end
67
+ result
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,16 @@
1
+ require 'simple_enum/multiple/accessors/multiple_accessor'
2
+ require 'simple_enum/multiple/accessors/join_table_accessor'
3
+ require 'simple_enum/multiple/accessors/bitwise_accessor'
4
+
5
+ module SimpleEnum
6
+ module Multiple
7
+ module Accessors
8
+ SimpleEnum::Accessors::ACCESSORS.merge!({
9
+ multiple: MultipleAccessor,
10
+ join_table: JoinTableAccessor,
11
+ bitwise: BitwiseAccessor
12
+ })
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ module SimpleEnum
2
+ module Multiple
3
+ module Coders
4
+ class Bitwise
5
+ def self.load(data)
6
+ n = data.to_i
7
+ (0..Math.log2(n).floor).reject do |i|
8
+ (n & (1 << i)).zero?
9
+ end
10
+ rescue FloatDomainError, Math::DomainError
11
+ []
12
+ end
13
+
14
+ def self.dump(array)
15
+ array.map do |i|
16
+ 1 << i
17
+ end.reduce(&:+)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module SimpleEnum
2
+ module Multiple
3
+ class CollectionProxy
4
+ attr_reader :origin, :accessor
5
+
6
+ def initialize(origin, accessor)
7
+ @origin, @accessor = origin, accessor
8
+ end
9
+
10
+ def push(*keys)
11
+ keys = accessor.filter_keys(keys)
12
+ origin.push(*accessor.fetch_values(keys)).uniq!
13
+ self
14
+ end
15
+ alias_method :<<, :push
16
+
17
+ def delete(key)
18
+ origin.delete(accessor.enum.value(key)) && key
19
+ end
20
+
21
+ def proxy
22
+ accessor.fetch_keys(origin)
23
+ end
24
+
25
+ alias_method :to_a, :proxy
26
+ delegate :inspect, :to_s, :==, to: :proxy
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ module SimpleEnum
2
+ module Multiple
3
+ module Extension
4
+ def generate_enum_multiple_extension_for enum, accessor
5
+ accessor.init(self) if accessor.respond_to? :init
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ SimpleEnum.register_generator :multiple, SimpleEnum::Multiple::Extension
@@ -0,0 +1,5 @@
1
+ module SimpleEnum
2
+ module Multiple
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'simple_enum/multiple/accessors'
2
+ require 'simple_enum/multiple/collection_proxy'
3
+ require 'simple_enum/multiple/extension'
4
+ require 'simple_enum/multiple/version'
5
+
6
+ module SimpleEnum
7
+ module Multiple
8
+ # Your code goes here...
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'simple_enum/multiple/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "simple_enum-multiple"
8
+ spec.version = SimpleEnum::Multiple::VERSION
9
+ spec.authors = ["Theo"]
10
+ spec.email = ["bbtfrr@gmail.com"]
11
+
12
+ spec.summary = %q{Multi-select enum support for SimpleEnum.}
13
+ spec.description = %q{SimpleEnum::Multiple is extension of SimpleEnum, which brings multi-select enum support to SimpleEnum.}
14
+ spec.homepage = "https://github.com/bbtfr/simple_enum-multiple"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_runtime_dependency 'simple_enum', '~> 2.2'
31
+
32
+ spec.add_development_dependency 'bundler', '~> 1.10'
33
+ spec.add_development_dependency 'rake', '~> 10.0'
34
+ spec.add_development_dependency 'activerecord', '>= 4.0.0'
35
+ spec.add_development_dependency 'mongoid', '>= 4.0.0'
36
+ spec.add_development_dependency 'rspec', '~> 2.14'
37
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_enum-multiple
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Theo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: simple_enum
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activerecord
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 4.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 4.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: mongoid
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 4.0.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 4.0.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.14'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.14'
97
+ description: SimpleEnum::Multiple is extension of SimpleEnum, which brings multi-select
98
+ enum support to SimpleEnum.
99
+ email:
100
+ - bbtfrr@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bin/console
111
+ - bin/setup
112
+ - lib/simple_enum/multiple.rb
113
+ - lib/simple_enum/multiple/accessors.rb
114
+ - lib/simple_enum/multiple/accessors/bitwise_accessor.rb
115
+ - lib/simple_enum/multiple/accessors/join_table_accessor.rb
116
+ - lib/simple_enum/multiple/accessors/multiple_accessor.rb
117
+ - lib/simple_enum/multiple/coders/bitwise.rb
118
+ - lib/simple_enum/multiple/collection_proxy.rb
119
+ - lib/simple_enum/multiple/extension.rb
120
+ - lib/simple_enum/multiple/version.rb
121
+ - simple_enum-multiple.gemspec
122
+ homepage: https://github.com/bbtfr/simple_enum-multiple
123
+ licenses:
124
+ - MIT
125
+ metadata:
126
+ allowed_push_host: https://rubygems.org
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.4.6
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Multi-select enum support for SimpleEnum.
147
+ test_files: []