enum_ext 0.2.1 → 0.4.3
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 +5 -5
- data/.travis.yml +6 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +68 -23
- data/README.md +142 -146
- data/Rakefile +8 -1
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/enum_ext.gemspec +9 -5
- data/lib/enum_ext.rb +177 -167
- data/lib/enum_ext/version.rb +1 -1
- metadata +76 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 74e124aa3c4dcc444a79313a06d79aa55862a58c065a00b399797413e11ca13a
|
|
4
|
+
data.tar.gz: ba28311cd931ba1233585407f8532c5026a7dee51b3a695c3c36a8fd675ec27e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 103d98798be494d1fc9b8354dc84b4bad405d282c3aa83be42cee4800775bc20b848294d913cf682be5e7e25db03bb34fb402bd296e9376d461e1cd29a369b3f
|
|
7
|
+
data.tar.gz: '081f7581316c5904826f0daf2aa023a8a959900c0cbe4ea3d16a64831602d17ff5b267260d8ff97da62e4c83676a07ae28996b767fd7dd120c73dce7484d09f5'
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# 0.4.3
|
|
2
|
+
* ext_enum_sets now can go without options just to define with and without scopes
|
|
3
|
+
|
|
4
|
+
# 0.4.2
|
|
5
|
+
* bugfix for localize_enum multiple call
|
|
6
|
+
* test added for 0.4.1 ver funcitonality ( multiple times humanize_enum calls )
|
|
7
|
+
* all assert( a == b ) replaced with assert_equal(a,b)
|
|
8
|
+
|
|
9
|
+
# 0.4.1
|
|
10
|
+
* security dependency issues resolved
|
|
11
|
+
* activerecord version raised
|
|
12
|
+
* humanize_enum could be called multiple times all definitions will be merged, i.e. there is no need to define all localizations in one place at once
|
data/Gemfile.lock
CHANGED
|
@@ -1,42 +1,87 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
enum_ext (0.1
|
|
5
|
-
activerecord (>= 4.
|
|
4
|
+
enum_ext (0.4.1)
|
|
5
|
+
activerecord (>= 5.2.4.3)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
actionpack (6.0.3.4)
|
|
11
|
+
actionview (= 6.0.3.4)
|
|
12
|
+
activesupport (= 6.0.3.4)
|
|
13
|
+
rack (~> 2.0, >= 2.0.8)
|
|
14
|
+
rack-test (>= 0.6.3)
|
|
15
|
+
rails-dom-testing (~> 2.0)
|
|
16
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
17
|
+
actionview (6.0.3.4)
|
|
18
|
+
activesupport (= 6.0.3.4)
|
|
12
19
|
builder (~> 3.1)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
erubi (~> 1.4)
|
|
21
|
+
rails-dom-testing (~> 2.0)
|
|
22
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
23
|
+
activemodel (6.0.3.4)
|
|
24
|
+
activesupport (= 6.0.3.4)
|
|
25
|
+
activerecord (6.0.3.4)
|
|
26
|
+
activemodel (= 6.0.3.4)
|
|
27
|
+
activesupport (= 6.0.3.4)
|
|
28
|
+
activesupport (6.0.3.4)
|
|
29
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
30
|
+
i18n (>= 0.7, < 2)
|
|
20
31
|
minitest (~> 5.1)
|
|
21
|
-
thread_safe (~> 0.3, >= 0.3.4)
|
|
22
32
|
tzinfo (~> 1.1)
|
|
23
|
-
|
|
24
|
-
builder (3.2.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
|
34
|
+
builder (3.2.4)
|
|
35
|
+
byebug (11.1.3)
|
|
36
|
+
concurrent-ruby (1.1.7)
|
|
37
|
+
crass (1.0.6)
|
|
38
|
+
erubi (1.10.0)
|
|
39
|
+
i18n (1.8.5)
|
|
40
|
+
concurrent-ruby (~> 1.0)
|
|
41
|
+
loofah (2.8.0)
|
|
42
|
+
crass (~> 1.0.2)
|
|
43
|
+
nokogiri (>= 1.5.9)
|
|
44
|
+
method_source (1.0.0)
|
|
45
|
+
mini_portile2 (2.4.0)
|
|
46
|
+
minitest (5.14.2)
|
|
47
|
+
nokogiri (1.10.10)
|
|
48
|
+
mini_portile2 (~> 2.4.0)
|
|
49
|
+
rack (2.2.3)
|
|
50
|
+
rack-test (1.1.0)
|
|
51
|
+
rack (>= 1.0, < 3)
|
|
52
|
+
rails-dom-testing (2.0.3)
|
|
53
|
+
activesupport (>= 4.2.0)
|
|
54
|
+
nokogiri (>= 1.6)
|
|
55
|
+
rails-html-sanitizer (1.3.0)
|
|
56
|
+
loofah (~> 2.3)
|
|
57
|
+
rails-i18n (6.0.0)
|
|
58
|
+
i18n (>= 0.7, < 2)
|
|
59
|
+
railties (>= 6.0.0, < 7)
|
|
60
|
+
railties (6.0.3.4)
|
|
61
|
+
actionpack (= 6.0.3.4)
|
|
62
|
+
activesupport (= 6.0.3.4)
|
|
63
|
+
method_source
|
|
64
|
+
rake (>= 0.8.7)
|
|
65
|
+
thor (>= 0.20.3, < 2.0)
|
|
66
|
+
rake (13.0.1)
|
|
67
|
+
sqlite3 (1.4.2)
|
|
68
|
+
thor (1.0.1)
|
|
69
|
+
thread_safe (0.3.6)
|
|
70
|
+
tzinfo (1.2.8)
|
|
31
71
|
thread_safe (~> 0.1)
|
|
72
|
+
zeitwerk (2.4.2)
|
|
32
73
|
|
|
33
74
|
PLATFORMS
|
|
34
75
|
ruby
|
|
35
76
|
|
|
36
77
|
DEPENDENCIES
|
|
37
|
-
bundler (
|
|
78
|
+
bundler (>= 1.11)
|
|
79
|
+
byebug
|
|
38
80
|
enum_ext!
|
|
39
|
-
|
|
81
|
+
minitest
|
|
82
|
+
rails-i18n (>= 4)
|
|
83
|
+
rake (>= 10.0)
|
|
84
|
+
sqlite3
|
|
40
85
|
|
|
41
86
|
BUNDLED WITH
|
|
42
|
-
1.
|
|
87
|
+
2.1.4
|
data/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# EnumExt
|
|
2
2
|
|
|
3
|
-
EnumExt extends rails enum
|
|
3
|
+
EnumExt extends rails enum with localization/translation and it's helpers, mass-assign on scopes with bang, advanced sets logic over existing enum.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
Add this line to your application's Gemfile:
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
gem 'enum_ext', '~> 0.
|
|
10
|
+
gem 'enum_ext', '~> 0.3'
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
And then execute:
|
|
@@ -19,23 +19,26 @@ Or install it yourself as:
|
|
|
19
19
|
$ gem install enum_ext
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
|
-
To use enum extension extend main model class with EnumExt module,
|
|
22
|
+
To use enum extension extend main model class with EnumExt module,
|
|
23
|
+
and customize your enums the way you need:
|
|
23
24
|
|
|
24
25
|
class SomeModel
|
|
25
26
|
extend EnumExt
|
|
26
27
|
|
|
28
|
+
enum_i ...
|
|
27
29
|
humanize_enum ...
|
|
28
30
|
translate_enum ...
|
|
29
31
|
ext_enum_sets ...
|
|
30
32
|
mass_assign_enum ...
|
|
31
33
|
end
|
|
32
34
|
|
|
33
|
-
Let's assume that we have model Request representing some buying requests with enum **status**, and we have model Order with requests,
|
|
35
|
+
Let's assume that we have model Request representing some buying requests with enum **status**, and we have model Order with requests,
|
|
36
|
+
representing single purchase, like this:
|
|
34
37
|
|
|
35
38
|
class Request
|
|
36
39
|
extend EnumExt
|
|
37
40
|
belongs_to :order
|
|
38
|
-
enum status: [ :in_cart, :waiting_for_payment, :
|
|
41
|
+
enum status: [ :in_cart, :waiting_for_payment, :paid, :ready_for_shipment, :on_delivery, :delivered ]
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
class Order
|
|
@@ -46,48 +49,67 @@ Or install it yourself as:
|
|
|
46
49
|
|
|
47
50
|
### Humanization (humanize_enum)
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
...
|
|
51
|
-
localize_enum :status, {
|
|
52
|
-
|
|
53
|
-
#locale dependent example with internal pluralization and lambda:
|
|
54
|
-
payed: -> (t_self) { I18n.t("request.status.payed", count: t_self.sum ) }
|
|
55
|
-
|
|
56
|
-
#locale dependent example with internal pluralization and proc:
|
|
57
|
-
payed: proc { I18n.t("request.status.payed", count: sum ) }
|
|
58
|
-
|
|
59
|
-
#locale independent:
|
|
60
|
-
ready_for_shipment: "Ready to go!"
|
|
61
|
-
}
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
Console:
|
|
65
|
-
|
|
66
|
-
request.sum = 3
|
|
67
|
-
request.payed!
|
|
68
|
-
request.status # >> payed
|
|
69
|
-
request.t_status # >> "Payed 3 dollars"
|
|
70
|
-
Request.t_statuses # >> { in_cart: -> { I18n.t("request.status.in_cart") }, .... }
|
|
71
|
-
|
|
72
|
-
If you need some substitution you can go like this:
|
|
52
|
+
if app doesn't need internationalization, it may use humanize_enum to make enum user friendly
|
|
73
53
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
54
|
+
```
|
|
55
|
+
humanize_enum :status, {
|
|
56
|
+
#locale dependent example with pluralization and lambda:
|
|
57
|
+
in_cart: -> (t_self) { I18n.t("request.status.in_cart", count: t_self.sum ) }
|
|
58
|
+
|
|
59
|
+
#locale dependent example with pluralization and proc:
|
|
60
|
+
paid: Proc.new{ I18n.t("request.status.paid", count: self.sum ) }
|
|
61
|
+
|
|
62
|
+
#locale independent:
|
|
63
|
+
ready_for_shipment: "Ready to go!"
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This humanize_enum adds to instance:
|
|
69
|
+
- t_in_cart, t_paid, t_ready_for_shipment
|
|
70
|
+
|
|
71
|
+
and to class:
|
|
72
|
+
- t_statuses - as given or generated values
|
|
73
|
+
- t_statuses_options - translated enum values options for select input
|
|
74
|
+
- t_statuses_options_i - same as above but use int values with translations works for ActiveAdmin filters for instance
|
|
83
75
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
f.select :status, Request.t_delivery_set_statuses_options
|
|
76
|
+
|
|
77
|
+
Example with block:
|
|
87
78
|
|
|
79
|
+
```
|
|
80
|
+
humanize_enum :status do
|
|
81
|
+
I18n.t("scope.#{status}")
|
|
82
|
+
end
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Example for select:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
f.select :status, Request.t_statuses_options
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
in Active Admin filters
|
|
92
|
+
```
|
|
93
|
+
filter :status, as: :select, label: 'Status', collection: Request.t_statuses_options_i
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
Rem: select options may break when using lambda() or proc with instance method, but will survive with block
|
|
98
|
+
|
|
99
|
+
Console:
|
|
100
|
+
```
|
|
101
|
+
request.sum = 3
|
|
102
|
+
request.paid!
|
|
103
|
+
request.status # >> paid
|
|
104
|
+
request.t_status # >> "paid 3 dollars"
|
|
105
|
+
Request.t_statuses # >> { in_cart: -> { I18n.t("request.status.in_cart") }, .... }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Could be called multiple times, all humanization definitions will be merged under the hood.
|
|
109
|
+
|
|
88
110
|
### Translate (translate_enum)
|
|
89
111
|
|
|
90
|
-
Enum is translated using scope 'active_record.attributes.class_name_underscore.
|
|
112
|
+
Enum is translated using scope 'active_record.attributes.class_name_underscore.enum_plural', or the given one:
|
|
91
113
|
|
|
92
114
|
translate_enum :status, 'active_record.request.enum'
|
|
93
115
|
|
|
@@ -97,9 +119,6 @@ Or it can be done with block either with translate or humanize:
|
|
|
97
119
|
I18n.t( "active_record.request.enum.#{status}" )
|
|
98
120
|
end
|
|
99
121
|
|
|
100
|
-
Also since we place by default enum translation in same place as enum name translation
|
|
101
|
-
human_attribute_name is redefined so it will work fine in ActiveAdmin, but you need to add translation to locale.
|
|
102
|
-
|
|
103
122
|
### Enum to_i shortcut ( enum_i )
|
|
104
123
|
|
|
105
124
|
Defines method enum_name_i shortcut for Model.enum_names[elem.enum_name]
|
|
@@ -107,125 +126,99 @@ Defines method enum_name_i shortcut for Model.enum_names[elem.enum_name]
|
|
|
107
126
|
**Ex**
|
|
108
127
|
enum_i :status
|
|
109
128
|
...
|
|
110
|
-
request.
|
|
129
|
+
request.paid_i # 10
|
|
111
130
|
|
|
112
131
|
|
|
113
132
|
### Enum Sets (ext_enum_sets)
|
|
114
133
|
|
|
115
|
-
**Use-case** For example you have pay bills of different types, and you want to group some types in debit and credit "super-types",
|
|
116
|
-
|
|
117
|
-
You can do this with method **ext_enum_sets**, it creates: scopes for subsets like enum did, instance method with ? similar to enum methods, and so...
|
|
134
|
+
**Use-case** For example you have pay bills of different types, and you want to group some types in debit and credit "super-types",
|
|
135
|
+
and have scope PayBill.debit, instance method with question mark as usual enum does pay_bill.debit?.
|
|
118
136
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
Request.non_payed_statuses # >> [:in_cart, :waiting_for_payment]
|
|
145
|
-
|
|
146
|
-
Request.with_statuses( :payed, :in_cart ) # >> scope for all in_cart and payed requests
|
|
147
|
-
Request.without_statuses( :payed ) # >> scope for all requests with statuses not eq to payed
|
|
148
|
-
Request.without_statuses( :payed, :non_payed ) # >> scope all requests with statuses not eq to payed and in_cart + waiting_for_payment
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
#### Rem:
|
|
152
|
-
|
|
153
|
-
You can call ext_enum_sets more than one time defining a superposition of already defined sets:
|
|
154
|
-
|
|
155
|
-
class Request
|
|
156
|
-
...
|
|
157
|
-
ext_enum_sets (... first time you call ext_enum_sets )
|
|
158
|
-
ext_enum_sets :status, {
|
|
159
|
-
already_payed: ( [:payed] | delivery_set_statuses ),
|
|
160
|
-
outside_wharehouse: ( delivery_set_statuses - in_warehouse_statuses )... # any other array operations like &, + and so can be used
|
|
161
|
-
}
|
|
162
|
-
|
|
137
|
+
You can do this with method **ext_enum_sets** it creates: scopes for subsets, instance method with ? and some class methods helpers
|
|
138
|
+
|
|
139
|
+
For this call:
|
|
140
|
+
```
|
|
141
|
+
ext_enum_sets :status, {
|
|
142
|
+
delivery_set: [:ready_for_shipment, :on_delivery, :delivered] # for shipping department for example
|
|
143
|
+
in_warehouse: [:ready_for_shipment] # this just for superposition example below
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
it will generate:
|
|
147
|
+
```
|
|
148
|
+
instance:
|
|
149
|
+
- methods: delivery_set?, in_warehouse?
|
|
150
|
+
|
|
151
|
+
class:
|
|
152
|
+
- named scopes: delivery_set, in_warehouse
|
|
153
|
+
- parametrized scopes: with_statuses, without_statuses
|
|
154
|
+
class helpers:
|
|
155
|
+
- delivery_set_statuses (=[:ready_for_shipment, :on_delivery, :delivered] ), in_warehouse_statuses
|
|
156
|
+
- delivery_set_statuses_i (= [3,4,5]), in_warehouse_statuses_i (=[3])
|
|
157
|
+
|
|
158
|
+
class translation helpers ( started with t_... ):
|
|
159
|
+
- t_delivery_set_statuses_options (= [['translation or humanization', :ready_for_shipment] ...] ) for select inputs purposes
|
|
160
|
+
- t_delivery_set_statuses_options_i (= [['translation or humanization', 3] ...]) same as above but with integer as value ( for example to use in Active admin filters )
|
|
161
|
+
```
|
|
163
162
|
|
|
164
|
-
|
|
163
|
+
```
|
|
164
|
+
Console:
|
|
165
|
+
request.on_delivery!
|
|
166
|
+
request.delivery_set? # >> true
|
|
165
167
|
|
|
166
|
-
|
|
168
|
+
Request.delivery_set.exists?(request) # >> true
|
|
169
|
+
Request.in_warehouse.exists?(request) # >> false
|
|
170
|
+
|
|
171
|
+
Request.delivery_set_statuses # >> [:ready_for_shipment, :on_delivery, :delivered]
|
|
172
|
+
|
|
173
|
+
Request.with_statuses( :payed, :delivery_set ) # >> :payed and [:ready_for_shipment, :on_delivery, :delivered] requests
|
|
174
|
+
Request.without_statuses( :payed ) # >> scope for all requests with statuses not eq to :payed
|
|
175
|
+
Request.without_statuses( :payed, :in_warehouse ) # >> scope all requests with statuses not eq to :payed or :ready_for_shipment
|
|
176
|
+
```
|
|
167
177
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
Rem:
|
|
179
|
+
ext_enum_sets can be called twice defining a superposition of already defined sets ( considering previous example ):
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
ext_enum_sets :status, {
|
|
183
|
+
outside_wharehouse: ( delivery_set_statuses - in_warehouse_statuses )... any other array operations like &, + and so can be used
|
|
184
|
+
}
|
|
185
|
+
```
|
|
175
186
|
|
|
176
|
-
Console:
|
|
177
187
|
|
|
178
|
-
|
|
179
|
-
request2.waiting_for_payment!
|
|
180
|
-
Request.non_payed.payed!
|
|
181
|
-
request1.payed? # >> true
|
|
182
|
-
request2.payed? # >> true
|
|
183
|
-
request1.updated_at # >> ~ Time.now
|
|
184
|
-
defined?(Request::MassAssignEnum) # >> true
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
order.requests.already_payed.count # >> N
|
|
188
|
-
order.requests.delivered.count # >> M
|
|
189
|
-
order.requests.already_payed.delivered!
|
|
190
|
-
order.requests.already_payed.count # >> 0
|
|
191
|
-
order.requests.delivered.count # >> N + M
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
####Rem:
|
|
196
|
-
|
|
197
|
-
**mass_assign_enum** accepts additional options as last argument. Calling
|
|
188
|
+
### Mass-assign ( mass_assign_enum )
|
|
198
189
|
|
|
199
|
-
|
|
190
|
+
Syntax sugar for mass-assigning enum values.
|
|
200
191
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
192
|
+
**Use-case:** it's often case when I need bulk update without callbacks, so it's gets frustrating to repeat:
|
|
193
|
+
```
|
|
194
|
+
some_scope.update_all(status: Request.statuses[:new_status], update_at: Time.now)
|
|
195
|
+
```
|
|
196
|
+
If you need callbacks you can do like this: some_scope.each(&:new_stat!) but if you don't need callbacks and you
|
|
197
|
+
have hundreds and thousands of records to change at once you need update_all
|
|
206
198
|
|
|
207
|
-
|
|
199
|
+
```
|
|
200
|
+
mass_assign_enum( :status )
|
|
201
|
+
```
|
|
208
202
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
**but it wouldn't work without 'scope' part!** If you want to use it without 'scope' you may do it this way:
|
|
212
|
-
|
|
213
|
-
class Request
|
|
214
|
-
...
|
|
215
|
-
mass_assign_enum( :status, relation: true, association_relation: false )
|
|
216
|
-
end
|
|
203
|
+
Console:
|
|
217
204
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
205
|
+
```
|
|
206
|
+
request1.in_cart!
|
|
207
|
+
request2.waiting_for_payment!
|
|
208
|
+
Request.non_paid.paid!
|
|
209
|
+
request1.paid? # >> true
|
|
210
|
+
request2.paid? # >> true
|
|
211
|
+
request1.updated_at # >> ~ Time.now
|
|
221
212
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
213
|
+
order.requests.already_paid.count # >> N
|
|
214
|
+
order.requests.delivered.count # >> M
|
|
215
|
+
order.requests.already_paid.delivered!
|
|
216
|
+
order.requests.already_paid.count # >> 0
|
|
217
|
+
order.requests.delivered.count # >> N + M
|
|
218
|
+
```
|
|
226
219
|
|
|
227
220
|
## Tests
|
|
228
|
-
|
|
221
|
+
rake test
|
|
229
222
|
|
|
230
223
|
## Development
|
|
231
224
|
|
|
@@ -239,3 +232,6 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/alekse
|
|
|
239
232
|
|
|
240
233
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
241
234
|
|
|
235
|
+
### Thanks
|
|
236
|
+
|
|
237
|
+
Thanks for the star vzamanillo, it inspires me to do mass refactor and gracefully cover code in this gem by tests.
|
data/Rakefile
CHANGED
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "enum_ext"
|
|
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
data/enum_ext.gemspec
CHANGED
|
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.authors = ["alekseyl"]
|
|
10
10
|
spec.email = ["leshchuk@gmail.com"]
|
|
11
11
|
|
|
12
|
-
spec.summary = %q{Enum extension, ads enum sets, mass-assign
|
|
13
|
-
spec.description = %q{Enum extension, ads enum sets, mass-assign
|
|
12
|
+
spec.summary = %q{Enum extension, ads enum sets, mass-assign, localization, and some sugar helpers.}
|
|
13
|
+
spec.description = %q{Enum extension, ads enum sets, mass-assign, localization, and some sugar helpers.}
|
|
14
14
|
spec.homepage = "https://github.com/alekseyl/enum_ext"
|
|
15
15
|
spec.license = "MIT"
|
|
16
16
|
|
|
@@ -19,8 +19,12 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
20
20
|
spec.require_paths = ["lib"]
|
|
21
21
|
|
|
22
|
-
spec.add_dependency
|
|
22
|
+
spec.add_dependency 'activerecord', ">= 5.2.4.3"
|
|
23
23
|
|
|
24
|
-
spec.add_development_dependency
|
|
25
|
-
spec.add_development_dependency
|
|
24
|
+
spec.add_development_dependency 'minitest'
|
|
25
|
+
spec.add_development_dependency 'bundler', '>= 1.11'
|
|
26
|
+
spec.add_development_dependency 'rake', '>= 10.0'
|
|
27
|
+
spec.add_development_dependency 'rails-i18n', '>=4'
|
|
28
|
+
spec.add_development_dependency 'sqlite3'
|
|
29
|
+
spec.add_development_dependency 'byebug'
|
|
26
30
|
end
|
data/lib/enum_ext.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'enum_ext/version'
|
|
2
2
|
|
|
3
3
|
# Let's assume we have model Request with enum status, and we have model Order with requests like this:
|
|
4
4
|
# class Request
|
|
5
5
|
# extend EnumExt
|
|
6
6
|
# belongs_to :order
|
|
7
|
-
# enum status:
|
|
7
|
+
# enum status: { in_cart: 0, waiting_for_payment: 1, payed: 2, ready_for_shipment: 3, on_delivery: 4, delivered: 5 }
|
|
8
8
|
# end
|
|
9
9
|
#
|
|
10
10
|
# class Order
|
|
@@ -13,6 +13,9 @@ require "enum_ext/version"
|
|
|
13
13
|
#
|
|
14
14
|
module EnumExt
|
|
15
15
|
|
|
16
|
+
# defines shortcut for getting integer value of enum.
|
|
17
|
+
# for enum named 'status' will generate:
|
|
18
|
+
# instance.status_i
|
|
16
19
|
def enum_i( enum_name )
|
|
17
20
|
define_method "#{enum_name}_i" do
|
|
18
21
|
self.class.send("#{enum_name.to_s.pluralize}")[send(enum_name)].to_i
|
|
@@ -20,233 +23,202 @@ module EnumExt
|
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
|
|
23
|
-
#
|
|
24
|
-
# This method intend for creating and using some sets of enum values
|
|
25
|
-
# it creates: scopes for subsets
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
# #class scopes: non_payed, delivery_set, in_warehouse
|
|
31
|
-
# #class scopes: with_statuses, without_statuses
|
|
32
|
-
# #class non_payed_statuses, delivery_set_statuses ( = [:in_cart, :waiting_for_payment], [:ready_for_shipment, :on_delivery, :delivered].. )
|
|
26
|
+
# ext_enum_sets
|
|
27
|
+
# This method intend for creating and using some sets of enum values
|
|
28
|
+
# it creates: scopes for subsets,
|
|
29
|
+
# instance method with ?,
|
|
30
|
+
# and some class methods helpers
|
|
31
|
+
#
|
|
32
|
+
# For this call:
|
|
33
33
|
# ext_enum_sets :status, {
|
|
34
|
-
# non_payed: [:in_cart, :waiting_for_payment],
|
|
35
34
|
# delivery_set: [:ready_for_shipment, :on_delivery, :delivered] # for shipping department for example
|
|
36
|
-
# in_warehouse: [:ready_for_shipment]
|
|
35
|
+
# in_warehouse: [:ready_for_shipment] # this scope is just for superposition example below
|
|
37
36
|
# }
|
|
38
|
-
#
|
|
37
|
+
#
|
|
38
|
+
# it will generate:
|
|
39
|
+
# instance:
|
|
40
|
+
# methods: delivery_set?, in_warehouse?
|
|
41
|
+
# class:
|
|
42
|
+
# named scopes: delivery_set, in_warehouse
|
|
43
|
+
# parametrized scopes: with_statuses, without_statuses
|
|
44
|
+
# class helpers:
|
|
45
|
+
# - delivery_set_statuses (=[:ready_for_shipment, :on_delivery, :delivered] ), in_warehouse_statuses
|
|
46
|
+
# - delivery_set_statuses_i (= [3,4,5]), in_warehouse_statuses_i (=[3])
|
|
47
|
+
# class translation helpers ( started with t_... )
|
|
48
|
+
# for select inputs purposes:
|
|
49
|
+
# - t_delivery_set_statuses_options (= [['translation or humanization', :ready_for_shipment] ...])
|
|
50
|
+
# same as above but with integer as value ( for example to use in Active admin filters )
|
|
51
|
+
# - t_delivery_set_statuses_options_i (= [['translation or humanization', 3] ...])
|
|
39
52
|
|
|
40
53
|
# Console:
|
|
41
|
-
# request.
|
|
42
|
-
# request.
|
|
54
|
+
# request.on_delivery!
|
|
55
|
+
# request.delivery_set? # >> true
|
|
43
56
|
|
|
44
|
-
# Request.
|
|
45
|
-
# Request.
|
|
46
|
-
|
|
47
|
-
# Request.
|
|
57
|
+
# Request.delivery_set.exists?(request) # >> true
|
|
58
|
+
# Request.in_warehouse.exists?(request) # >> false
|
|
59
|
+
#
|
|
60
|
+
# Request.delivery_set_statuses # >> [:ready_for_shipment, :on_delivery, :delivered]
|
|
48
61
|
#
|
|
49
|
-
# Request.with_statuses( :payed, :
|
|
50
|
-
# Request.without_statuses( :payed )
|
|
51
|
-
# Request.without_statuses( :payed, :
|
|
62
|
+
# Request.with_statuses( :payed, :delivery_set ) # >> :payed and [:ready_for_shipment, :on_delivery, :delivered] requests
|
|
63
|
+
# Request.without_statuses( :payed ) # >> scope for all requests with statuses not eq to :payed
|
|
64
|
+
# Request.without_statuses( :payed, :in_warehouse ) # >> scope all requests with statuses not eq to :payed or :ready_for_shipment
|
|
52
65
|
#
|
|
53
66
|
|
|
54
67
|
#Rem:
|
|
55
|
-
# ext_enum_sets can be called twice defining a
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
# already_payed: ( [:payed] | delivery_set_statuses ),
|
|
61
|
-
# outside_wharehouse: ( delivery_set_statuses - in_warehouse_statuses )... any other array operations like &, + and so can be used
|
|
62
|
-
# }
|
|
63
|
-
def ext_enum_sets( enum_name, options )
|
|
68
|
+
# ext_enum_sets can be called twice defining a superposition of already defined sets ( considering previous example ):
|
|
69
|
+
# ext_enum_sets :status, {
|
|
70
|
+
# outside_wharehouse: ( delivery_set_statuses - in_warehouse_statuses )... any other array operations like &, + and so can be used
|
|
71
|
+
# }
|
|
72
|
+
def ext_enum_sets( enum_name, options = {} )
|
|
64
73
|
enum_plural = enum_name.to_s.pluralize
|
|
65
74
|
|
|
66
75
|
self.instance_eval do
|
|
76
|
+
# with_enums scope
|
|
77
|
+
scope "with_#{enum_plural}", -> (sets_arr) {
|
|
78
|
+
where( enum_name => self.send( enum_plural ).slice(
|
|
79
|
+
*sets_arr.map{|set_name| self.try( "#{set_name}_#{enum_plural}" ) || set_name }.flatten.uniq.map(&:to_s) ).values )
|
|
80
|
+
} unless respond_to?("with_#{enum_plural}")
|
|
81
|
+
|
|
82
|
+
# without_enums scope
|
|
83
|
+
scope "without_#{enum_plural}", -> (sets_arr) {
|
|
84
|
+
where.not( id: self.send("with_#{enum_plural}", sets_arr) )
|
|
85
|
+
} unless respond_to?("without_#{enum_plural}")
|
|
86
|
+
|
|
67
87
|
options.each do |set_name, enum_vals|
|
|
88
|
+
# set_name scope
|
|
68
89
|
scope set_name, -> { where( enum_name => self.send( enum_plural ).slice( *enum_vals.map(&:to_s) ).values ) }
|
|
69
90
|
|
|
70
|
-
|
|
91
|
+
# class.enum_set_values
|
|
71
92
|
define_singleton_method( "#{set_name}_#{enum_plural}" ) do
|
|
72
93
|
enum_vals
|
|
73
94
|
end
|
|
74
95
|
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
self.send(
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# t_set_enums
|
|
81
|
-
define_singleton_method( "t_#{set_name}_#{enum_plural}" ) do
|
|
82
|
-
send( "t_#{enum_plural}" ).slice( *self.send("#{set_name}_#{enum_plural}") )
|
|
96
|
+
# class.enum_set_enums_i
|
|
97
|
+
define_singleton_method( "#{set_name}_#{enum_plural}_i" ) do
|
|
98
|
+
self.send( "#{enum_plural}" ).slice( *self.send("#{set_name}_#{enum_plural}") ).values
|
|
83
99
|
end
|
|
84
100
|
|
|
85
|
-
#
|
|
101
|
+
# t_... - are translation dependent methods
|
|
102
|
+
# class.t_enums_options
|
|
86
103
|
define_singleton_method( "t_#{set_name}_#{enum_plural}_options" ) do
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
end
|
|
104
|
+
return [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2] unless respond_to?( "t_#{enum_plural}_options_raw" )
|
|
105
|
+
|
|
106
|
+
send("t_#{enum_plural}_options_raw", send("t_#{set_name}_#{enum_plural}") )
|
|
91
107
|
end
|
|
92
108
|
|
|
93
|
-
#
|
|
94
|
-
define_singleton_method( "#{set_name}_#{enum_plural}
|
|
95
|
-
|
|
109
|
+
# class.t_enums_options_i
|
|
110
|
+
define_singleton_method( "t_#{set_name}_#{enum_plural}_options_i" ) do
|
|
111
|
+
return [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2] unless respond_to?( "t_#{enum_plural}_options_raw_i" )
|
|
112
|
+
|
|
113
|
+
send("t_#{enum_plural}_options_raw_i", send("t_#{set_name}_#{enum_plural}") )
|
|
96
114
|
end
|
|
97
115
|
|
|
98
|
-
|
|
116
|
+
# instance.set_name?
|
|
117
|
+
define_method "#{set_name}?" do
|
|
118
|
+
self.send(enum_name) && ( enum_vals.include?( self.send(enum_name) ) || enum_vals.include?( self.send(enum_name).to_sym ))
|
|
119
|
+
end
|
|
99
120
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
121
|
+
# protected?
|
|
122
|
+
# class.t_setname_enums ( translations or humanizations subset for a given set )
|
|
123
|
+
define_singleton_method( "t_#{set_name}_#{enum_plural}" ) do
|
|
124
|
+
return [(["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2)].to_h unless respond_to?( "t_#{enum_plural}" )
|
|
104
125
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
126
|
+
send( "t_#{enum_plural}" ).slice( *self.send("#{set_name}_#{enum_plural}") )
|
|
127
|
+
end
|
|
128
|
+
end
|
|
108
129
|
end
|
|
109
130
|
end
|
|
110
131
|
|
|
111
132
|
# Ex mass_assign_enum
|
|
112
|
-
# Used for mass assigning for collection
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
# If you need callbacks you can do like this: some_scope.each(&:new_stat!) but if you don't need callbacks and you
|
|
133
|
+
# Used for mass assigning for collection without callbacks it creates bang methods for collections using update_all.
|
|
134
|
+
# it's often case when you need bulk update without callbacks, so it's gets frustrating to repeat:
|
|
135
|
+
# some_scope.update_all(status: Request.statuses[:new_status], update_at: Time.now)
|
|
136
|
+
# If you need callbacks you can do like this: some_scope.each(&:new_stat!) but if you don't need callbacks and you have lots of records
|
|
137
|
+
# to change at once you need update_all
|
|
116
138
|
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
139
|
+
# mass_assign_enum( :status )
|
|
140
|
+
#
|
|
141
|
+
# class methods:
|
|
142
|
+
# in_cart! paid! in_warehouse! and so
|
|
121
143
|
#
|
|
122
144
|
# Console:
|
|
123
145
|
# request1.in_cart!
|
|
124
146
|
# request2.waiting_for_payment!
|
|
125
|
-
# Request.
|
|
126
|
-
# request1.
|
|
127
|
-
# request2.
|
|
147
|
+
# Request.with_statuses( :in_cart, :waiting_for_payment ).payed!
|
|
148
|
+
# request1.paid? # >> true
|
|
149
|
+
# request2.paid? # >> true
|
|
128
150
|
# request1.updated_at # >> Time.now
|
|
129
151
|
# defined?(Request::MassAssignEnum) # >> true
|
|
130
152
|
#
|
|
131
|
-
# order.requests.
|
|
132
|
-
# order.requests.
|
|
153
|
+
# order.requests.paid.all?(&:paid?) # >> true
|
|
154
|
+
# order.requests.paid.delivered!
|
|
133
155
|
# order.requests.map(&:status).uniq #>> [:delivered]
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
# Rem:
|
|
137
|
-
# mass_assign_enum accepts additional options as last argument.
|
|
138
|
-
# calling mass_assign_enum( :status ) actually is equal to call: mass_assign_enum( :status, { relation: true, association_relation: true } )
|
|
139
|
-
#
|
|
140
|
-
# Meaning:
|
|
141
|
-
|
|
142
|
-
# relation: true - Request.some_scope.payed! - works
|
|
143
|
-
|
|
144
|
-
# association_relation: true - Order.first.requests.scope.new_stat! - works
|
|
145
|
-
# but it wouldn't works without 'scope' part! If you want to use it without 'scope' you may do it this way:
|
|
146
|
-
# class Request
|
|
147
|
-
# ...
|
|
148
|
-
# mass_assign_enum( :status, association_relation: false )
|
|
149
|
-
# end
|
|
150
|
-
# class Order
|
|
151
|
-
# has_many :requests, extend: Request::MassAssignEnum
|
|
152
|
-
# end
|
|
153
|
-
#
|
|
154
|
-
# Order.first.requests.respond_to?(:in_cart!) # >> true
|
|
155
|
-
#
|
|
156
|
-
# Rem2:
|
|
157
|
-
# you can mass-assign more than one enum ::MassAssignEnum module will contain mass assign for both. It will break nothing since all enum name must be uniq across model
|
|
158
156
|
|
|
159
|
-
def mass_assign_enum( *
|
|
160
|
-
relation_options = (options[-1].is_a?(Hash) && options.pop || {relation: true, association_relation: true} ).with_indifferent_access
|
|
161
|
-
enums_names = options
|
|
157
|
+
def mass_assign_enum( *enums_names )
|
|
162
158
|
enums_names.each do |enum_name|
|
|
163
159
|
enum_vals = self.send( enum_name.to_s.pluralize )
|
|
164
160
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
enum_vals.keys.each do |enum_el|
|
|
169
|
-
define_method( "#{enum_el}!" ) do
|
|
170
|
-
self.update_all( {enum_name => enum_vals[enum_el]}.merge( self.column_names.include?('updated_at') ? {updated_at: Time.now} : {} ))
|
|
171
|
-
end
|
|
161
|
+
enum_vals.keys.each do |enum_el|
|
|
162
|
+
define_singleton_method( "#{enum_el}!" ) do
|
|
163
|
+
self.update_all( {enum_name => enum_vals[enum_el]}.merge( self.column_names.include?('updated_at') ? {updated_at: Time.now} : {} ))
|
|
172
164
|
end
|
|
173
165
|
end
|
|
174
|
-
self.const_set( :MassAssignEnum, mass_ass_module ) unless defined?(self::MassAssignEnum)
|
|
175
|
-
|
|
176
|
-
self::ActiveRecord_Relation.include( self::MassAssignEnum ) if relation_options[:relation]
|
|
177
|
-
self::ActiveRecord_AssociationRelation.include( self::MassAssignEnum ) if relation_options[:association_relation]
|
|
178
166
|
end
|
|
179
167
|
end
|
|
180
168
|
|
|
181
|
-
# Ex using localize_enum with Request
|
|
182
|
-
# class Request
|
|
183
|
-
#
|
|
184
169
|
# if app doesn't need internationalization, it may use humanize_enum to make enum user friendly
|
|
185
|
-
#
|
|
170
|
+
# class Request
|
|
186
171
|
# humanize_enum :status, {
|
|
187
172
|
# #locale dependent example with pluralization and lambda:
|
|
188
173
|
# payed: -> (t_self) { I18n.t("request.status.payed", count: t_self.sum ) }
|
|
189
174
|
#
|
|
190
175
|
# #locale dependent example with pluralization and proc:
|
|
191
|
-
# payed:
|
|
176
|
+
# payed: Proc.new{ I18n.t("request.status.payed", count: self.sum ) }
|
|
192
177
|
#
|
|
193
178
|
# #locale independent:
|
|
194
179
|
# ready_for_shipment: "Ready to go!"
|
|
195
180
|
# }
|
|
196
181
|
# end
|
|
197
182
|
#
|
|
183
|
+
# Could be called multiple times, all humanization definitions will be merged under the hood:
|
|
184
|
+
# humanize_enum :status, {
|
|
185
|
+
# payed: I18n.t("scope.#{status}")
|
|
186
|
+
# }
|
|
187
|
+
# humanize_enum :status, {
|
|
188
|
+
# billed: I18n.t("scope.#{status}")
|
|
189
|
+
# }
|
|
190
|
+
#
|
|
191
|
+
#
|
|
198
192
|
# Example with block:
|
|
199
193
|
#
|
|
200
194
|
# humanize_enum :status do
|
|
201
195
|
# I18n.t("scope.#{status}")
|
|
202
196
|
# end
|
|
203
|
-
|
|
197
|
+
#
|
|
198
|
+
# in views select:
|
|
199
|
+
# f.select :status, Request.t_statuses_options
|
|
200
|
+
#
|
|
201
|
+
# in select in Active Admin filter
|
|
202
|
+
# collection: Request.t_statuses_options_i
|
|
203
|
+
#
|
|
204
|
+
# Rem: select options breaks when using lambda() with params
|
|
205
|
+
#
|
|
204
206
|
# Console:
|
|
205
207
|
# request.sum = 3
|
|
206
208
|
# request.payed!
|
|
207
|
-
# request.status
|
|
208
|
-
# request.t_status
|
|
209
|
+
# request.status # >> payed
|
|
210
|
+
# request.t_status # >> "Payed 3 dollars"
|
|
209
211
|
# Request.t_statuses # >> { in_cart: -> { I18n.t("request.status.in_cart") }, .... }
|
|
210
|
-
|
|
211
|
-
# if you need some substitution you can go like this
|
|
212
|
-
# localize_enum :status, {
|
|
213
|
-
# ..
|
|
214
|
-
# delivered: "Delivered at: %{date}"
|
|
215
|
-
# }
|
|
216
|
-
# request.delivered!
|
|
217
|
-
# request.t_status % {date: Time.now.to_s} # >> Delivered at: 05.02.2016
|
|
218
|
-
#
|
|
219
|
-
# Using in select:
|
|
220
|
-
# f.select :status, Request.t_statuses_options
|
|
221
|
-
#
|
|
222
|
-
# Rem: select options breaks when using lambda
|
|
223
|
-
|
|
224
212
|
def humanize_enum( *args, &block )
|
|
225
213
|
enum_name = args.shift
|
|
226
214
|
localizations = args.pop
|
|
227
|
-
|
|
215
|
+
enum_plural = enum_name.to_s.pluralize
|
|
228
216
|
|
|
229
217
|
self.instance_eval do
|
|
230
218
|
|
|
231
|
-
#t_enums
|
|
232
|
-
define_singleton_method( "t_#{enum_pural}" ) do
|
|
233
|
-
# if localization is abscent than block must be given
|
|
234
|
-
localizations.try(:with_indifferent_access) || localizations ||
|
|
235
|
-
send(enum_pural).keys.map {|en| [en, self.new( {enum_name => en} ).send("t_#{enum_name}")] }.to_h.with_indifferent_access
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
#t_enums_options
|
|
239
|
-
define_singleton_method( "t_#{enum_pural}_options" ) do
|
|
240
|
-
send("t_#{enum_pural}").invert.to_a.map do | key_val |
|
|
241
|
-
# since all procs in t_enum are evaluated in context of a record than it's not always possible to create select options
|
|
242
|
-
key_val[0] = ( key_val[0].try(:call) || "Cannot create option for #{key_val[0]}" ) if key_val[0].respond_to?(:call) && key_val[0].try(:arity) < 1
|
|
243
|
-
key_val
|
|
244
|
-
end
|
|
245
|
-
end
|
|
246
|
-
|
|
247
219
|
#t_enum
|
|
248
220
|
define_method "t_#{enum_name}" do
|
|
249
|
-
t = block || localizations.try(:with_indifferent_access)[send(enum_name)]
|
|
221
|
+
t = block || @@localizations.try(:with_indifferent_access)[send(enum_name)]
|
|
250
222
|
if t.try(:lambda?)
|
|
251
223
|
t.try(:arity) == 1 && t.call( self ) || t.try(:call)
|
|
252
224
|
elsif t.is_a?(Proc)
|
|
@@ -255,6 +227,54 @@ module EnumExt
|
|
|
255
227
|
t
|
|
256
228
|
end.to_s
|
|
257
229
|
end
|
|
230
|
+
|
|
231
|
+
@@localizations ||= {}.with_indifferent_access
|
|
232
|
+
# if localization is abscent than block must be given
|
|
233
|
+
@@localizations.merge!(
|
|
234
|
+
localizations.try(:with_indifferent_access) ||
|
|
235
|
+
localizations ||
|
|
236
|
+
send(enum_plural).keys.map{|en| [en, Proc.new{ self.new({ enum_name => en }).send("t_#{enum_name}") }] }.to_h.with_indifferent_access
|
|
237
|
+
)
|
|
238
|
+
#t_enums
|
|
239
|
+
define_singleton_method( "t_#{enum_plural}" ) do
|
|
240
|
+
@@localizations
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
#t_enums_options
|
|
244
|
+
define_singleton_method( "t_#{enum_plural}_options" ) do
|
|
245
|
+
send("t_#{enum_plural}_options_raw", send("t_#{enum_plural}") )
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
#t_enums_options_i
|
|
249
|
+
define_singleton_method( "t_#{enum_plural}_options_i" ) do
|
|
250
|
+
send("t_#{enum_plural}_options_raw_i", send("t_#{enum_plural}") )
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
define_method "t_#{enum_name}=" do |new_val|
|
|
254
|
+
send("#{enum_name}=", new_val)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
#protected?
|
|
258
|
+
define_singleton_method( "t_#{enum_plural}_options_raw_i" ) do |t_enum_set|
|
|
259
|
+
send("t_#{enum_plural}_options_raw", t_enum_set ).map do | key_val |
|
|
260
|
+
key_val[1] = send(enum_plural)[key_val[1]]
|
|
261
|
+
key_val
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
define_singleton_method( "t_#{enum_plural}_options_raw" ) do |t_enum_set|
|
|
266
|
+
t_enum_set.invert.to_a.map do | key_val |
|
|
267
|
+
# since all procs in t_enum are evaluated in context of a record than it's not always possible to create select options
|
|
268
|
+
if key_val[0].respond_to?(:call)
|
|
269
|
+
if key_val[0].try(:arity) < 1
|
|
270
|
+
key_val[0] = key_val[0].try(:call) rescue "Cannot create option for #{key_val[1]} ( proc fails to evaluate )"
|
|
271
|
+
else
|
|
272
|
+
key_val[0] = "Cannot create option for #{key_val[1]} because of a lambda"
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
key_val
|
|
276
|
+
end
|
|
277
|
+
end
|
|
258
278
|
end
|
|
259
279
|
end
|
|
260
280
|
alias localize_enum humanize_enum
|
|
@@ -264,33 +284,23 @@ module EnumExt
|
|
|
264
284
|
# If block is given than no scopes are taken in consider
|
|
265
285
|
def translate_enum( *args, &block )
|
|
266
286
|
enum_name = args.shift
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
translated_enums << enum_name.to_sym
|
|
287
|
+
enum_plural = enum_name.to_s.pluralize
|
|
288
|
+
t_scope = args.pop || "activerecord.attributes.#{self.name.underscore}.#{enum_plural}"
|
|
270
289
|
|
|
271
290
|
if block_given?
|
|
272
291
|
humanize_enum( enum_name, &block )
|
|
273
292
|
else
|
|
274
|
-
humanize_enum( enum_name, send(
|
|
293
|
+
humanize_enum( enum_name, send(enum_plural).keys.map{|en| [ en, Proc.new{ I18n.t("#{t_scope}.#{en}") }] }.to_h )
|
|
275
294
|
end
|
|
276
|
-
|
|
277
295
|
end
|
|
278
296
|
|
|
279
|
-
#
|
|
280
|
-
#
|
|
281
|
-
#
|
|
297
|
+
# human_attribute_name is redefined for automation like this:
|
|
298
|
+
# p #{object.class.human_attribute_name( attr_name )}:
|
|
299
|
+
# p object.send(attr_name)
|
|
282
300
|
def human_attribute_name( name, options = {} )
|
|
283
|
-
|
|
301
|
+
# if name starts from t_ and there is a column with the last part then ...
|
|
302
|
+
name[0..1] == 't_' && column_names.include?(name[2..-1]) ? super( name[2..-1], options ) : super( name, options )
|
|
284
303
|
end
|
|
285
304
|
|
|
286
|
-
# helper to determine is attribute is translated enum
|
|
287
|
-
def enum_translated?( name )
|
|
288
|
-
translated_enums.include?( name.to_sym )
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
private
|
|
292
|
-
def translated_enums
|
|
293
|
-
@translated_enums ||= Set.new
|
|
294
|
-
end
|
|
295
305
|
|
|
296
306
|
end
|
data/lib/enum_ext/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: enum_ext
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- alekseyl
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-12-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -16,44 +16,100 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 5.2.4.3
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 5.2.4.3
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: minitest
|
|
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'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
42
|
name: bundler
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
30
44
|
requirements:
|
|
31
|
-
- - "
|
|
45
|
+
- - ">="
|
|
32
46
|
- !ruby/object:Gem::Version
|
|
33
47
|
version: '1.11'
|
|
34
48
|
type: :development
|
|
35
49
|
prerelease: false
|
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
51
|
requirements:
|
|
38
|
-
- - "
|
|
52
|
+
- - ">="
|
|
39
53
|
- !ruby/object:Gem::Version
|
|
40
54
|
version: '1.11'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: rake
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
44
58
|
requirements:
|
|
45
|
-
- - "
|
|
59
|
+
- - ">="
|
|
46
60
|
- !ruby/object:Gem::Version
|
|
47
61
|
version: '10.0'
|
|
48
62
|
type: :development
|
|
49
63
|
prerelease: false
|
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
65
|
requirements:
|
|
52
|
-
- - "
|
|
66
|
+
- - ">="
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
68
|
version: '10.0'
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rails-i18n
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '4'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '4'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: sqlite3
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: byebug
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
description: Enum extension, ads enum sets, mass-assign, localization, and some sugar
|
|
112
|
+
helpers.
|
|
57
113
|
email:
|
|
58
114
|
- leshchuk@gmail.com
|
|
59
115
|
executables: []
|
|
@@ -61,11 +117,15 @@ extensions: []
|
|
|
61
117
|
extra_rdoc_files: []
|
|
62
118
|
files:
|
|
63
119
|
- ".gitignore"
|
|
120
|
+
- ".travis.yml"
|
|
121
|
+
- CHANGELOG.md
|
|
64
122
|
- Gemfile
|
|
65
123
|
- Gemfile.lock
|
|
66
124
|
- LICENSE.txt
|
|
67
125
|
- README.md
|
|
68
126
|
- Rakefile
|
|
127
|
+
- bin/console
|
|
128
|
+
- bin/setup
|
|
69
129
|
- enum_ext.gemspec
|
|
70
130
|
- lib/enum_ext.rb
|
|
71
131
|
- lib/enum_ext/version.rb
|
|
@@ -73,7 +133,7 @@ homepage: https://github.com/alekseyl/enum_ext
|
|
|
73
133
|
licenses:
|
|
74
134
|
- MIT
|
|
75
135
|
metadata: {}
|
|
76
|
-
post_install_message:
|
|
136
|
+
post_install_message:
|
|
77
137
|
rdoc_options: []
|
|
78
138
|
require_paths:
|
|
79
139
|
- lib
|
|
@@ -88,9 +148,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
88
148
|
- !ruby/object:Gem::Version
|
|
89
149
|
version: '0'
|
|
90
150
|
requirements: []
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
signing_key:
|
|
151
|
+
rubygems_version: 3.1.2
|
|
152
|
+
signing_key:
|
|
94
153
|
specification_version: 4
|
|
95
|
-
summary: Enum extension, ads enum sets, mass-assign
|
|
154
|
+
summary: Enum extension, ads enum sets, mass-assign, localization, and some sugar
|
|
155
|
+
helpers.
|
|
96
156
|
test_files: []
|