eitil 0.3.3 → 0.3.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7472becb49f3ad8ed37b1af23436257255cd050fd6d101a552fad3b35d020e38
4
- data.tar.gz: 3508a76cd84733740cc9b8e9be223406ec4f620f38d9ba86048118743c058dd5
3
+ metadata.gz: f742ee647b11b18765c5022fe55301317822282c3443cd161e0d49ded69678cf
4
+ data.tar.gz: 10aa2196982292c2cab87a25906b59eba9147294dfdddaafce8a53eb8521c7b7
5
5
  SHA512:
6
- metadata.gz: 52c7c51884686864b8b1aede6fc70a03028636614ef309ef59f9a70336091bd1e122ca2e7ccd99dcd824e371c60a83db73924cda249c373fda62b6f548af32fd
7
- data.tar.gz: 47071fee4cd78efdda04753406985bb093150b70e4d1bcfd355091f3c402cd08603a508c234e3dec2d9d324c1e2a23f0f86c2ae33d1bd914b0c5f1d3631d0de6
6
+ metadata.gz: 13b655a01ff36fd371f50cc9868409a0a0d51520afffaf3d8871bd9955a502bf342945f51d0f551cbd9c58b97bdcc6fa7d28f9a09ee06b9c1e127764abbc3854
7
+ data.tar.gz: c7a02991659f2c23d9c4846d2377e6160a3f15fdd0119f231ad1e78fef0686ba651c35c25b283f49405cd5f1fc545d40d082c7ff7639ee080cef76d8b25ff7b6
data/README.md CHANGED
@@ -103,6 +103,20 @@ include_concerns_of(*directories, namespace: nil)
103
103
 
104
104
 
105
105
 
106
+ ## String
107
+
108
+ ```ruby
109
+ is_num?
110
+ # returns true if a string matches the Rubinius source code regex for strings and integers, false otherwise
111
+ # comes in handy since ruby plays it safe on string to number conversion ('aaa'.to_f returns 0.0, thus is valid)
112
+
113
+ is_nan?
114
+ # returns true if a string does NOT match the Rubinius source code regex for strings and integers, false otherwise
115
+ # comes in handy since ruby plays it safe on string to number conversion ('aaa'.to_f returns 0.0, thus is valid)
116
+ ```
117
+
118
+
119
+
106
120
  ## Hash
107
121
 
108
122
  ```ruby
@@ -131,6 +145,14 @@ slice_params(*args)
131
145
  self.all_associations
132
146
  # returns all associations for a given model
133
147
  # call as: Environment.all_associations
148
+
149
+ self.where_like(_hash)
150
+ # runs .where with your string field made into a wildcard and case insensitive
151
+ # call as: User.where_like(name: 'jurriaan')
152
+
153
+ self.find_by_like(_hash)
154
+ # runs .find_by with your string field made into a wildcard and case insensitive
155
+ # call as: User.find_by_like(name: 'jurriaan')
134
156
  ```
135
157
 
136
158
 
@@ -177,7 +199,7 @@ decorate(dec_item, dec_method: nil, dec_class: nil, **dec_kwargs)
177
199
  # initializers/eitil.rb
178
200
 
179
201
  Eitil.set_config do |config|
180
- config.get_controller_ivars_method = 'API::VerlofVerzoekenController.controller_ivars' # => [:user, :env]
202
+ config.get_controller_ivars_method = 'API::BaseController.controller_ivars' # => [:user, :env]
181
203
  end
182
204
  ```
183
205
 
@@ -227,14 +249,18 @@ The router wrapper is a single module which is automatically included into your
227
249
 
228
250
  ### Info
229
251
 
230
- The Eitil jobs wrapper enables you to create perform_later jobs on the fly, without the need to create a new class and file. The macro new_job accepts a :method as argument and defines a new method :method_job. The newly defined :method_job, when called, performs the orginal :method in the background. The new_job macro accepts both instance methods and singleton methods, which are defined within there own method scope.
252
+ The Eitil jobs wrapper enables you to create perform_now and perform_later jobs on the fly, without the need to create a new class and file. The macro new_job accepts a :method as argument and defines a new method :method_job. The newly defined :method_job, when called, performs the orginal :method in the background. The new_job macro accepts both instance methods and singleton methods, which are defined within there own method scope. In contrast, the new_job_debugger macro defines a new :method_job_debugger method, which performs in the foreground.
231
253
 
232
254
  ### Job Macro
233
255
 
234
256
  ```ruby
235
- new_job(_method)
236
- # assuming a method :print_hello_world, call as: new_job :hello_world
237
- # => method :hello_world_job
257
+ new_job(_method, *args, **kwargs)
258
+ # assuming a method :hello_world, call as: new_job :hello_world
259
+ # => defines :hello_world_job
260
+
261
+ new_job_debugger(_method, *args, **kwargs)
262
+ # assuming a method :hello_world, call as: new_job_debugger :hello_world
263
+ # => defines :hello_world_job_debugger
238
264
  ```
239
265
 
240
266
  - method is
@@ -304,3 +330,50 @@ Scopes are generated through the columns of your model's database table. Which s
304
330
  Your models should inherit the module Eitil::DefaultScopes through inclusion in a superclass, such as ApplicationRecord.
305
331
 
306
332
 
333
+
334
+ # Modules & Classes
335
+
336
+
337
+
338
+ ## Eitil::Dir
339
+
340
+ ### Info
341
+
342
+ The Eitil::Dir module provides methods which help you introspect your Rails project.
343
+
344
+ ### Methods
345
+
346
+ ```ruby
347
+ Eitil::Dir.contents(directory='app')
348
+ # returns all files and subdirectories of a given directory
349
+
350
+ Eitil::Dir.files(directory='app')
351
+ # returns all files of a given directory
352
+
353
+ Eitil::Dir.subdirs(directory='app')
354
+ # returns all subdirectories of a given directory
355
+
356
+ Eitil::Dir.lines(directory='app')
357
+ # returns the total amount of lines of all files of a given directory
358
+ ```
359
+
360
+
361
+
362
+ ## Eitil::StackTrace
363
+
364
+ ### Info
365
+
366
+ The two classes Eitil::StackTrace::Stack and Eitil::StackTrace::Call, and the single module Eitil::StackTrace::Audit, provide the utility of easily viewing and storing the current stacktrace anywhere in your code. By initializing stack = Eitil::StackTrace::Stack.new, you can call methods as stack.report (returns the stacktrace as array), stack.show (pretty prints the stacktrace) and stack.find (accepts a block to find a call).
367
+
368
+ You can also store the stacktrace of a record's update action into its audit. In order to do so, you need 1) to include Eitil::StackTrace::Audit into your model, and 2) add a :stacktrace column to your audits through the following migration.
369
+
370
+ ### Migration
371
+
372
+ ```ruby
373
+ def change
374
+ add_column :audits, :stacktrace, :text, array: true, default: []
375
+ end
376
+ ```
377
+
378
+
379
+
@@ -0,0 +1,15 @@
1
+ module Eitil::StackTrace::Audit
2
+ extend ActiveSupport::Concern
3
+ included do
4
+
5
+ private
6
+
7
+ after_update :add_stacktrace_to_audit
8
+
9
+ def add_stacktrace_to_audit
10
+ stacktrace = Eitil::StackTrace::Stack.new.report
11
+ self.audits.last.update(stacktrace: stacktrace)
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module Eitil::StackTrace
2
+ class Call
3
+
4
+ attr_reader :program, :line, :meth
5
+
6
+ CALL_RE = /(.*):(\d+):in `(.*)'/
7
+
8
+ def initialize(string)
9
+ @program, @line, @meth = CALL_RE.match(string).captures
10
+ end
11
+
12
+ def to_s
13
+ "#{"#{meth}".ljust(50)} #{"(#{line})".ljust(6)} #{program}"
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,28 @@
1
+ module Eitil::StackTrace
2
+ class Stack
3
+
4
+ attr_reader :stack, :backtrace
5
+
6
+ def initialize
7
+ @stack = caller[1..]
8
+ @backtrace = stack.map { |call| Eitil::StackTrace::Call.new(call) }
9
+ end
10
+
11
+ def report
12
+ backtrace.map(&:to_s)
13
+ end
14
+
15
+ def show
16
+ ap report
17
+ end
18
+
19
+ def find(&block)
20
+ backtrace.find(&block)
21
+ end
22
+
23
+ def self.parse(array_as_string)
24
+ array_as_string.sub('[', ' ').reverse.sub(']','').reverse.split(',').flatten
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ module Eitil::Dir
2
+ class << self
3
+
4
+ def contents(directory='app')
5
+ Dir[File.join(directory, '**', '*')]
6
+ end
7
+
8
+ def files(directory='app')
9
+ contents(directory).select { |file| File.file?(file) }
10
+ end
11
+
12
+ def subdirs(directory='app')
13
+ contents(directory).select { |file| File.directory?(file) }
14
+ end
15
+
16
+ def lines(directory='app')
17
+ files(directory).map { |file| File.open(file).count }.sum
18
+ end
19
+
20
+ end
21
+ end
@@ -4,10 +4,24 @@ module ApplicationRecordMonkey
4
4
  extend ActiveSupport::Concern
5
5
  included do
6
6
 
7
- def self.all_associations
8
- reflect_on_all_associations.map do |assoc|
9
- { assoc.name => assoc.association_class.to_s.demodulize }
10
- end.inject &:merge
7
+ class << self
8
+
9
+ def all_associations
10
+ reflect_on_all_associations.map do |assoc|
11
+ { assoc.name => assoc.association_class.to_s.demodulize }
12
+ end.inject &:merge
13
+ end
14
+
15
+ def where_like(_hash)
16
+ column, like = _hash.first
17
+ where("LOWER(#{column}) LIKE ?", "%#{like.downcase}%")
18
+ end
19
+
20
+ def find_by_like(_hash)
21
+ column, like = _hash.first
22
+ find_by("LOWER(#{column}) LIKE ?", "%#{like.downcase}%")
23
+ end
24
+
11
25
  end
12
26
 
13
27
  end
@@ -8,7 +8,7 @@ class Hash
8
8
  return answer if answer
9
9
  end
10
10
 
11
- false
11
+ nil
12
12
  end
13
13
 
14
14
  end
@@ -0,0 +1,16 @@
1
+ class String
2
+
3
+ # The NumberRegex belows comes from the Rubinius source code and
4
+ # identifies both integers and floats correctly.
5
+
6
+ NumberRegex = /^\s*[+-]?((\d+_?)*\d+(\.(\d+_?)*\d+)?|\.(\d+_?)*\d+)(\s*|([eE][+-]?(\d+_?)*\d+)\s*)$/
7
+
8
+ def is_nan?
9
+ self !~ NumberRegex
10
+ end
11
+
12
+ def is_num?
13
+ !is_nan?
14
+ end
15
+
16
+ end
@@ -1,18 +1,52 @@
1
1
  Kernel.module_eval do
2
2
 
3
- def new_job(_method)
3
+ # BEWARE: _self is currently not accepted in perform_later jobs due to serialization errors
4
+
5
+ # The cases of 'id' and '_self' both handle instances, with the difference
6
+ # being that 'id' works for objects that have a database record, while '_self'
7
+ # works for non database supported instanes, such as an Exporter instance.
8
+
9
+ def new_job(_method, *args, **kwargs)
4
10
 
5
11
  if instance_methods(false).include? _method
6
- define_method "#{_method}_job" do
7
- Eitil::SingleMethodJob.perform_later(_class: self.class.to_s, _method: _method.to_s, id: id)
12
+ define_method "#{_method}_job" do |_self = nil, *args, **kwargs|
13
+
14
+ Eitil::SingleMethodJob.perform_later(
15
+ *args, _class: self.class.to_s, _method: _method.to_s, id: safe_send(:id), _self: self.to_json, **kwargs
16
+ )
8
17
  end
9
18
 
10
- elsif singleton_methods(false).include? _method
11
- define_singleton_method "#{_method}_job" do
12
- Eitil::SingleMethodJob.perform_later(_class: to_s, _method: _method.to_s)
19
+ elsif singleton_methods(false).include? _method
20
+ define_singleton_method "#{_method}_job" do |*args, **kwargs|
21
+
22
+ Eitil::SingleMethodJob.perform_later(
23
+ *args, _class: to_s, _method: _method.to_s, **kwargs
24
+ )
13
25
  end
14
- end
26
+ end
27
+ end
28
+
29
+ # BEWARE: This is an exact copy of the above method, except for .perform_now instead of .perform_later and the method's name.
30
+ # The reason for not using helper methods is to not unnecessarily fill and potentially fuck up the Kernel environment.
31
+
32
+ def new_job_debugger(_method, *args, **kwargs)
15
33
 
34
+ if instance_methods(false).include? _method
35
+ define_method "#{_method}_job_debugger" do |_self = nil, *args, **kwargs|
36
+
37
+ Eitil::SingleMethodJob.perform_now(
38
+ *args, _class: self.class.to_s, _method: _method.to_s, id: safe_send(:id), _self: self.to_json, **kwargs
39
+ )
40
+ end
41
+
42
+ elsif singleton_methods(false).include? _method
43
+ define_singleton_method "#{_method}_job_debugger" do |*args, **kwargs|
44
+
45
+ Eitil::SingleMethodJob.perform_now(
46
+ *args, _class: to_s, _method: _method.to_s, **kwargs
47
+ )
48
+ end
49
+ end
16
50
  end
17
51
 
18
52
  end
@@ -2,9 +2,23 @@ module Eitil
2
2
  class SingleMethodJob < ApplicationJob
3
3
  queue_as :default
4
4
 
5
- def perform(_class:, _method:, id: nil)
6
- object = id ? _class.constantize.find(id) : _class.constantize
7
- object.send _method
5
+ # BEWARE: _self is currently not accepted in perform_later jobs due to serialization errors
6
+
7
+ # The cases of 'id' and '_self' both handle instances, with the difference
8
+ # being that 'id' works for objects that have a database record, while '_self'
9
+ # works for non database supported instanes, such as an Exporter instance.
10
+
11
+ def perform(*args, _class:, _method:, id: nil, _self: nil, **kwargs)
12
+ object =
13
+ if id
14
+ _class.constantize.find(id)
15
+ elsif _self
16
+ _self
17
+ else
18
+ _class.constantize
19
+ end
20
+
21
+ object.send _method, *args, **kwargs
8
22
  end
9
23
  end
10
24
  end
@@ -2,86 +2,82 @@ module Eitil
2
2
  module DefaultScopes
3
3
  extend ActiveSupport::Concern
4
4
  included do
5
-
6
- SharableDateScopes = -> (column) {
7
- eitil_scope :"#{column}_today", -> { where("#{column} = ?", Date.today) }
8
- eitil_scope :"#{column}_past", -> { where("#{column} < ?", Date.today) }
9
- eitil_scope :"#{column}_future", -> { where("#{column} > ?", Date.today) }
10
-
11
- eitil_scope :"#{column}_on_date", -> (date) { where("#{column} = ?", date) }
12
- eitil_scope :"#{column}_before_date", -> (date) { where("#{column} = ?", date) }
13
- eitil_scope :"#{column}_after_date", -> (date) { where("#{column} = ?", date) }
14
- eitil_scope :"#{column}_between_dates", -> (from, till) { where("#{column} >= ? and #{column} <= ?", from, till) }
5
+ class << self
15
6
 
16
- eitil_scope :"#{column}_oldest_first", -> { order("#{column} ASC") }
17
- eitil_scope :"#{column}_newest_first", -> { order("#{column} DESC") }
18
- }
7
+ SharableDateScopes = -> (_class, column) {
8
+ _class.eitil_scope :"#{column}_today", -> { where("#{column} = ?", Date.today) }
9
+ _class.eitil_scope :"#{column}_past", -> { where("#{column} < ?", Date.today) }
10
+ _class.eitil_scope :"#{column}_future", -> { where("#{column} > ?", Date.today) }
11
+
12
+ _class.eitil_scope :"#{column}_on_date", -> (date) { where("#{column} = ?", date) }
13
+ _class.eitil_scope :"#{column}_before_date", -> (date) { where("#{column} = ?", date) }
14
+ _class.eitil_scope :"#{column}_after_date", -> (date) { where("#{column} = ?", date) }
15
+ _class.eitil_scope :"#{column}_between_dates", -> (from, till) { where("#{column} >= ? and #{column} <= ?", from, till) }
19
16
 
20
- SharableNumScopes = -> (column) {
21
- eitil_scope :"#{column}_equal_to", -> (number) { where("#{column} = ?", number) }
22
- eitil_scope :"#{column}_lower_than", -> (number) { where("#{column} = <", number) }
23
- eitil_scope :"#{column}_higher_than", -> (number) { where("#{column} = >", number) }
24
- eitil_scope :"#{column}_between", -> (min, max) { where("#{column} >= ? and #{column} <= ?", min, max) }
17
+ _class.eitil_scope :"#{column}_oldest_first", -> { order("#{column} ASC") }
18
+ _class.eitil_scope :"#{column}_newest_first", -> { order("#{column} DESC") }
19
+ }
25
20
 
26
- eitil_scope :"#{column}_ascending", -> { order("#{column} ASC") }
27
- eitil_scope :"#{column}_descending", -> { order("#{column} DESC") }
28
- }
21
+ SharableNumScopes = -> (_class, column) {
22
+ _class.eitil_scope :"#{column}_equal_to", -> (number) { where("#{column} = ?", number) }
23
+ _class.eitil_scope :"#{column}_lower_than", -> (number) { where("#{column} = <", number) }
24
+ _class.eitil_scope :"#{column}_higher_than", -> (number) { where("#{column} = >", number) }
25
+ _class.eitil_scope :"#{column}_between", -> (min, max) { where("#{column} >= ? and #{column} <= ?", min, max) }
29
26
 
30
- class << self
27
+ _class.eitil_scope :"#{column}_ascending", -> { order("#{column} ASC") }
28
+ _class.eitil_scope :"#{column}_descending", -> { order("#{column} DESC") }
29
+ }
31
30
 
32
- private
33
-
34
- def self.inherited(subclass)
35
- use_eitil_scopes
36
- super
37
- end
31
+ def inherited(subclass)
32
+ super
33
+ subclass.use_eitil_scopes
34
+ end
38
35
 
39
- def use_eitil_scopes
40
- return if abstract_class?
41
- %i[boolean datetime date integer float].each { |_type| send :"create_eitil_#{_type}_scopes" }
42
- end
36
+ def use_eitil_scopes
37
+ return if abstract_class?
38
+ %i[boolean datetime date integer float].each { |_type| send :"create_eitil_#{_type}_scopes" }
39
+ end
43
40
 
44
- def eitil_scope(_name, _proc)
45
- scope _name, _proc unless respond_to? _name
46
- end
47
-
48
- def columns_of_type(data_type)
49
- columns_hash.select { |column,v| v.sql_type_metadata.type == data_type }
50
- end
41
+ def eitil_scope(_name, _proc)
42
+ scope _name, _proc unless respond_to? _name
43
+ end
44
+
45
+ def columns_of_type(data_type)
46
+ columns_hash.select { |column,v| v.sql_type_metadata.type == data_type }
47
+ end
51
48
 
52
- def create_eitil_boolean_scopes
53
- columns_of_type(:boolean)&.map do |column, object|
54
- eitil_scope :"#{column}_true", -> { where(column => true) }
55
- eitil_scope :"#{column}_false", -> { where(column => [false, nil]) }
49
+ def create_eitil_boolean_scopes
50
+ columns_of_type(:boolean)&.map do |column, object|
51
+ eitil_scope :"#{column}_true", -> { where(column => true) }
52
+ eitil_scope :"#{column}_false", -> { where(column => [false, nil]) }
53
+ end
56
54
  end
57
- end
58
55
 
59
- def create_eitil_datetime_scopes
60
- columns_of_type(:datetime)&.map do |column, object|
61
- SharableDateScopes.call column
56
+ def create_eitil_datetime_scopes
57
+ columns_of_type(:datetime)&.map do |column, object|
58
+ SharableDateScopes.call self, column
59
+ end
62
60
  end
63
- end
64
61
 
65
- def create_eitil_date_scopes
66
- columns_of_type(:date)&.map do |column, object|
67
- SharableDateScopes.call column
62
+ def create_eitil_date_scopes
63
+ columns_of_type(:date)&.map do |column, object|
64
+ SharableDateScopes.call self, column
65
+ end
68
66
  end
69
- end
70
67
 
71
- def create_eitil_integer_scopes
72
- columns_of_type(:integer)&.map do |column, object|
73
- SharableNumScopes.call column
68
+ def create_eitil_integer_scopes
69
+ columns_of_type(:integer)&.map do |column, object|
70
+ SharableNumScopes.call self, column
71
+ end
74
72
  end
75
- end
76
73
 
77
- def create_eitil_float_scopes
78
- columns_of_type(:float)&.map do |column, object|
79
- SharableNumScopes.call column
74
+ def create_eitil_float_scopes
75
+ columns_of_type(:float)&.map do |column, object|
76
+ SharableNumScopes.call self, column
77
+ end
80
78
  end
81
- end
82
79
 
83
80
  end
84
-
85
81
  end
86
82
  end
87
83
  end
data/lib/eitil/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Eitil
2
- VERSION = '0.3.3'
2
+ VERSION = '0.3.8'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eitil
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.3.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jurriaan Schrofer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-18 00:00:00.000000000 Z
11
+ date: 2021-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,6 +52,10 @@ files:
52
52
  - app/jobs/eitil/application_job.rb
53
53
  - app/mailers/eitil/application_mailer.rb
54
54
  - app/models/eitil/application_record.rb
55
+ - app/models/eitil/stack_trace/audit.rb
56
+ - app/models/eitil/stack_trace/call.rb
57
+ - app/models/eitil/stack_trace/stack.rb
58
+ - config/initializers/modules/dir.rb
55
59
  - config/initializers/monkeys/application_controller.rb
56
60
  - config/initializers/monkeys/application_record.rb
57
61
  - config/initializers/monkeys/date_time.rb
@@ -59,6 +63,7 @@ files:
59
63
  - config/initializers/monkeys/kernel.rb
60
64
  - config/initializers/monkeys/module.rb
61
65
  - config/initializers/monkeys/object.rb
66
+ - config/initializers/monkeys/string.rb
62
67
  - config/initializers/wrappers/decorators/application_decorator.rb
63
68
  - config/initializers/wrappers/decorators/controller_decorator.rb
64
69
  - config/initializers/wrappers/jobs/active_job_macros.rb