enum_ish 1.1.1 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d5f4dc1bae786f87d0835e8a9148b0203ea05795366279a0fc5dfa44fffdb81
4
- data.tar.gz: 735635b6dd325ad6a36d702c33c7dfc4b567a252a4d47b1b7a6848e2a1d2688b
3
+ metadata.gz: c81de716639aaef492be612f2f9d412889513785e61163a2d0e409dad03f883e
4
+ data.tar.gz: 8163a1339902881f9300cf582b852ca8e505c0cbe27b031b0e9e680cef5a1547
5
5
  SHA512:
6
- metadata.gz: d0519566e727bc12540003a7a0f5f9c916e297a1732220ba3adddf635996ef812c882858c187107be9a14ee8eb157ca1e686e7623c326f2c3da54933475bb6f5
7
- data.tar.gz: 692d5363b0cfeb5b52f7e5d46998835cf24b8e317743ac750d0c16cf6ee347f7bdd5d900f4d437bcb50e20f555ded080bab22d6aef75c00cdacab3710d403a35
6
+ metadata.gz: 689437f57eb521fe7b5458fde6ea43d2bfa86fba8b7cca2873c18e7d5ece8cbc5da72ea300ed8ac6ed373c113bc1e3e1ff48e1ecb8abc04e0712e7b7e4b28a88
7
+ data.tar.gz: 8269c8f1268008af6bedb3bec2eff776254430d440d9c71bd68243d1661f137392bfbfbe74d1ec5aaef78ce293637a080a3c713e2bcaaafcb4d0eff72887e213
data/.travis.yml CHANGED
@@ -17,7 +17,7 @@ matrix:
17
17
  gemfile: gemfiles/rails60.gemfile
18
18
  before_script:
19
19
  - cd spec/dummy
20
- - bundle exec rake db:migrate RAILS_ENV=test
20
+ - bundle exec rake db:create db:migrate db:seed RAILS_ENV=test
21
21
  - cd ../..
22
22
  script:
23
23
  - bundle exec rspec
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.2.0
4
+
5
+ * Register enum definitions in the class.
6
+ * Add configuration for method name prefix and suffix.
7
+ * Use ActiveRecord::Type for accessor definition.
8
+ * Raise errors if scope is used for non ActiveRecord class.
9
+ * Refactoring.
10
+
3
11
  ## 1.1.1
4
12
 
5
13
  * Fix lack of options when some enums have same translation.
data/README.md CHANGED
@@ -5,6 +5,7 @@ A ruby and rails extension to generate enum-like methods.
5
5
  ## Dependencies
6
6
 
7
7
  * ruby 2.3+
8
+ * activerecord 5.0+
8
9
  * activesupport 5.0+
9
10
 
10
11
  ## Installation
@@ -137,7 +138,22 @@ user.status #=> :_enable
137
138
  user.status_raw #=> "enable"
138
139
  ```
139
140
 
140
- ### Scope (for ActiveRecord)
141
+ ### ActiveRecord features
142
+
143
+ #### Accessor
144
+
145
+ Generate accessor:
146
+
147
+ ```ruby
148
+ class User < ActiveRecord::Base
149
+ extend EnumIsh
150
+ enum_ish :status, { _enable: 'enable', _disable: 'disable' }, accessor: true
151
+ end
152
+
153
+ User.where(status: :_enable) #=> SELECT "users".* FROM "users" WHERE "users"."status" = "enable"
154
+ ```
155
+
156
+ #### Scope
141
157
 
142
158
  Generate scope:
143
159
 
@@ -150,7 +166,7 @@ end
150
166
  User.with_status(:enable) #=> SELECT "users".* FROM "users" WHERE "users"."status" = "enable"
151
167
  ```
152
168
 
153
- ### Validation (for ActiveRecord)
169
+ #### Validation
154
170
 
155
171
  Generate validation:
156
172
 
@@ -0,0 +1,43 @@
1
+ require_relative 'enum_type'
2
+
3
+ module EnumIsh
4
+ module ActiveRecord
5
+ class Definer < EnumIsh::Definer
6
+ def define_default(enum)
7
+ method = "_enum_ish_init_#{enum.name}".to_sym
8
+
9
+ @klass.class_eval do
10
+ after_initialize method
11
+ define_method method do
12
+ if respond_to?(enum.name) && public_send(enum.name).nil?
13
+ default = enum.setting[:default]
14
+ default = instance_exec(&default) if default.kind_of?(Proc)
15
+ public_send("#{enum.name}=", default)
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def define_accessor(enum)
22
+ @klass.class_eval do
23
+ define_method "#{Config.raw_prefix}#{enum.name}#{Config.raw_suffix}" do
24
+ value = read_attribute(enum.name)
25
+ enum.mapping.fetch(value, value)
26
+ end
27
+
28
+ decorate_attribute_type(enum.name, :enum) do |subtype|
29
+ EnumIsh::ActiveRecord::EnumType.new(enum.name, enum.mapping, subtype)
30
+ end
31
+ end
32
+ end
33
+
34
+ def define_scope(enum)
35
+ @klass.class_eval do
36
+ scope "#{Config.scope_prefix}#{enum.name}#{Config.scope_suffix}", ->(value) {
37
+ where(enum.name => enum.mapping.fetch(value, value))
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ module EnumIsh
2
+ module ActiveRecord
3
+ class EnumType < ::ActiveRecord::Type::Value
4
+ def initialize(name, mapping, subtype)
5
+ @name = name
6
+ @mapping = mapping
7
+ @subtype = subtype
8
+ end
9
+
10
+ def cast(value)
11
+ if @mapping.has_key?(value)
12
+ value
13
+ elsif @mapping.has_value?(value)
14
+ @mapping.key(value)
15
+ else
16
+ value
17
+ end
18
+ end
19
+
20
+ def deserialize(value)
21
+ @mapping.key(@subtype.deserialize(value))
22
+ end
23
+
24
+ def serialize(value)
25
+ @mapping.fetch(value, value)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ module EnumIsh
2
+ class Config
3
+ @@options = {
4
+ text_prefix: '',
5
+ text_suffix: '_text',
6
+ options_prefix: '',
7
+ options_suffix: '_options',
8
+ raw_prefix: '',
9
+ raw_suffix: '_raw',
10
+ scope_prefix: 'with_',
11
+ scope_suffix: ''
12
+ }
13
+
14
+ @@options.keys.each do |key|
15
+ define_singleton_method "#{key}" do
16
+ @@options[key]
17
+ end
18
+
19
+ define_singleton_method "#{key}=" do |val|
20
+ @@options[key] = val
21
+ end
22
+ end
23
+
24
+ class << self
25
+ def configure
26
+ yield self
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,83 @@
1
+ module EnumIsh
2
+ class Definer
3
+ def initialize(klass)
4
+ @klass = klass
5
+ end
6
+
7
+ def define(enum)
8
+ [:text, :options, :predicate, :default, :accessor, :validate, :scope].each do |type|
9
+ send("define_#{type}", enum) if enum.setting[type]
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def define_text(enum)
16
+ @klass.class_eval do
17
+ define_method "#{Config.text_prefix}#{enum.name}#{Config.text_suffix}" do |options = {}|
18
+ value = public_send(enum.name)
19
+ dic = Dictionary.new(self.class).load(enum, options)
20
+ dic[value] || value
21
+ end
22
+ end
23
+ end
24
+
25
+ def define_options(enum)
26
+ @klass.class_eval do
27
+ define_singleton_method "#{Config.options_prefix}#{enum.name}#{Config.options_suffix}" do |options = {}|
28
+ dic = Dictionary.new(self).load(enum, options)
29
+ dic.to_a.map { |value, label| [label, value] }
30
+ end
31
+ end
32
+ end
33
+
34
+ def define_predicate(enum)
35
+ enum.mapping.each do |key, value|
36
+ @klass.class_eval do
37
+ define_method "#{enum.name}_#{key}?".tr('.', '_') do
38
+ public_send(enum.name) == value
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def define_default(enum)
45
+ mod = Module.new
46
+ mod.module_eval do
47
+ define_method :initialize do |*args|
48
+ if respond_to?(enum.name) && public_send(enum.name).nil?
49
+ default = enum.setting[:default]
50
+ default = instance_exec(&default) if default.kind_of?(Proc)
51
+ public_send("#{enum.name}=", default)
52
+ end
53
+ super(*args)
54
+ end
55
+ end
56
+ @klass.prepend mod
57
+ end
58
+
59
+ def define_accessor(enum)
60
+ @klass.class_eval do
61
+ define_method "#{Config.raw_prefix}#{enum.name}#{Config.raw_suffix}" do
62
+ instance_variable_get("@#{enum.name}")
63
+ end
64
+ define_method "#{enum.name}" do
65
+ enum.mapping.invert[instance_variable_get("@#{enum.name}")]
66
+ end
67
+ define_method "#{enum.name}=" do |value|
68
+ instance_variable_set("@#{enum.name}", enum.mapping.fetch(value, value))
69
+ end
70
+ end
71
+ end
72
+
73
+ def define_validate(enum)
74
+ @klass.class_eval do
75
+ validates enum.name, inclusion: { in: enum.mapping.values }, allow_nil: true
76
+ end
77
+ end
78
+
79
+ def define_scope(enum)
80
+ raise EnumIsh::Error.new(':scope option can be used for ActiveRecord only.')
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,49 @@
1
+ module EnumIsh
2
+ class Dictionary
3
+ def initialize(klass)
4
+ @klass = klass
5
+ end
6
+
7
+ def load(enum, options = {})
8
+ translated = translate(enum, options)
9
+ filter(translated, options)
10
+ end
11
+
12
+ private
13
+
14
+ def translate(enum, options)
15
+ dict = load_dict(enum, options)
16
+ translated = enum.mapping.map { |k, v| dict[k] ? [k, dict[k]] : [k, v.to_s] }.to_h
17
+ translated = translated.map { |k, v| [enum.mapping[k], v] }.to_h unless enum.setting[:accessor]
18
+ translated
19
+ end
20
+
21
+ def load_dict(enum, options)
22
+ key = i18n_key(enum, options)
23
+ dict = I18n.t("enum_ish.#{@klass.name.underscore}.#{key}", i18n_options(enum, options))
24
+ dict.map { |k, v| [k.to_s.to_sym, v.to_s] }.to_h
25
+ end
26
+
27
+ def i18n_key(enum, options)
28
+ [enum.name, options[:format]].compact.join('/')
29
+ end
30
+
31
+ def i18n_options(enum, options)
32
+ key = i18n_key(enum, options)
33
+ opts = options[:i18n_options] || {}
34
+ opts.merge(default: [:"enum_ish.defaults.#{key}", enum.mapping.invert])
35
+ end
36
+
37
+ def filter(translated, options)
38
+ if options[:except]
39
+ except = Array(options[:except])
40
+ translated.reject! { |k, v| except.include?(k) }
41
+ end
42
+ if options[:only]
43
+ only = Array(options[:only])
44
+ translated.select! { |k, v| only.include?(k) }
45
+ end
46
+ translated
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,34 @@
1
+ module EnumIsh
2
+ class Enum
3
+ attr_accessor :name
4
+ attr_accessor :mapping
5
+ attr_accessor :setting
6
+
7
+ def initialize(name, mapping, setting)
8
+ @name = name
9
+ @mapping = init_mapping(mapping)
10
+ @setting = init_setting(setting)
11
+ end
12
+
13
+ private
14
+
15
+ def init_mapping(mapping)
16
+ if mapping.is_a?(Array)
17
+ mapping.map do |v|
18
+ k = v.to_s.to_sym
19
+ v = v.to_s if v.is_a?(Symbol)
20
+ [k, v]
21
+ end.to_h
22
+ else
23
+ mapping
24
+ end
25
+ end
26
+
27
+ def init_setting(setting)
28
+ [:text, :options].each do |key|
29
+ setting[key] = true unless setting.key?(key)
30
+ end
31
+ setting
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ module EnumIsh
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -1,3 +1,3 @@
1
1
  module EnumIsh
2
- VERSION = '1.1.1'
2
+ VERSION = '1.2.0'
3
3
  end
data/lib/enum_ish.rb CHANGED
@@ -1,18 +1,24 @@
1
1
  require 'active_support'
2
2
  require 'enum_ish/version'
3
- require 'enum_ish/builder'
4
- require 'enum_ish/builder/active_record' if defined?(ActiveRecord::Base)
3
+ require 'enum_ish/errors'
4
+ require 'enum_ish/config'
5
+ require 'enum_ish/enum'
6
+ require 'enum_ish/dictionary'
7
+ require 'enum_ish/definer'
8
+ require 'enum_ish/active_record/definer' if defined?(::ActiveRecord::Base)
5
9
 
6
10
  module EnumIsh
7
- def enum_ish(attr, enum, config = {})
8
- [:text, :options].each do |key|
9
- config[key] = true unless config.key?(key)
10
- end
11
+ def enum_ish(name, map, config = {})
12
+ enum = Enum.new(name, map, config)
13
+
14
+ self.class_attribute :_enum_ish_enums unless self.respond_to?(:_enum_ish_enums)
15
+ self._enum_ish_enums ||= []
16
+ self._enum_ish_enums << enum
11
17
 
12
- if defined?(ActiveRecord::Base) && self.ancestors.include?(ActiveRecord::Base)
13
- Builder::ActiveRecord.new(self).build(attr, enum, config)
18
+ if defined?(::ActiveRecord::Base) && self.ancestors.include?(::ActiveRecord::Base)
19
+ ActiveRecord::Definer.new(self).define(enum)
14
20
  else
15
- Builder.new(self).build(attr, enum, config)
21
+ Definer.new(self).define(enum)
16
22
  end
17
23
  end
18
24
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enum_ish
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoshikazu Kaneta
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-31 00:00:00.000000000 Z
11
+ date: 2019-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -115,8 +115,13 @@ files:
115
115
  - gemfiles/rails52.gemfile
116
116
  - gemfiles/rails60.gemfile
117
117
  - lib/enum_ish.rb
118
- - lib/enum_ish/builder.rb
119
- - lib/enum_ish/builder/active_record.rb
118
+ - lib/enum_ish/active_record/definer.rb
119
+ - lib/enum_ish/active_record/enum_type.rb
120
+ - lib/enum_ish/config.rb
121
+ - lib/enum_ish/definer.rb
122
+ - lib/enum_ish/dictionary.rb
123
+ - lib/enum_ish/enum.rb
124
+ - lib/enum_ish/errors.rb
120
125
  - lib/enum_ish/version.rb
121
126
  homepage: https://github.com/kanety/enum_ish
122
127
  licenses: []
@@ -1,51 +0,0 @@
1
- module EnumIsh
2
- class Builder
3
- class ActiveRecord < Builder
4
- private
5
-
6
- def define_default_value(attr, enum, config)
7
- method = "_enum_ish_init_#{attr}".to_sym
8
-
9
- @klass.class_eval do
10
- after_initialize method
11
- define_method method do
12
- if respond_to?(attr) && public_send(attr).nil?
13
- default = if config[:default].kind_of?(Proc)
14
- instance_exec(&config[:default])
15
- else
16
- config[:default]
17
- end
18
- public_send("#{attr}=", default)
19
- end
20
- end
21
- end
22
- end
23
-
24
- def define_accessor(attr, enum, config)
25
- method = "#{attr}_raw"
26
-
27
- @klass.class_eval do
28
- define_method method do
29
- read_attribute(attr)
30
- end
31
- define_method "#{attr}" do
32
- enum.invert[read_attribute(attr)]
33
- end
34
- define_method "#{attr}=" do |value|
35
- write_attribute(attr, enum[value])
36
- end
37
- end
38
- end
39
-
40
- def define_scope(attr, enum, config)
41
- method = "with_#{attr}"
42
-
43
- @klass.class_eval do
44
- scope method, ->(value) {
45
- where(attr => (enum[value] || value))
46
- }
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,137 +0,0 @@
1
- module EnumIsh
2
- class Builder
3
- def initialize(klass)
4
- @klass = klass
5
- end
6
-
7
- def build(attr, enum, config = {})
8
- if enum.is_a?(Array)
9
- enum = enum.map { |v|
10
- k = v.to_s.to_sym
11
- v = v.to_s if v.is_a?(Symbol)
12
- [k, v]
13
- }.to_h
14
- end
15
-
16
- [:text, :options, :predicate, :accessor, :validate, :scope].each do |key|
17
- if config[key]
18
- send("define_#{key}", attr, enum, config)
19
- end
20
- end
21
-
22
- if config.key?(:default)
23
- define_default_value(attr, enum, config)
24
- end
25
- end
26
-
27
- private
28
-
29
- def define_text(attr, enum, config)
30
- method = "#{attr}_text"
31
-
32
- @klass.class_eval do
33
- define_method method do |options = {}|
34
- value = public_send(attr)
35
- dic = EnumIsh::Dictionary.new(self.class).load(attr, enum, config, options)
36
- dic[value] || value
37
- end
38
- end
39
- end
40
-
41
- def define_options(attr, enum, config)
42
- method = "#{attr}_options"
43
-
44
- @klass.class_eval do
45
- define_singleton_method method do |options = {}|
46
- dic = EnumIsh::Dictionary.new(self).load(attr, enum, config, options)
47
- dic.to_a.map { |value, label| [label, value] }
48
- end
49
- end
50
- end
51
-
52
- def define_predicate(attr, enum, config)
53
- enum.each do |key, value|
54
- method = "#{attr}_#{key}?".tr('.', '_')
55
- @klass.class_eval do
56
- define_method method do
57
- public_send(attr) == value
58
- end
59
- end
60
- end
61
- end
62
-
63
- def define_default_value(attr, enum, config)
64
- mod = Module.new
65
- mod.module_eval do
66
- define_method :initialize do |*args|
67
- if respond_to?(attr) && public_send(attr).nil?
68
- default = if config[:default].kind_of?(Proc)
69
- instance_exec(&config[:default])
70
- else
71
- config[:default]
72
- end
73
- public_send("#{attr}=", default)
74
- end
75
- super(*args)
76
- end
77
- end
78
- @klass.prepend mod
79
- end
80
-
81
- def define_accessor(attr, enum, config)
82
- method = "#{attr}_raw"
83
-
84
- @klass.class_eval do
85
- define_method method do
86
- instance_variable_get("@#{attr}")
87
- end
88
- define_method "#{attr}" do
89
- enum.invert[instance_variable_get("@#{attr}")]
90
- end
91
- define_method "#{attr}=" do |value|
92
- instance_variable_set("@#{attr}", enum[value])
93
- end
94
- end
95
- end
96
-
97
- def define_validate(attr, enum, config)
98
- @klass.class_eval do
99
- validates attr, inclusion: { in: enum.values }, allow_nil: true
100
- end
101
- end
102
- end
103
-
104
- class Dictionary
105
- def initialize(klass)
106
- @klass = klass
107
- end
108
-
109
- def load(attr, enum, config, options)
110
- dict = load_i18n(attr, enum, options)
111
-
112
- translated = enum.map { |k, v| dict[k] ? [k, dict[k]] : [k, v.to_s] }.to_h
113
- translated = translated.map { |k, v| [enum[k], v] }.to_h unless config[:accessor]
114
-
115
- if options[:except]
116
- except = Array(options[:except])
117
- translated.reject! { |k, v| except.include?(k) }
118
- end
119
- if options[:only]
120
- only = Array(options[:only])
121
- translated.select! { |k, v| only.include?(k) }
122
- end
123
-
124
- translated
125
- end
126
-
127
- private
128
-
129
- def load_i18n(attr, enum, options)
130
- attr_key = [attr, options[:format]].compact.join('/')
131
- i18n_options = (options[:i18n_options] || {}).merge(default: [:"enum_ish.defaults.#{attr_key}", enum.invert])
132
-
133
- dict = I18n.t("enum_ish.#{@klass.name.underscore}.#{attr_key}", i18n_options)
134
- dict.map { |k, v| [k.to_s.to_sym, v.to_s] }.to_h
135
- end
136
- end
137
- end