enum_ext 0.2.0 → 0.4.2
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 +9 -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 +180 -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: 364abab1ad0b759be15eb2353fbbca5d483d13ea65e4d9d9c407cdb0a9a7265d
|
|
4
|
+
data.tar.gz: 3653c710a34c0d02d1568e961ce667e6b0c005285b04f56a1debe2737944cdbe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d6960e5b712f3f48a84d5e746fa49c3354a78c433547750c9b2dd324da8a7b021d09eb8bdcfdc73d67e38aca67e4771b6fe8c1f6a57958b93db4b14b03e0780e
|
|
7
|
+
data.tar.gz: 7aa13f57d737196c809f05a8cb4cd1ee193a6ac06ff3516fa022af9210f789f461fd35c636fb524dcf777926643af53e231c9eb17ad9daad558866a699d9da6c
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 0.4.2
|
|
2
|
+
* bugfix for localize_enum multiple call
|
|
3
|
+
* test added for 0.4.1 ver funcitonality ( multiple times humanize_enum calls )
|
|
4
|
+
* all assert( a == b ) replaced with assert_equal(a,b)
|
|
5
|
+
|
|
6
|
+
# 0.4.1
|
|
7
|
+
* security dependency issues resolved
|
|
8
|
+
* activerecord version raised
|
|
9
|
+
* 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,231 +23,203 @@ 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.
|
|
43
|
-
|
|
44
|
-
# Request.non_payed.exists?(request) # >> true
|
|
45
|
-
# Request.delivery_set.exists?(request) # >> false
|
|
54
|
+
# request.on_delivery!
|
|
55
|
+
# request.delivery_set? # >> true
|
|
46
56
|
|
|
47
|
-
# Request.
|
|
57
|
+
# Request.delivery_set.exists?(request) # >> true
|
|
58
|
+
# Request.in_warehouse.exists?(request) # >> false
|
|
48
59
|
#
|
|
49
|
-
# Request.
|
|
50
|
-
#
|
|
51
|
-
# Request.
|
|
60
|
+
# Request.delivery_set_statuses # >> [:ready_for_shipment, :on_delivery, :delivered]
|
|
61
|
+
#
|
|
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
|
-
# ext_enum_sets :status, {
|
|
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
|
-
# }
|
|
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
|
+
# }
|
|
63
72
|
def ext_enum_sets( enum_name, options )
|
|
73
|
+
enum_plural = enum_name.to_s.pluralize
|
|
74
|
+
|
|
64
75
|
self.instance_eval do
|
|
65
76
|
options.each do |set_name, enum_vals|
|
|
66
|
-
|
|
77
|
+
# set_name scope
|
|
78
|
+
scope set_name, -> { where( enum_name => self.send( enum_plural ).slice( *enum_vals.map(&:to_s) ).values ) }
|
|
79
|
+
|
|
80
|
+
# with_enums scope
|
|
81
|
+
scope "with_#{enum_plural}", -> (sets_arr) {
|
|
82
|
+
where( enum_name => self.send( enum_plural ).slice(
|
|
83
|
+
*sets_arr.map{|set_name| self.try( "#{set_name}_#{enum_plural}" ) || set_name }.flatten.uniq.map(&:to_s) ).values )
|
|
84
|
+
} unless respond_to?("with_#{enum_plural}")
|
|
67
85
|
|
|
86
|
+
# without_enums scope
|
|
87
|
+
scope "without_#{enum_plural}", -> (sets_arr) {
|
|
88
|
+
where.not( id: self.send("with_#{enum_plural}", sets_arr) )
|
|
89
|
+
} unless respond_to?("without_#{enum_plural}")
|
|
68
90
|
|
|
69
|
-
|
|
91
|
+
|
|
92
|
+
# class.enum_set_values
|
|
93
|
+
define_singleton_method( "#{set_name}_#{enum_plural}" ) do
|
|
70
94
|
enum_vals
|
|
71
95
|
end
|
|
72
96
|
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
self.send(
|
|
97
|
+
# class.enum_set_enums_i
|
|
98
|
+
define_singleton_method( "#{set_name}_#{enum_plural}_i" ) do
|
|
99
|
+
self.send( "#{enum_plural}" ).slice( *self.send("#{set_name}_#{enum_plural}") ).values
|
|
76
100
|
end
|
|
77
101
|
|
|
78
|
-
#
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
102
|
+
# t_... - are translation dependent methods
|
|
103
|
+
# class.t_enums_options
|
|
104
|
+
define_singleton_method( "t_#{set_name}_#{enum_plural}_options" ) do
|
|
105
|
+
return [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2] unless respond_to?( "t_#{enum_plural}_options_raw" )
|
|
82
106
|
|
|
83
|
-
|
|
84
|
-
define_singleton_method( "t_#{set_name}_#{enum_name.to_s.pluralize}_options" ) do
|
|
85
|
-
send( "t_#{set_name}_#{enum_name.to_s.pluralize}" ).invert.to_a.map do | key_val |
|
|
86
|
-
key_val[0] = key_val[0].call if key_val[0].respond_to?(:call) && key_val[0].try(:arity) < 1
|
|
87
|
-
key_val
|
|
88
|
-
end
|
|
107
|
+
send("t_#{enum_plural}_options_raw", send("t_#{set_name}_#{enum_plural}") )
|
|
89
108
|
end
|
|
90
109
|
|
|
91
|
-
#
|
|
92
|
-
define_singleton_method( "#{set_name}_#{
|
|
93
|
-
|
|
110
|
+
# class.t_enums_options_i
|
|
111
|
+
define_singleton_method( "t_#{set_name}_#{enum_plural}_options_i" ) do
|
|
112
|
+
return [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2] unless respond_to?( "t_#{enum_plural}_options_raw_i" )
|
|
113
|
+
|
|
114
|
+
send("t_#{enum_plural}_options_raw_i", send("t_#{set_name}_#{enum_plural}") )
|
|
94
115
|
end
|
|
95
116
|
|
|
96
|
-
|
|
117
|
+
# instance.set_name?
|
|
118
|
+
define_method "#{set_name}?" do
|
|
119
|
+
self.send(enum_name) && ( enum_vals.include?( self.send(enum_name) ) || enum_vals.include?( self.send(enum_name).to_sym ))
|
|
120
|
+
end
|
|
97
121
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
122
|
+
# protected?
|
|
123
|
+
# class.t_setname_enums ( translations or humanizations subset for a given set )
|
|
124
|
+
define_singleton_method( "t_#{set_name}_#{enum_plural}" ) do
|
|
125
|
+
return [(["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2)].to_h unless respond_to?( "t_#{enum_plural}" )
|
|
102
126
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
send( "t_#{enum_plural}" ).slice( *self.send("#{set_name}_#{enum_plural}") )
|
|
128
|
+
end
|
|
129
|
+
end
|
|
106
130
|
end
|
|
107
131
|
end
|
|
108
132
|
|
|
109
133
|
# Ex mass_assign_enum
|
|
110
|
-
# Used for mass assigning for collection
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
# If you need callbacks you can do like this: some_scope.each(&:new_stat!) but if you don't need callbacks and you
|
|
134
|
+
# Used for mass assigning for collection without callbacks it creates bang methods for collections using update_all.
|
|
135
|
+
# it's often case when you need bulk update without callbacks, so it's gets frustrating to repeat:
|
|
136
|
+
# some_scope.update_all(status: Request.statuses[:new_status], update_at: Time.now)
|
|
137
|
+
# 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
|
|
138
|
+
# to change at once you need update_all
|
|
114
139
|
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
140
|
+
# mass_assign_enum( :status )
|
|
141
|
+
#
|
|
142
|
+
# class methods:
|
|
143
|
+
# in_cart! paid! in_warehouse! and so
|
|
119
144
|
#
|
|
120
145
|
# Console:
|
|
121
146
|
# request1.in_cart!
|
|
122
147
|
# request2.waiting_for_payment!
|
|
123
|
-
# Request.
|
|
124
|
-
# request1.
|
|
125
|
-
# request2.
|
|
148
|
+
# Request.with_statuses( :in_cart, :waiting_for_payment ).payed!
|
|
149
|
+
# request1.paid? # >> true
|
|
150
|
+
# request2.paid? # >> true
|
|
126
151
|
# request1.updated_at # >> Time.now
|
|
127
152
|
# defined?(Request::MassAssignEnum) # >> true
|
|
128
153
|
#
|
|
129
|
-
# order.requests.
|
|
130
|
-
# order.requests.
|
|
154
|
+
# order.requests.paid.all?(&:paid?) # >> true
|
|
155
|
+
# order.requests.paid.delivered!
|
|
131
156
|
# order.requests.map(&:status).uniq #>> [:delivered]
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
# Rem:
|
|
135
|
-
# mass_assign_enum accepts additional options as last argument.
|
|
136
|
-
# calling mass_assign_enum( :status ) actually is equal to call: mass_assign_enum( :status, { relation: true, association_relation: true } )
|
|
137
|
-
#
|
|
138
|
-
# Meaning:
|
|
139
|
-
|
|
140
|
-
# relation: true - Request.some_scope.payed! - works
|
|
141
157
|
|
|
142
|
-
|
|
143
|
-
# but it wouldn't works without 'scope' part! If you want to use it without 'scope' you may do it this way:
|
|
144
|
-
# class Request
|
|
145
|
-
# ...
|
|
146
|
-
# mass_assign_enum( :status, association_relation: false )
|
|
147
|
-
# end
|
|
148
|
-
# class Order
|
|
149
|
-
# has_many :requests, extend: Request::MassAssignEnum
|
|
150
|
-
# end
|
|
151
|
-
#
|
|
152
|
-
# Order.first.requests.respond_to?(:in_cart!) # >> true
|
|
153
|
-
#
|
|
154
|
-
# Rem2:
|
|
155
|
-
# 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
|
|
156
|
-
|
|
157
|
-
def mass_assign_enum( *options )
|
|
158
|
-
relation_options = (options[-1].is_a?(Hash) && options.pop || {relation: true, association_relation: true} ).with_indifferent_access
|
|
159
|
-
enums_names = options
|
|
158
|
+
def mass_assign_enum( *enums_names )
|
|
160
159
|
enums_names.each do |enum_name|
|
|
161
160
|
enum_vals = self.send( enum_name.to_s.pluralize )
|
|
162
161
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
enum_vals.keys.each do |enum_el|
|
|
167
|
-
define_method( "#{enum_el}!" ) do
|
|
168
|
-
self.update_all( {enum_name => enum_vals[enum_el]}.merge( self.column_names.include?('updated_at') ? {updated_at: Time.now} : {} ))
|
|
169
|
-
end
|
|
162
|
+
enum_vals.keys.each do |enum_el|
|
|
163
|
+
define_singleton_method( "#{enum_el}!" ) do
|
|
164
|
+
self.update_all( {enum_name => enum_vals[enum_el]}.merge( self.column_names.include?('updated_at') ? {updated_at: Time.now} : {} ))
|
|
170
165
|
end
|
|
171
166
|
end
|
|
172
|
-
self.const_set( :MassAssignEnum, mass_ass_module ) unless defined?(self::MassAssignEnum)
|
|
173
|
-
|
|
174
|
-
self::ActiveRecord_Relation.include( self::MassAssignEnum ) if relation_options[:relation]
|
|
175
|
-
self::ActiveRecord_AssociationRelation.include( self::MassAssignEnum ) if relation_options[:association_relation]
|
|
176
167
|
end
|
|
177
168
|
end
|
|
178
169
|
|
|
179
|
-
# Ex using localize_enum with Request
|
|
180
|
-
# class Request
|
|
181
|
-
#
|
|
182
170
|
# if app doesn't need internationalization, it may use humanize_enum to make enum user friendly
|
|
183
|
-
#
|
|
171
|
+
# class Request
|
|
184
172
|
# humanize_enum :status, {
|
|
185
173
|
# #locale dependent example with pluralization and lambda:
|
|
186
174
|
# payed: -> (t_self) { I18n.t("request.status.payed", count: t_self.sum ) }
|
|
187
175
|
#
|
|
188
176
|
# #locale dependent example with pluralization and proc:
|
|
189
|
-
# payed:
|
|
177
|
+
# payed: Proc.new{ I18n.t("request.status.payed", count: self.sum ) }
|
|
190
178
|
#
|
|
191
179
|
# #locale independent:
|
|
192
180
|
# ready_for_shipment: "Ready to go!"
|
|
193
181
|
# }
|
|
194
182
|
# end
|
|
195
183
|
#
|
|
184
|
+
# Could be called multiple times, all humanization definitions will be merged under the hood:
|
|
185
|
+
# humanize_enum :status, {
|
|
186
|
+
# payed: I18n.t("scope.#{status}")
|
|
187
|
+
# }
|
|
188
|
+
# humanize_enum :status, {
|
|
189
|
+
# billed: I18n.t("scope.#{status}")
|
|
190
|
+
# }
|
|
191
|
+
#
|
|
192
|
+
#
|
|
196
193
|
# Example with block:
|
|
197
194
|
#
|
|
198
195
|
# humanize_enum :status do
|
|
199
196
|
# I18n.t("scope.#{status}")
|
|
200
197
|
# end
|
|
201
|
-
|
|
198
|
+
#
|
|
199
|
+
# in views select:
|
|
200
|
+
# f.select :status, Request.t_statuses_options
|
|
201
|
+
#
|
|
202
|
+
# in select in Active Admin filter
|
|
203
|
+
# collection: Request.t_statuses_options_i
|
|
204
|
+
#
|
|
205
|
+
# Rem: select options breaks when using lambda() with params
|
|
206
|
+
#
|
|
202
207
|
# Console:
|
|
203
208
|
# request.sum = 3
|
|
204
209
|
# request.payed!
|
|
205
|
-
# request.status
|
|
206
|
-
# request.t_status
|
|
210
|
+
# request.status # >> payed
|
|
211
|
+
# request.t_status # >> "Payed 3 dollars"
|
|
207
212
|
# Request.t_statuses # >> { in_cart: -> { I18n.t("request.status.in_cart") }, .... }
|
|
208
|
-
|
|
209
|
-
# if you need some substitution you can go like this
|
|
210
|
-
# localize_enum :status, {
|
|
211
|
-
# ..
|
|
212
|
-
# delivered: "Delivered at: %{date}"
|
|
213
|
-
# }
|
|
214
|
-
# request.delivered!
|
|
215
|
-
# request.t_status % {date: Time.now.to_s} # >> Delivered at: 05.02.2016
|
|
216
|
-
#
|
|
217
|
-
# Using in select:
|
|
218
|
-
# f.select :status, Request.t_statuses_options
|
|
219
|
-
#
|
|
220
|
-
# Rem: select options breaks when using lambda
|
|
221
|
-
|
|
222
213
|
def humanize_enum( *args, &block )
|
|
223
214
|
enum_name = args.shift
|
|
224
215
|
localizations = args.pop
|
|
225
|
-
|
|
216
|
+
enum_plural = enum_name.to_s.pluralize
|
|
226
217
|
|
|
227
218
|
self.instance_eval do
|
|
228
219
|
|
|
229
|
-
#t_enums
|
|
230
|
-
define_singleton_method( "t_#{enum_pural}" ) do
|
|
231
|
-
# if localization is abscent than block must be given
|
|
232
|
-
localizations.try(:with_indifferent_access) || localizations ||
|
|
233
|
-
send(enum_pural).keys.map {|en| [en, self.new( {enum_name => en} ).send("t_#{enum_name}")] }.to_h
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
#t_enums_options
|
|
237
|
-
define_singleton_method( "t_#{enum_pural}_options" ) do
|
|
238
|
-
send("t_#{enum_pural}").invert.to_a.map do | key_val |
|
|
239
|
-
# since all procs in t_enum are evaluated in context of a record than it's not always possible to create select options
|
|
240
|
-
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
|
|
241
|
-
key_val
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
|
|
245
220
|
#t_enum
|
|
246
221
|
define_method "t_#{enum_name}" do
|
|
247
|
-
t = block || localizations.try(:with_indifferent_access)[send(enum_name)]
|
|
222
|
+
t = block || @@localizations.try(:with_indifferent_access)[send(enum_name)]
|
|
248
223
|
if t.try(:lambda?)
|
|
249
224
|
t.try(:arity) == 1 && t.call( self ) || t.try(:call)
|
|
250
225
|
elsif t.is_a?(Proc)
|
|
@@ -253,6 +228,54 @@ module EnumExt
|
|
|
253
228
|
t
|
|
254
229
|
end.to_s
|
|
255
230
|
end
|
|
231
|
+
|
|
232
|
+
@@localizations ||= {}.with_indifferent_access
|
|
233
|
+
# if localization is abscent than block must be given
|
|
234
|
+
@@localizations.merge!(
|
|
235
|
+
localizations.try(:with_indifferent_access) ||
|
|
236
|
+
localizations ||
|
|
237
|
+
send(enum_plural).keys.map{|en| [en, Proc.new{ self.new({ enum_name => en }).send("t_#{enum_name}") }] }.to_h.with_indifferent_access
|
|
238
|
+
)
|
|
239
|
+
#t_enums
|
|
240
|
+
define_singleton_method( "t_#{enum_plural}" ) do
|
|
241
|
+
@@localizations
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
#t_enums_options
|
|
245
|
+
define_singleton_method( "t_#{enum_plural}_options" ) do
|
|
246
|
+
send("t_#{enum_plural}_options_raw", send("t_#{enum_plural}") )
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
#t_enums_options_i
|
|
250
|
+
define_singleton_method( "t_#{enum_plural}_options_i" ) do
|
|
251
|
+
send("t_#{enum_plural}_options_raw_i", send("t_#{enum_plural}") )
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
define_method "t_#{enum_name}=" do |new_val|
|
|
255
|
+
send("#{enum_name}=", new_val)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
#protected?
|
|
259
|
+
define_singleton_method( "t_#{enum_plural}_options_raw_i" ) do |t_enum_set|
|
|
260
|
+
send("t_#{enum_plural}_options_raw", t_enum_set ).map do | key_val |
|
|
261
|
+
key_val[1] = send(enum_plural)[key_val[1]]
|
|
262
|
+
key_val
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
define_singleton_method( "t_#{enum_plural}_options_raw" ) do |t_enum_set|
|
|
267
|
+
t_enum_set.invert.to_a.map do | key_val |
|
|
268
|
+
# since all procs in t_enum are evaluated in context of a record than it's not always possible to create select options
|
|
269
|
+
if key_val[0].respond_to?(:call)
|
|
270
|
+
if key_val[0].try(:arity) < 1
|
|
271
|
+
key_val[0] = key_val[0].try(:call) rescue "Cannot create option for #{key_val[1]} ( proc fails to evaluate )"
|
|
272
|
+
else
|
|
273
|
+
key_val[0] = "Cannot create option for #{key_val[1]} because of a lambda"
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
key_val
|
|
277
|
+
end
|
|
278
|
+
end
|
|
256
279
|
end
|
|
257
280
|
end
|
|
258
281
|
alias localize_enum humanize_enum
|
|
@@ -262,33 +285,23 @@ module EnumExt
|
|
|
262
285
|
# If block is given than no scopes are taken in consider
|
|
263
286
|
def translate_enum( *args, &block )
|
|
264
287
|
enum_name = args.shift
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
translated_enums << enum_name.to_sym
|
|
288
|
+
enum_plural = enum_name.to_s.pluralize
|
|
289
|
+
t_scope = args.pop || "activerecord.attributes.#{self.name.underscore}.#{enum_plural}"
|
|
268
290
|
|
|
269
291
|
if block_given?
|
|
270
292
|
humanize_enum( enum_name, &block )
|
|
271
293
|
else
|
|
272
|
-
humanize_enum( enum_name, send(
|
|
294
|
+
humanize_enum( enum_name, send(enum_plural).keys.map{|en| [ en, Proc.new{ I18n.t("#{t_scope}.#{en}") }] }.to_h )
|
|
273
295
|
end
|
|
274
|
-
|
|
275
296
|
end
|
|
276
297
|
|
|
277
|
-
#
|
|
278
|
-
#
|
|
279
|
-
#
|
|
298
|
+
# human_attribute_name is redefined for automation like this:
|
|
299
|
+
# p #{object.class.human_attribute_name( attr_name )}:
|
|
300
|
+
# p object.send(attr_name)
|
|
280
301
|
def human_attribute_name( name, options = {} )
|
|
281
|
-
|
|
302
|
+
# if name starts from t_ and there is a column with the last part then ...
|
|
303
|
+
name[0..1] == 't_' && column_names.include?(name[2..-1]) ? super( name[2..-1], options ) : super( name, options )
|
|
282
304
|
end
|
|
283
305
|
|
|
284
|
-
# helper to determine is attribute is translated enum
|
|
285
|
-
def enum_translated?( name )
|
|
286
|
-
translated_enums.include?( name.to_sym )
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
private
|
|
290
|
-
def translated_enums
|
|
291
|
-
@translated_enums ||= Set.new
|
|
292
|
-
end
|
|
293
306
|
|
|
294
307
|
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.2
|
|
4
|
+
version: 0.4.2
|
|
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-04 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: []
|