gemmyrb 0.0.2 → 0.0.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 +4 -4
- data/README.md +1 -1
- data/lib/gemmy/cli.rb +1 -1
- data/lib/gemmy/components/dynamic_steps.rb +1 -1
- data/lib/gemmy/components.rb +1 -1
- data/lib/gemmy/patches/array_patch.rb +183 -11
- data/lib/gemmy/patches/hash_patch.rb +88 -76
- data/lib/gemmy/patches/integer_patch.rb +53 -0
- data/lib/gemmy/patches/method_patch.rb +18 -13
- data/lib/gemmy/patches/object_patch.rb +66 -46
- data/lib/gemmy/patches/string_patch.rb +17 -12
- data/lib/gemmy/patches/symbol_patch.rb +16 -10
- data/lib/gemmy/patches/thread_patch.rb +9 -6
- data/lib/gemmy/patches.rb +79 -12
- data/lib/gemmy/tasks/make_gem.rb +1 -1
- data/lib/gemmy/tests/component_tests/dynamic_steps_tests.rb +2 -1
- data/lib/gemmy/tests/patch_test.rb +1 -1
- data/lib/gemmy/version.rb +1 -1
- data/lib/gemmy.rb +18 -3
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57241faae027cf6e4416104dcedd95dcdb6f3d56
|
4
|
+
data.tar.gz: 6f4caf998b97cd0e9fdc84f243f19813248e0541
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2fef2f0b535f1829842689b3c6d5dc92cbf223dac26ea3b0dceb60476a075309e59e3ee3be8e25b93b198734bc2e8c15f8c3a5030cfb4cb21a832fb13ddc51d5
|
7
|
+
data.tar.gz: ce6f3a3f1c5d7b54eac6e72fc1341bf3748a6926bc054cda10d6512e7905a22ca87d291ddd6c6dc8a485e70fb05750c42c1b0b6bc7a14e7a44125190bc16c4f3
|
data/README.md
CHANGED
@@ -22,4 +22,4 @@ documents:
|
|
22
22
|
- _List of methods intended for general Ruby use_
|
23
23
|
- [examples/03_ruby_extensions_list.rb](./examples/03_ruby_extensions_list.rb)
|
24
24
|
- _Shell commands and other one-off processes_
|
25
|
-
- [examples/04_shell_commands.rb](./04_shell_commands.rb)
|
25
|
+
- [examples/04_shell_commands.rb](./examples/04_shell_commands.rb)
|
data/lib/gemmy/cli.rb
CHANGED
@@ -14,7 +14,7 @@ class Gemmy::CLI < Thor
|
|
14
14
|
def self.run(arguments: nil)
|
15
15
|
# Store a copy of the arguments.
|
16
16
|
# The originals are shifted so they don't intefere with gets
|
17
|
-
arguments
|
17
|
+
arguments ||= ARGV.clone
|
18
18
|
ARGV.clear
|
19
19
|
|
20
20
|
# Can't make this conditional on "__FILE__ == $0"
|
@@ -19,7 +19,7 @@
|
|
19
19
|
#
|
20
20
|
module Gemmy::Components::DynamicSteps
|
21
21
|
|
22
|
-
Gemmy::Patches.
|
22
|
+
Gemmy::Patches.class_refinements.each { |r| using r }
|
23
23
|
|
24
24
|
# Error raised when a string matches multiple step regexes.
|
25
25
|
# It's frequently accidental to come into this situation,
|
data/lib/gemmy/components.rb
CHANGED
@@ -1,19 +1,191 @@
|
|
1
1
|
# Array patches
|
2
2
|
#
|
3
3
|
module Gemmy::Patches::ArrayPatch
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
module Zip
|
8
|
+
# facets
|
9
|
+
def self.zip(*arrays)
|
10
|
+
return [] if arrays.empty?
|
11
|
+
return arrays[0].zip(*arrays[1..-1])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
13
15
|
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
+
module InstanceMethods
|
18
|
+
|
19
|
+
module KeyBy
|
20
|
+
# facets
|
21
|
+
def key_by
|
22
|
+
return to_enum(:key_by) unless block_given?
|
23
|
+
h = {}
|
24
|
+
each do |v|
|
25
|
+
h[yield(v)] = v
|
26
|
+
end
|
27
|
+
return h
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module After
|
32
|
+
# facets
|
33
|
+
def after(value)
|
34
|
+
return nil unless include? value
|
35
|
+
self[(index(value).to_i + 1) % length]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module Before
|
40
|
+
# facets
|
41
|
+
def before(value)
|
42
|
+
return nil unless include? value
|
43
|
+
self[(index(value).to_i - 1) % length]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module Duplicates
|
48
|
+
# facets
|
49
|
+
def duplicates(min=2)
|
50
|
+
h = Hash.new( 0 )
|
51
|
+
each {|i|
|
52
|
+
h[i] += 1
|
53
|
+
}
|
54
|
+
h.delete_if{|_,v| v < min}.keys
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module ExtractOptions
|
59
|
+
# facets
|
60
|
+
def extract_options!
|
61
|
+
if Hash === last && last.extractable_options?
|
62
|
+
pop
|
63
|
+
else
|
64
|
+
{}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module RejectValues
|
70
|
+
# facets
|
71
|
+
def reject_values(*values)
|
72
|
+
reject { |x| values.include?(x) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module Recurse
|
77
|
+
# facets
|
78
|
+
def recurse(*types, &block)
|
79
|
+
types = [self.class] if types.empty?
|
80
|
+
a = inject([]) do |array, value|
|
81
|
+
case value
|
82
|
+
when *types
|
83
|
+
array << value.recurse(*types, &block)
|
84
|
+
else
|
85
|
+
array << value
|
86
|
+
end
|
87
|
+
array
|
88
|
+
end
|
89
|
+
yield a
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module Probability
|
94
|
+
# facets
|
95
|
+
def probability
|
96
|
+
probs = Hash.new(0.0)
|
97
|
+
size = 0.0
|
98
|
+
each do |e|
|
99
|
+
probs[e] += 1.0
|
100
|
+
size += 1.0
|
101
|
+
end
|
102
|
+
probs.keys.each{ |e| probs[e] /= size }
|
103
|
+
probs
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module NotEmpty
|
108
|
+
# facets
|
109
|
+
def not_empty?
|
110
|
+
!empty?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
module Median
|
115
|
+
# facets
|
116
|
+
def median(offset=0)
|
117
|
+
return nil if self.size == 0
|
118
|
+
|
119
|
+
tmp = self.sort
|
120
|
+
mid = (tmp.size / 2).to_i + offset
|
121
|
+
|
122
|
+
tmp[mid]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module Mode
|
127
|
+
# facets
|
128
|
+
def mode
|
129
|
+
max = 0
|
130
|
+
c = Hash.new 0
|
131
|
+
each {|x| cc = c[x] += 1; max = cc if cc > max}
|
132
|
+
c.select {|k,v| v == max}.map {|k,v| k}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
module NonUniq
|
137
|
+
|
138
|
+
# facets
|
139
|
+
def nonuniq
|
140
|
+
h1 = {}
|
141
|
+
h2 = {}
|
142
|
+
each {|i|
|
143
|
+
h2[i] = true if h1[i]
|
144
|
+
h1[i] = true
|
145
|
+
}
|
146
|
+
h2.keys
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
module Arrange
|
151
|
+
# facets
|
152
|
+
# creates ranges from array
|
153
|
+
def arrange
|
154
|
+
array = uniq.sort_by { |e| Range === e ? e.first : e }
|
155
|
+
array.inject([]) do |c, value|
|
156
|
+
unless c.empty?
|
157
|
+
last = c.last
|
158
|
+
last_value = (Range === last ? last.last : last)
|
159
|
+
current_value = (Range === value ? value.first : value)
|
160
|
+
if (last_value.succ <=> current_value) == -1
|
161
|
+
c << value
|
162
|
+
else
|
163
|
+
first = (Range === last ? last.first : last)
|
164
|
+
second = [Range === last ? last.last : last, Range === value ? value.last : value].max
|
165
|
+
c[-1] = [first..second]
|
166
|
+
c.flatten!
|
167
|
+
end
|
168
|
+
else
|
169
|
+
c << value
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
alias rangify arrange
|
174
|
+
end
|
175
|
+
|
176
|
+
module AnyNot
|
177
|
+
# checks if any of the results of an array do not respond truthily
|
178
|
+
# to a block
|
179
|
+
#
|
180
|
+
# For example, to check if any items of an array are truthy:
|
181
|
+
# [false, nil, ''].any_not? &:blank?
|
182
|
+
# => false
|
183
|
+
#
|
184
|
+
def any_not?(&blk)
|
185
|
+
any? { |item| ! blk.call(item) }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
17
189
|
end
|
18
190
|
|
19
191
|
end
|
@@ -2,85 +2,100 @@
|
|
2
2
|
#
|
3
3
|
module Gemmy::Patches::HashPatch
|
4
4
|
|
5
|
-
|
6
|
-
# Takes a list of keys followed by a value to set
|
7
|
-
#
|
8
|
-
# Example:
|
9
|
-
#
|
10
|
-
# a = {a: {b: {}} }
|
11
|
-
# a.bury(:a, :b, :c, 0)
|
12
|
-
# puts a[:a][:b][:c]
|
13
|
-
# => 0
|
14
|
-
#
|
15
|
-
# Source: https://github.com/dam13n/ruby-bury/blob/master/hash.rb
|
16
|
-
#
|
17
|
-
def bury *args
|
18
|
-
Gemmy::Patches::HashPatch._bury(self, *args)
|
5
|
+
module ClassMethods
|
19
6
|
end
|
20
7
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
8
|
+
module InstanceMethods
|
9
|
+
|
10
|
+
# The opposite of Hash#dig
|
11
|
+
# Takes a list of keys followed by a value to set
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# a = {a: {b: {}} }
|
16
|
+
# a.bury(:a, :b, :c, 0)
|
17
|
+
# puts a[:a][:b][:c]
|
18
|
+
# => 0
|
19
|
+
#
|
20
|
+
# Source: https://github.com/dam13n/ruby-bury/blob/master/hash.rb
|
21
|
+
#
|
22
|
+
module Bury
|
23
|
+
def bury *args
|
24
|
+
Gemmy::Patches::HashPatch::InstanceMethods::Bury._bury(self, *args)
|
25
|
+
end
|
26
|
+
# The bury method, taking the input hash as a parameter
|
27
|
+
# Used by the Hash#bury instance method
|
28
|
+
def self._bury(caller_hash, *args)
|
29
|
+
if args.count < 2
|
30
|
+
raise ArgumentError.new("2 or more arguments required")
|
31
|
+
elsif args.count == 2
|
32
|
+
caller_hash[args[0]] = args[1]
|
33
|
+
else
|
34
|
+
arg = args.shift
|
35
|
+
caller_hash[arg] = {} unless caller_hash[arg]
|
36
|
+
_bury(caller_hash[arg], *args) unless args.empty?
|
37
|
+
end
|
38
|
+
caller_hash
|
39
|
+
end
|
32
40
|
end
|
33
|
-
caller_hash
|
34
|
-
end
|
35
41
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
# Turns a hash into one that's "autovivified"
|
43
|
+
# meaning it's default values for keys is an empty hash.
|
44
|
+
# The result is that you can set nested keys without initializing
|
45
|
+
# more than one hash layer.
|
46
|
+
#
|
47
|
+
# Usage:
|
48
|
+
# hash = {}.autovivified
|
49
|
+
# hash[:a][:b] = 0
|
50
|
+
# puts hash[:a][:b]
|
51
|
+
# => 0
|
52
|
+
#
|
53
|
+
module Autovivified
|
54
|
+
def self._autovivified(caller_hash)
|
55
|
+
result = Hash.new do |hash,key|
|
56
|
+
hash[key] = Hash.new(&hash.default_proc)
|
57
|
+
end
|
58
|
+
result.deep_merge caller_hash
|
59
|
+
end
|
60
|
+
def autovivified
|
61
|
+
Gemmy::Patches::HashPatch::InstanceMethods::Autovivified._autovivified(self)
|
62
|
+
end
|
63
|
+
end
|
50
64
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
65
|
+
# Sets up a hash to mirror all changes to a database file
|
66
|
+
# All nested gets & sets require a list of keys, passed as subsequent args.
|
67
|
+
# Instead of [] and []=, use get and set
|
68
|
+
#
|
69
|
+
# Everything in the db is contained in a hash with one predefined
|
70
|
+
# key, :data. The value is an empty, autovivified hash.
|
71
|
+
#
|
72
|
+
# This also makes the caller hash autovivified
|
73
|
+
#
|
74
|
+
# Example:
|
75
|
+
#
|
76
|
+
# hash = {}.persisted("db.yaml")
|
77
|
+
# hash.set(:a, :b, 0) # => this writes to disk and memory
|
78
|
+
# hash.get(:a, :b) # => reads from memory
|
79
|
+
# hash.get(:a, :b, disk: true) # => reads from disk
|
80
|
+
#
|
55
81
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# hash.set(:a, :b, 0) # => this writes to disk and memory
|
69
|
-
# hash.get(:a, :b) # => reads from memory
|
70
|
-
# hash.get(:a, :b, disk: true) # => reads from disk
|
71
|
-
#
|
72
|
-
def persisted(path)
|
73
|
-
require 'yaml/store'
|
74
|
-
Gemmy::Patches::HashPatch._autovivified(self).tap do |hash|
|
75
|
-
hash.instance_exec do
|
76
|
-
@db = YAML::Store.new path
|
77
|
-
@db.transaction do
|
78
|
-
@db[:data] = Gemmy::Patches::HashPatch._autovivified({})
|
82
|
+
module Persisted
|
83
|
+
def persisted(path)
|
84
|
+
require 'yaml/store'
|
85
|
+
autovivified = Gemmy::Patches::HashPatch::InstanceMethods::Autovivified.method(:_autovivified)
|
86
|
+
autovivified.call(self).tap do |hash|
|
87
|
+
hash.instance_exec do
|
88
|
+
@db = YAML::Store.new path
|
89
|
+
@db.transaction do
|
90
|
+
@db[:data] = autovivified.call({})
|
91
|
+
end
|
92
|
+
end
|
93
|
+
hash.extend Gemmy::Patches::HashPatch::PersistedHash
|
79
94
|
end
|
80
95
|
end
|
81
|
-
hash.extend Gemmy::Patches::HashPatch::PersistedHash
|
82
96
|
end
|
83
|
-
|
97
|
+
|
98
|
+
end # end instance methods
|
84
99
|
|
85
100
|
# Helper methods for the persistence patch
|
86
101
|
#
|
@@ -89,16 +104,13 @@ module Gemmy::Patches::HashPatch
|
|
89
104
|
disk ? @db.transaction { @db[:data].dig(*keys) } : dig(*keys)
|
90
105
|
end
|
91
106
|
def set(*keys, val)
|
92
|
-
Gemmy::Patches::HashPatch._bury
|
107
|
+
bury = Gemmy::Patches::HashPatch::InstanceMethods::Bury.method(:_bury)
|
108
|
+
bury.call(self, *keys, val)
|
93
109
|
@db.transaction do
|
94
|
-
|
110
|
+
bury.call(@db[:data], *(keys + [val]))
|
95
111
|
end
|
96
112
|
val
|
97
113
|
end
|
98
114
|
end
|
99
115
|
|
100
|
-
refine Hash do
|
101
|
-
include Gemmy::Patches::HashPatch
|
102
|
-
end
|
103
|
-
|
104
116
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Gemmy::Patches::IntegerPatch
|
2
|
+
|
3
|
+
module ClassMethods
|
4
|
+
|
5
|
+
end
|
6
|
+
|
7
|
+
module InstanceMethods
|
8
|
+
|
9
|
+
module Factorial
|
10
|
+
# facets
|
11
|
+
def factorial
|
12
|
+
return 1 if zero?
|
13
|
+
f = 1
|
14
|
+
2.upto(self) { |n| f *= n }
|
15
|
+
f
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module MultipleOf
|
20
|
+
# facets
|
21
|
+
def multiple_of?(number)
|
22
|
+
if number.zero?
|
23
|
+
zero? ? true : false
|
24
|
+
else
|
25
|
+
self % number == 0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Odd
|
31
|
+
def odd?
|
32
|
+
! even?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Of
|
37
|
+
# facets
|
38
|
+
def of(&block)
|
39
|
+
Array.new(self, &block)
|
40
|
+
end
|
41
|
+
alias times_map of
|
42
|
+
end
|
43
|
+
|
44
|
+
module Collapse
|
45
|
+
# facets
|
46
|
+
def collapse
|
47
|
+
flatten.compact
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -2,21 +2,26 @@
|
|
2
2
|
#
|
3
3
|
module Gemmy::Patches::MethodPatch
|
4
4
|
|
5
|
-
|
6
|
-
# Very useful for the proc shorthand.
|
7
|
-
# For example say there's a method "def add(a,b); print a + b; end"
|
8
|
-
# You can run it for each number in a list:
|
9
|
-
# [1,2,3].each &method(:add).bind(1)
|
10
|
-
# => 234
|
11
|
-
#
|
12
|
-
def bind *args
|
13
|
-
Proc.new do |*more|
|
14
|
-
self.call *(args + more)
|
15
|
-
end
|
5
|
+
module ClassMethods
|
16
6
|
end
|
17
7
|
|
18
|
-
|
19
|
-
|
8
|
+
module InstanceMethods
|
9
|
+
|
10
|
+
# Bind an argument to a method.
|
11
|
+
# Very useful for the proc shorthand.
|
12
|
+
# For example say there's a method "def add(a,b); print a + b; end"
|
13
|
+
# You can run it for each number in a list:
|
14
|
+
# [1,2,3].each &method(:add).bind(1)
|
15
|
+
# => 234
|
16
|
+
#
|
17
|
+
module Bind
|
18
|
+
def bind *args
|
19
|
+
Proc.new do |*more|
|
20
|
+
self.call *(args + more)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
20
25
|
end
|
21
26
|
|
22
27
|
end
|
@@ -2,61 +2,81 @@
|
|
2
2
|
#
|
3
3
|
module Gemmy::Patches::ObjectPatch
|
4
4
|
|
5
|
-
|
6
|
-
#
|
7
|
-
def verbose_mode
|
8
|
-
$VERBOSE = true
|
5
|
+
module ClassMethods
|
9
6
|
end
|
10
7
|
|
11
|
-
|
12
|
-
# @param msg [String] optional
|
13
|
-
#
|
14
|
-
def error(msg='')
|
15
|
-
raise RuntimeError, msg
|
16
|
-
end
|
8
|
+
module InstanceMethods
|
17
9
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
10
|
+
# Turns on verbose mode, showing warnings
|
11
|
+
#
|
12
|
+
module VerboseMode
|
13
|
+
def verbose_mode
|
14
|
+
$VERBOSE = true
|
15
|
+
end
|
16
|
+
end
|
25
17
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
18
|
+
# Generic error. Raises RuntimeError
|
19
|
+
# @param msg [String] optional
|
20
|
+
#
|
21
|
+
module Error
|
22
|
+
def error(msg='')
|
23
|
+
raise RuntimeError, msg
|
24
|
+
end
|
25
|
+
end
|
32
26
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
27
|
+
# Prints a string then gets input
|
28
|
+
# @param txt [String]
|
29
|
+
#
|
30
|
+
module Prompt
|
31
|
+
def _prompt(txt)
|
32
|
+
puts txt
|
33
|
+
gets.chomp
|
34
|
+
end
|
35
|
+
end
|
40
36
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
37
|
+
# Shifts one ARGV and raises a message if it's undefined.
|
38
|
+
# @param msg [String]
|
39
|
+
#
|
40
|
+
module GetArgOrError
|
41
|
+
def get_arg_or_error(msg)
|
42
|
+
([ARGV.shift, msg].tap &method(:error_if_blank)).shift
|
43
|
+
end
|
44
|
+
end
|
48
45
|
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
# Writes a string to a file
|
47
|
+
# @param file [String] path to write to
|
48
|
+
# @param text [String] text to write
|
49
|
+
#
|
50
|
+
module Write
|
51
|
+
def write(file:, text:)
|
52
|
+
File.open(file, 'w') { |f| f.write text }
|
53
|
+
end
|
54
|
+
end
|
52
55
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
56
|
+
# if args[0] (object) is blank, raises args[1] (message)
|
57
|
+
# @param args [Array] - value 1 is obj, value 2 is msg
|
58
|
+
#
|
59
|
+
module ErrorIfBlank
|
60
|
+
def error_if_blank(args)
|
61
|
+
obj, msg = args
|
62
|
+
obj.blank? && error(msg)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module M
|
67
|
+
# shorter proc shorthands
|
68
|
+
def m(*args)
|
69
|
+
method(*args)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# method which does absolutely nothing, ignoring all arguments
|
74
|
+
#
|
75
|
+
module Nothing
|
76
|
+
def nothing(*args)
|
77
|
+
end
|
78
|
+
end
|
57
79
|
|
58
|
-
refine Object do
|
59
|
-
include Gemmy::Patches::ObjectPatch
|
60
80
|
end
|
61
81
|
|
62
82
|
end
|
@@ -2,20 +2,25 @@
|
|
2
2
|
#
|
3
3
|
module Gemmy::Patches::StringPatch
|
4
4
|
|
5
|
-
|
6
|
-
#
|
7
|
-
# this takes an indented heredoc and treats it as if the first line is not
|
8
|
-
# indented.
|
9
|
-
#
|
10
|
-
# Instead of using alias, a method is defined here to enable modular
|
11
|
-
# patches
|
12
|
-
#
|
13
|
-
def unindent
|
14
|
-
strip_heredoc
|
5
|
+
module ClassMethods
|
15
6
|
end
|
16
7
|
|
17
|
-
|
18
|
-
|
8
|
+
module InstanceMethods
|
9
|
+
|
10
|
+
# reference 'strip_heredoc' (provided by active support) by 'unindent'
|
11
|
+
#
|
12
|
+
# this takes an indented heredoc and treats it as if the first line is not
|
13
|
+
# indented.
|
14
|
+
#
|
15
|
+
# Instead of using alias, a method is defined here to enable modular
|
16
|
+
# patches
|
17
|
+
#
|
18
|
+
module Unindent
|
19
|
+
def unindent
|
20
|
+
strip_heredoc
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
19
24
|
end
|
20
25
|
|
21
26
|
end
|
@@ -2,18 +2,24 @@
|
|
2
2
|
#
|
3
3
|
module Gemmy::Patches::SymbolPatch
|
4
4
|
|
5
|
-
|
6
|
-
# http://stackoverflow.com/a/23711606/2981429
|
7
|
-
#
|
8
|
-
# Example: [1,2,3].map &:*.with(2)
|
9
|
-
# => [2,4,6]
|
10
|
-
#
|
11
|
-
def with(*args, &block)
|
12
|
-
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
|
5
|
+
module ClassMethods
|
13
6
|
end
|
14
7
|
|
15
|
-
|
16
|
-
|
8
|
+
module InstanceMethods
|
9
|
+
|
10
|
+
# Patch symbol so the proc shorthand can take extra arguments
|
11
|
+
# http://stackoverflow.com/a/23711606/2981429
|
12
|
+
#
|
13
|
+
# Example: [1,2,3].map &:*.with(2)
|
14
|
+
# => [2,4,6]
|
15
|
+
#
|
16
|
+
module With
|
17
|
+
def with(*args, &block)
|
18
|
+
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
17
22
|
end
|
18
23
|
|
24
|
+
|
19
25
|
end
|
@@ -5,14 +5,17 @@
|
|
5
5
|
#
|
6
6
|
module Gemmy::Patches::ThreadPatch
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
module ClassMethods
|
9
|
+
module Included
|
10
|
+
# Ensure that threads bubble up their errors
|
11
|
+
#
|
12
|
+
def self.included(base)
|
13
|
+
Thread.abort_on_exception = true
|
14
|
+
end
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
|
-
|
15
|
-
include Gemmy::Patches::ThreadPatch
|
18
|
+
module InstanceMethods
|
16
19
|
end
|
17
20
|
|
18
21
|
end
|
data/lib/gemmy/patches.rb
CHANGED
@@ -1,23 +1,89 @@
|
|
1
1
|
# Gemmy provides patches for a few of the core classes.
|
2
2
|
#
|
3
|
-
#
|
3
|
+
# Use Gemmy#load_globally to load these on the root namespace
|
4
4
|
# For a refinements-based approach, use this in a class/module definition:
|
5
5
|
#
|
6
|
-
# Gemmy::Patches.
|
6
|
+
# Gemmy::Patches.class_refinements.each { |klass| using klass }
|
7
7
|
#
|
8
|
-
# Note that there are nuances for how refinements are used.
|
9
|
-
# the patches using define_method, for example.
|
8
|
+
# Note that there are nuances for how refinements are used.
|
9
|
+
# You can't refer to the patches using define_method, for example.
|
10
10
|
#
|
11
11
|
# See examples/01_using_as_refinement.rb for more info
|
12
|
+
# (linked from the README at github.com/maxpleaner/gemmy
|
12
13
|
#
|
13
14
|
module Gemmy::Patches
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
# The usage of this method is to load all the patches for some core classes.
|
17
|
+
# With no arguments it will include all patches
|
18
|
+
# There are two optional params (keyword arguments).
|
19
|
+
# @param only [Array<Symbol>] if provided, will only patch those classes
|
20
|
+
# @param except [Array<Symbol>] if provided, will exclude those classes
|
21
|
+
# @return [Array<Class>] to be iteratively passed to 'using'
|
22
|
+
def self.class_refinements(only: nil, except: nil)
|
23
|
+
return @@refined if defined? @@refined
|
24
|
+
@@refined = core_patches.reduce([]) do |arr, (core_klass_sym, patch_klass)|
|
25
|
+
next if only && !only.include?(core_klass_sym)
|
26
|
+
next if except && except.include?(core_klass_sym)
|
27
|
+
core_klass = const_get core_klass_sym.to_s
|
28
|
+
class_patches = patch_klass.const_get "ClassMethods"
|
29
|
+
instance_patches = patch_klass.const_get "InstanceMethods"
|
30
|
+
class_patches.constants.each do |patch_class_sym|
|
31
|
+
patch_class = class_patches.const_get patch_class_sym
|
32
|
+
patch_as_class_method(core_klass, patch_class)
|
33
|
+
arr.push patch_class
|
34
|
+
end
|
35
|
+
instance_patches.constants.each do |patch_class_sym|
|
36
|
+
patch_class = instance_patches.const_get patch_class_sym
|
37
|
+
patch_as_instance_method(core_klass, patch_class)
|
38
|
+
arr.push patch_class
|
39
|
+
end
|
40
|
+
arr
|
41
|
+
end.compact
|
42
|
+
@@refined
|
43
|
+
end
|
44
|
+
|
45
|
+
# Cherry pick methods to patch.
|
46
|
+
# @param hash [Hash] with a particular structure:
|
47
|
+
# top level keys are Symbols referring to core classes, i.e. :String
|
48
|
+
# top level values are hashes.
|
49
|
+
# second level keys are either :ClassMethods or :InstanceMethods
|
50
|
+
# referring to the scope
|
51
|
+
# second level values are arrays of symbols (one per method)
|
52
|
+
#
|
53
|
+
# To give an example:
|
54
|
+
#
|
55
|
+
# Gemmy::Patches.method_refinements(
|
56
|
+
# Array: { InstanceMethods: [:Recurse, :KeyBy] }
|
57
|
+
# ).each { |r| using r }
|
58
|
+
#
|
59
|
+
def self.method_refinements(hash)
|
60
|
+
hash.map do |core_klass_sym, patch_types|
|
61
|
+
core_class = const_get(core_klass_sym)
|
62
|
+
patch_types.map do |type_sym, patch_method_syms|
|
63
|
+
patch_methods = core_patches[core_klass_sym].const_get type_sym.to_s
|
64
|
+
patch_method_syms.map do |patch_method_sym|
|
65
|
+
method_class = patch_methods.const_get(patch_method_sym)
|
66
|
+
if type_sym == :InstanceMethods
|
67
|
+
patch_as_instance_method(core_class, method_class)
|
68
|
+
elsif type_sym == :ClassMethods
|
69
|
+
patch_as_class_method(core_class, method_class)
|
70
|
+
end
|
71
|
+
method_class
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end.flatten.compact
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.patch_as_class_method(core_klass, patch_klass)
|
78
|
+
patch_klass.send(:refine, core_klass.singleton_class) do
|
79
|
+
include patch_klass
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.patch_as_instance_method(core_klass, patch_klass)
|
84
|
+
patch_klass.send(:refine, core_klass) do
|
85
|
+
include patch_klass
|
86
|
+
end
|
21
87
|
end
|
22
88
|
|
23
89
|
def self.core_patches
|
@@ -25,10 +91,11 @@ module Gemmy::Patches
|
|
25
91
|
String: Gemmy::Patches::StringPatch,
|
26
92
|
Symbol: Gemmy::Patches::SymbolPatch,
|
27
93
|
Object: Gemmy::Patches::ObjectPatch,
|
28
|
-
Array:
|
94
|
+
Array: Gemmy::Patches::ArrayPatch,
|
29
95
|
Method: Gemmy::Patches::MethodPatch,
|
30
|
-
Hash:
|
96
|
+
Hash: Gemmy::Patches::HashPatch,
|
31
97
|
Thread: Gemmy::Patches::ThreadPatch,
|
98
|
+
Integer: Gemmy::Patches::IntegerPatch
|
32
99
|
}.with_indifferent_access
|
33
100
|
end
|
34
101
|
|
data/lib/gemmy/tasks/make_gem.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Gemmy::Tests::ComponentTests::DynamicStepsTests
|
2
2
|
|
3
|
-
Gemmy::Patches.
|
3
|
+
Gemmy::Patches.class_refinements.each { |r| using r }
|
4
4
|
|
5
5
|
def self.run
|
6
6
|
runner_class = Class.new
|
@@ -18,4 +18,5 @@ module Gemmy::Tests::ComponentTests::DynamicStepsTests
|
|
18
18
|
puts " step".blue
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
21
22
|
end
|
data/lib/gemmy/version.rb
CHANGED
data/lib/gemmy.rb
CHANGED
@@ -15,13 +15,28 @@ class Gemmy
|
|
15
15
|
# This is the method handling the global case
|
16
16
|
#
|
17
17
|
def self.load_globally
|
18
|
-
Patches.core_patches.
|
19
|
-
core_klass_name.to_s.constantize
|
18
|
+
core_patches = Patches.core_patches.map do |core_klass_name, patch_klass|
|
19
|
+
core_klass = core_klass_name.to_s.constantize
|
20
|
+
instance_method_class = patch_klass.const_get("InstanceMethods")
|
21
|
+
class_method_class = patch_klass.const_get("ClassMethods")
|
22
|
+
instance_classes = instance_method_class.constants.map do |klass_sym|
|
23
|
+
klass = instance_method_class.const_get klass_sym
|
24
|
+
core_klass.include klass
|
25
|
+
klass
|
26
|
+
end
|
27
|
+
class_classes = class_method_class.constants.map do |klass_sym|
|
28
|
+
klass = class_method_class.const_get klass_sym
|
29
|
+
core_klass.extend klass
|
30
|
+
klass
|
31
|
+
end
|
32
|
+
[instance_classes, class_classes]
|
20
33
|
end
|
21
|
-
Components.list.
|
34
|
+
components = Components.list.map do |patch_klass|
|
22
35
|
Object.include patch_klass
|
23
36
|
Object.extend patch_klass
|
37
|
+
patch_klass
|
24
38
|
end
|
39
|
+
[components, core_patches].flatten
|
25
40
|
end
|
26
41
|
end
|
27
42
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gemmyrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- max pleaner
|
@@ -102,6 +102,7 @@ files:
|
|
102
102
|
- lib/gemmy/patches.rb
|
103
103
|
- lib/gemmy/patches/array_patch.rb
|
104
104
|
- lib/gemmy/patches/hash_patch.rb
|
105
|
+
- lib/gemmy/patches/integer_patch.rb
|
105
106
|
- lib/gemmy/patches/method_patch.rb
|
106
107
|
- lib/gemmy/patches/object_patch.rb
|
107
108
|
- lib/gemmy/patches/string_patch.rb
|