gemmyrb 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3738de0bcb6474f41bc2f0959d5fc57c056e1c2d
4
- data.tar.gz: 6d5a3339c10b4ecd92e29d4f380b66f536748756
3
+ metadata.gz: 57241faae027cf6e4416104dcedd95dcdb6f3d56
4
+ data.tar.gz: 6f4caf998b97cd0e9fdc84f243f19813248e0541
5
5
  SHA512:
6
- metadata.gz: f9afef465e6795c1e168dda4178c1cf77d45508f9069e279891ee022b08cd27ac64390de49ada10978ab494898bdad6b1c3a8f67ce24c6b62154e8a5ec4484de
7
- data.tar.gz: f5183dea0d4e286ac33bffa45d060d379b0ddf3c108e098d8f8c408cf0b6ae67a2b7e5497519fb20ea7496df8be53ad47ca5e9e8a426c68807ae18aa16787718
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 = ARGV.clone
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.refinements.each { |r| using r }
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,
@@ -9,7 +9,7 @@
9
9
  end
10
10
 
11
11
  def self.list
12
- [
12
+ @@list ||= [
13
13
  Gemmy::Components::DynamicSteps
14
14
  ]
15
15
  end
@@ -1,19 +1,191 @@
1
1
  # Array patches
2
2
  #
3
3
  module Gemmy::Patches::ArrayPatch
4
- # checks if any of the results of an array do not respond truthily
5
- # to a block
6
- #
7
- # For example, to check if any items of an array are truthy:
8
- # [false, nil, ''].any_not? &:blank?
9
- # => false
10
- #
11
- def any_not?(&blk)
12
- any? { |item| ! blk.call(item) }
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
- refine Array do
16
- include Gemmy::Patches::ArrayPatch
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
- # The opposite of Hash#dig
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
- # The bury method, taking the input hash as a parameter
22
- # Used by the Hash#bury instance method
23
- def self._bury(caller_hash, *args)
24
- if args.count < 2
25
- raise ArgumentError.new("2 or more arguments required")
26
- elsif args.count == 2
27
- caller_hash[args[0]] = args[1]
28
- else
29
- arg = args.shift
30
- caller_hash[arg] = {} unless caller_hash[arg]
31
- _bury(caller_hash[arg], *args) unless args.empty?
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
- # Turns a hash into one that's "autovivified"
37
- # meaning it's default values for keys is an empty hash.
38
- # The result is that you can set nested keys without initializing
39
- # more than one hash layer.
40
- #
41
- # Usage:
42
- # hash = {}.autovivified
43
- # hash[:a][:b] = 0
44
- # puts hash[:a][:b]
45
- # => 0
46
- #
47
- def autovivified
48
- Gemmy::Patches::HashPatch._autovivified(self)
49
- end
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
- def self._autovivified(caller_hash)
52
- result = Hash.new { |hash,key| hash[key] = Hash.new(&hash.default_proc) }
53
- result.deep_merge caller_hash
54
- end
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
- # Sets up a hash to mirror all changes to a database file
57
- # All nested gets & sets require a list of keys, passed as subsequent args.
58
- # Instead of [] and []=, use get and set
59
- #
60
- # Everything in the db is contained in a hash with one predefined
61
- # key, :data. The value is an empty, autovivified hash.
62
- #
63
- # This also makes the caller hash autovivified
64
- #
65
- # Example:
66
- #
67
- # hash = {}.persisted("db.yaml")
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
- end
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(self, *keys, val)
107
+ bury = Gemmy::Patches::HashPatch::InstanceMethods::Bury.method(:_bury)
108
+ bury.call(self, *keys, val)
93
109
  @db.transaction do
94
- Gemmy::Patches::HashPatch._bury(@db[:data], *(keys + [val]))
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
- # Bind an argument to a method.
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
- refine Method do
19
- include Gemmy::Patches::MethodPatch
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
- # Turns on verbose mode, showing warnings
6
- #
7
- def verbose_mode
8
- $VERBOSE = true
5
+ module ClassMethods
9
6
  end
10
7
 
11
- # Generic error. Raises RuntimeError
12
- # @param msg [String] optional
13
- #
14
- def error(msg='')
15
- raise RuntimeError, msg
16
- end
8
+ module InstanceMethods
17
9
 
18
- # Prints a string then gets input
19
- # @param txt [String]
20
- #
21
- def _prompt(txt)
22
- puts txt
23
- gets.chomp
24
- end
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
- # Shifts one ARGV and raises a message if it's undefined.
27
- # @param msg [String]
28
- #
29
- def get_arg_or_error(msg)
30
- ([ARGV.shift, msg].tap &method(:error_if_blank)).shift
31
- end
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
- # Writes a string to a file
34
- # @param file [String] path to write to
35
- # @param text [String] text to write
36
- #
37
- def write(file:, text:)
38
- File.open(file, 'w') { |f| f.write text }
39
- end
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
- # if args[0] (object) is blank, raises args[1] (message)
42
- # @param args [Array] - value 1 is obj, value 2 is msg
43
- #
44
- def error_if_blank(args)
45
- obj, msg = args
46
- obj.blank? && error(msg)
47
- end
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
- # shorter proc shorthands
50
- #
51
- alias m method
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
- # method which does absolutely nothing, ignoring all arguments
54
- #
55
- def nothing(*args)
56
- end
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
- # reference 'strip_heredoc' (provided by active support) by 'unindent'
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
- refine String do
18
- include Gemmy::Patches::StringPatch
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
- # Patch symbol so the proc shorthand can take extra arguments
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
- refine Symbol do
16
- include Gemmy::Patches::SymbolPatch
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
- # Ensure that threads bubble up their errors
9
- #
10
- def self.included(base)
11
- Thread.abort_on_exception = true
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
- refine Thread do
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
- # See {Gemmy#load_globally} for how to load these on the root namespace
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.refinements.each { |klass| using klass }
6
+ # Gemmy::Patches.class_refinements.each { |klass| using klass }
7
7
  #
8
- # Note that there are nuances for how refinements are used. You can't refer to
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
- def self.refinements(only: nil, except: nil)
16
- core_patches.select do |core_klass, patch_klass|
17
- return false if only && !only.include?(core_klass)
18
- return false if except && except.include?(core_klass)
19
- true
20
- end.values
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: Gemmy::Patches::ArrayPatch,
94
+ Array: Gemmy::Patches::ArrayPatch,
29
95
  Method: Gemmy::Patches::MethodPatch,
30
- Hash: Gemmy::Patches::HashPatch,
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
 
@@ -21,7 +21,7 @@
21
21
  #
22
22
  class Gemmy::Tasks::MakeGem
23
23
 
24
- Gemmy::Patches.refinements.each { |r| using r }
24
+ Gemmy::Patches.class_refinements.each { |r| using r }
25
25
 
26
26
  # Builds a skeleton ruby gem.
27
27
  # Prompts for some input using gets.chomp
@@ -1,6 +1,6 @@
1
1
  module Gemmy::Tests::ComponentTests::DynamicStepsTests
2
2
 
3
- Gemmy::Patches.refinements.each { |r| using r }
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
@@ -26,7 +26,7 @@ module Gemmy::Tests
26
26
 
27
27
  class PatchedClass
28
28
 
29
- Gemmy::Patches.refinements.each { |r| using r }
29
+ Gemmy::Patches.class_refinements.each { |r| using r }
30
30
 
31
31
  extend Gemmy::Tests::PatchTests::Error
32
32
 
data/lib/gemmy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Gemmy
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
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.each do |core_klass_name, patch_klass|
19
- core_klass_name.to_s.constantize.include patch_klass
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.each do |patch_klass|
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.2
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