enum_ish 1.1.1 → 1.2.0

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