enum_ext 0.1.0

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