enum_ext 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/enum_ext.rb +197 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4ffa90e0370f3f30ab49c181e22ae0912d145f13
4
+ data.tar.gz: eddf18cc4c9387c5598725692659fff237ab8d56
5
+ SHA512:
6
+ metadata.gz: 508e8720112409a3a3f68ff4274e09d8900a68e6154d921b5c2009e5cc599f9cefb3d621ac091a0d7dddd5363b49fedf5a28e36b3688285494768f21bfc36cc8
7
+ data.tar.gz: 65ff47b4104d223e663e6db03660a4d2c6378560fc174f5720ecd172c86f0045b60cc8130b99b29c1909a99aecb6ec6e05fc124c6180f451532dd88cc87e7e25
data/lib/enum_ext.rb ADDED
@@ -0,0 +1,197 @@
1
+ # Let's assume we have model Request with enum status, and we have model Order with requests like this:
2
+ # class Request
3
+ # extend EnumExt
4
+ # belongs_to :order
5
+ # enum status: [ :in_cart, :waiting_for_payment, :payed, :ready_for_shipment, :on_delivery, :delivered ]
6
+ # end
7
+ #
8
+ # class Order
9
+ # has_many :requests
10
+ # end
11
+ #
12
+ module EnumExt
13
+ # Ex using localize_enum with Request
14
+ # class Request
15
+ # ...
16
+ # localize_enum :status, {
17
+ #
18
+ # #locale dependent example ( it dynamically use current locale ):
19
+ # in_cart: -> { I18n.t("request.status.in_cart") },
20
+
21
+ # #locale dependent example with pluralization:
22
+ # payed: -> (t_self) { I18n.t("request.status.payed", count: t_self.sum ) }
23
+ #
24
+ # #locale independent:
25
+ # ready_for_shipment: "Ready to go!"
26
+ #
27
+ #
28
+ # }
29
+ # end
30
+
31
+ # Console:
32
+ # request.sum = 3
33
+ # request.payed!
34
+ # request.status # >> payed
35
+ # request.t_status # >> "Payed 3 dollars"
36
+ # Request.t_statuses # >> { in_cart: -> { I18n.t("request.status.in_cart") }, .... }
37
+
38
+ # if you need some substitution you can go like this
39
+ # localize_enum :status, {
40
+ # ..
41
+ # delivered: "Delivered at: %{date}"
42
+ # }
43
+ # request.delivered!
44
+ # request.t_status % {date: Time.now.to_s} # >> Delivered at: 05.02.2016
45
+ #
46
+ # Using in select:
47
+ # f.select :status, Request.t_statuses.invert.to_a
48
+ #
49
+ def localize_enum( enum_name, localizations )
50
+ self.instance_eval do
51
+ define_singleton_method( "t_#{enum_name.to_s.pluralize}" ) do
52
+ localizations.try(:with_indifferent_access) || localizations
53
+ end
54
+ define_method "t_#{enum_name}" do
55
+ t = localizations[send(enum_name)]
56
+ ( t.try(:arity) == 1 && t.call( self ) || t.try(:call) || t).to_s
57
+ end
58
+ end
59
+ end
60
+
61
+ # Ex ext_enum_sets
62
+ # This method intend for creating and using some sets of enum values with similar to original enum syntax
63
+ # it creates: scopes for subsets like enum did, instance method with ? similar to enum methods, and methods like Request.statuses
64
+ # Usually I supply comment near method call to remember what methods will be defined
65
+ # class Request
66
+ # ...
67
+ # #instance non_payed?, delivery_set?, in_warehouse?
68
+ # #class scopes: non_payed, delivery_set, in_warehouse
69
+ # #class scopes: with_statuses, without_statuses
70
+ # #class non_payed_statuses, delivery_set_statuses ( = [:in_cart, :waiting_for_payment], [:ready_for_shipment, :on_delivery, :delivered].. )
71
+ # ext_enum_sets :status, {
72
+ # non_payed: [:in_cart, :waiting_for_payment],
73
+ # delivery_set: [:ready_for_shipment, :on_delivery, :delivered] # for shipping department for example
74
+ # in_warehouse: [:ready_for_shipment] # it's just for example below
75
+ # }
76
+ # end
77
+
78
+ # Console:
79
+ # request.waiting_for_payment!
80
+ # request.non_payed? # >> true
81
+
82
+ # Request.non_payed.exists?(request) # >> true
83
+ # Request.delivery_set.exists?(request) # >> false
84
+
85
+ # Request.non_payed_statuses # >> [:in_cart, :waiting_for_payment]
86
+ #
87
+ # Request.with_statuses( :payed, :in_cart ) # >> scope for all in_cart and payed requests
88
+ # Request.without_statuses( :payed ) # >> scope for all requests with statuses not eq to payed
89
+ # Request.without_statuses( :payed, :non_payed ) # >> scope all requests with statuses not eq to payed and in_cart + waiting_for_payment
90
+ #
91
+
92
+ #Rem:
93
+ # ext_enum_sets can be called twice defining a superpositoin of already defined sets:
94
+ # class Request
95
+ # ...
96
+ # ext_enum_sets (... first time call )
97
+ # ext_enum_sets :status, {
98
+ # already_payed: ( [:payed] | delivery_set_statuses ),
99
+ # outside_wharehouse: ( delivery_set_statuses - in_warehouse_statuses )... any other array operations like &, + and so can be used
100
+ # }
101
+ def ext_enum_sets( enum_name, options )
102
+ self.instance_eval do
103
+ options.each do |set_name, enum_vals|
104
+ scope set_name, -> { where( enum_name => self.send( enum_name.to_s.pluralize ).slice( *enum_vals.map(&:to_s) ).values ) }
105
+
106
+ define_singleton_method( "#{set_name}_#{enum_name.to_s.pluralize}" ) do
107
+ enum_vals
108
+ end
109
+
110
+ define_method "#{set_name}?" do
111
+ self.send(enum_name) && ( enum_vals.include?( self.send(enum_name) ) || enum_vals.include?( self.send(enum_name).to_sym ))
112
+ end
113
+
114
+ end
115
+
116
+ scope "with_#{enum_name.to_s.pluralize}", -> (sets_arr) {
117
+ where( enum_name => self.send( enum_name.to_s.pluralize ).slice(
118
+ *sets_arr.map{|set_name| self.try( "#{set_name}_#{enum_name.to_s.pluralize}" ) || set_name }.flatten.uniq.map(&:to_s) ).values )
119
+ } unless respond_to?("with_#{enum_name.to_s.pluralize}")
120
+
121
+ scope "without_#{enum_name.to_s.pluralize}", -> (sets_arr) {
122
+ where.not( id: self.send("with_#{enum_name.to_s.pluralize}", sets_arr) )
123
+ } unless respond_to?("without_#{enum_name.to_s.pluralize}")
124
+ end
125
+ end
126
+
127
+ # Ex mass_assign_enum
128
+ # Used for mass assigning for collection, it creates dynamically nested module with methods similar to enum bang methods, and includes it to relation classes
129
+ # Behind the scene it creates bang methods for collections using update_all.
130
+ # it's often case when I need bulk update without callbacks, so it's gets frustrating to repeat: some_scope.update_all(status: Request.statuses[:new_status], update_at: Time.now)
131
+ # If you need callbacks you can do like this: some_scope.each(&:new_stat!) but if you don't need callbacks and you has hundreds and thousands of records to change at once you need update_all
132
+ #
133
+ # class Request
134
+ # ...
135
+ # mass_assign_enum( :status )
136
+ # end
137
+ #
138
+ # Console:
139
+ # request1.in_cart!
140
+ # request2.waiting_for_payment!
141
+ # Request.non_payed.payed!
142
+ # request1.payed? # >> true
143
+ # request2.payed? # >> true
144
+ # request1.updated_at # >> Time.now
145
+ # Request.respond_to?('::MassAssignEnum') # >> true
146
+ #
147
+ # order.requests.already_payed.all?(&:already_payed?) # >> true
148
+ # order.requests.already_payed.delivered!
149
+ # order.requests.map(&:status).uniq #>> [:delivered]
150
+ #
151
+ #
152
+ # Rem:
153
+ # mass_assign_enum accepts additional options as last argument.
154
+ # calling mass_assign_enum( :status ) actually is equal to call: mass_assign_enum( :status, { relation: true, association_relation: true } )
155
+ #
156
+ # Meaning:
157
+
158
+ # relation: true - Request.some_scope.payed! - works
159
+
160
+ # association_relation: true - Order.first.requests.scope.new_stat! - works
161
+ # but it wouldn't works without 'scope' part! If you want to use it without 'scope' you may do it this way:
162
+ # class Request
163
+ # ...
164
+ # mass_assign_enum( :status, association_relation: false )
165
+ # end
166
+ # class Order
167
+ # has_many :requests, extend: Request::MassAssignEnum
168
+ # end
169
+ #
170
+ # Order.first.requests.respond_to?(:in_cart!) # >> true
171
+ #
172
+ # Rem2:
173
+ # 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
174
+
175
+ def mass_assign_enum( *options )
176
+ relation_options = (options[-1].is_a?(Hash) && options.pop || {relation: true, association_relation: true} ).with_indifferent_access
177
+ enums_names = options
178
+ enums_names.each do |enum_name|
179
+ enum_vals = self.send( enum_name.to_s.pluralize )
180
+
181
+ mass_ass_module = ( self.try( "::MassAssignEnum" ) || Module.new )
182
+
183
+ mass_ass_module.instance_eval do
184
+ enum_vals.keys.each do |enum_el|
185
+ define_method( "#{enum_el}!" ) do
186
+ self.update_all( {enum_name => enum_vals[enum_el]}.merge( self.column_names.include?('updated_at') ? {updated_at: Time.now} : {} ))
187
+ end
188
+ end
189
+ end
190
+ self.const_set( :MassAssignEnum, mass_ass_module ) unless self.try( '::MassAssignEnum' )
191
+
192
+ self::ActiveRecord_Relation.include( self::MassAssignEnum ) if relation_options[:relation]
193
+ self::ActiveRecord_AssociationRelation.include( self::MassAssignEnum ) if relation_options[:association_relation]
194
+ end
195
+ end
196
+
197
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: enum_ext
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Leshchuk Alexey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-22 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Some sugar extention for rails enum
14
+ email: leshchuk@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/enum_ext.rb
20
+ homepage: http://rubygems.org/gems/hola
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.5.1
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Enum extention!
44
+ test_files: []