bblib 0.4.1 → 1.0.2
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/.gitignore +12 -11
- data/.rspec +2 -2
- data/.travis.yml +4 -4
- data/CODE_OF_CONDUCT.md +13 -13
- data/Gemfile +4 -4
- data/LICENSE.txt +21 -21
- data/Rakefile +6 -6
- data/bblib.gemspec +34 -34
- data/bin/console +14 -14
- data/bin/setup +7 -7
- data/lib/array/bbarray.rb +2 -2
- data/lib/bblib.rb +1 -0
- data/lib/bblib/version.rb +1 -1
- data/lib/class/effortless.rb +2 -2
- data/lib/file/bbfile.rb +10 -8
- data/lib/hash/bbhash.rb +23 -15
- data/lib/hash/hash_struct.rb +1 -1
- data/lib/hash/tree_hash.rb +1 -1
- data/lib/hash_path/hash_path.rb +3 -3
- data/lib/hash_path/part.rb +2 -2
- data/lib/html/bbhtml.rb +1 -0
- data/lib/html/builder.rb +15 -6
- data/lib/html/tag.rb +74 -5
- data/lib/html/tag_set.rb +20 -0
- data/lib/logging/bblogging.rb +2 -1
- data/lib/mixins/attrs.rb +40 -25
- data/lib/mixins/bbmixins.rb +2 -0
- data/lib/mixins/family_tree.rb +12 -0
- data/lib/mixins/hooks.rb +8 -1
- data/lib/mixins/logger.rb +10 -10
- data/lib/mixins/prototype.rb +26 -0
- data/lib/mixins/simple_init.rb +47 -17
- data/lib/mixins/type_init.rb +21 -0
- data/lib/number/bbnumber.rb +90 -0
- data/lib/object/bbobject.rb +1 -5
- data/lib/opal/bbopal.rb +5 -0
- data/lib/os/bbos.rb +0 -63
- data/lib/string/bbstring.rb +22 -1
- data/lib/string/cases.rb +4 -4
- data/lib/time/bbtime.rb +27 -0
- data/lib/time/task_timer.rb +3 -1
- metadata +4 -2
- data/lib/os/bbsys.rb +0 -255
data/lib/mixins/hooks.rb
CHANGED
@@ -83,17 +83,21 @@ module BBLib
|
|
83
83
|
# modify_args - Replaces the original args with the returned value of the
|
84
84
|
# send_method - Sends the method name as an argument to the hooked method.
|
85
85
|
# before hook method.
|
86
|
+
# try_first - Sends the args to the desired hook first and if the result
|
87
|
+
# is non-nil, the result is sent instead of calling the hooked
|
88
|
+
# method.
|
86
89
|
def _hook_before_method(method, hook, opts = {})
|
87
90
|
return false if method == hook
|
88
91
|
_add_hooked_method(:before, hook, method)
|
89
92
|
original = instance_method(method)
|
90
93
|
@_defining_hook = true
|
91
94
|
define_method(method) do |*args, &block|
|
92
|
-
if opts[:send_args] || opts[:send_arg] || opts[:modify_args] || opts[:send_method]
|
95
|
+
if opts[:send_args] || opts[:send_arg] || opts[:modify_args] || opts[:send_method] || opts[:try_first]
|
93
96
|
margs = args
|
94
97
|
margs = [method] + args if opts[:send_method]
|
95
98
|
margs = args + [opts[:add_args]].flatten(1) if opts[:add_args]
|
96
99
|
result = method(hook).call(*margs)
|
100
|
+
return result if result && opts[:try_first]
|
97
101
|
args = result if opts[:modify_args]
|
98
102
|
else
|
99
103
|
method(hook).call
|
@@ -112,6 +116,7 @@ module BBLib
|
|
112
116
|
# modify_value - Opts must also include one of the two above. Passes the returned
|
113
117
|
# => value of the method to the hook and returns the hooks value
|
114
118
|
# => rather than the original methods value.
|
119
|
+
# send_all - Sends a hash containing the args, method and value (return).
|
115
120
|
def _hook_after_method(method, hook, opts = {})
|
116
121
|
return false if method == hook
|
117
122
|
_add_hooked_method(:after, hook, method)
|
@@ -127,6 +132,8 @@ module BBLib
|
|
127
132
|
elsif opts[:send_return_ary] || opts[:send_value_ary]
|
128
133
|
result = method(hook).call(*rtr)
|
129
134
|
rtr = result if opts[:modify_value] || opts[:modify_return]
|
135
|
+
elsif opts[:send_all]
|
136
|
+
result = method(hook).call(args: args, value: rtr, method: method)
|
130
137
|
else
|
131
138
|
method(hook).call
|
132
139
|
end
|
data/lib/mixins/logger.rb
CHANGED
@@ -5,11 +5,11 @@ module BBLib
|
|
5
5
|
self.class.logger
|
6
6
|
end
|
7
7
|
|
8
|
-
[:debug, :info, :warn, :error, :fatal, :unknown].each do |sev|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
8
|
+
# [:debug, :info, :warn, :error, :fatal, :unknown].each do |sev|
|
9
|
+
# define_method(sev) do |msg = nil, &block|
|
10
|
+
# logger.send(sev) { "[#{self.class}] #{msg ? msg : block.call}" }
|
11
|
+
# end
|
12
|
+
# end
|
13
13
|
|
14
14
|
def self.included(base)
|
15
15
|
base.extend ClassMethods
|
@@ -20,11 +20,11 @@ module BBLib
|
|
20
20
|
BBLib.logger
|
21
21
|
end
|
22
22
|
|
23
|
-
[:debug, :info, :warn, :error, :fatal, :unknown].each do |sev|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
23
|
+
# [:debug, :info, :warn, :error, :fatal, :unknown].each do |sev|
|
24
|
+
# define_method(sev) do |msg = nil, &block|
|
25
|
+
# logger.send(sev) { "[#{self}] #{msg ? msg : block.call}" }
|
26
|
+
# end
|
27
|
+
# end
|
28
28
|
end
|
29
29
|
|
30
30
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
module BBLib
|
3
|
+
module Prototype
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def prototype(opts = prototype_defaults)
|
10
|
+
@prototype ||= self.new(*prototype_defaults)
|
11
|
+
end
|
12
|
+
|
13
|
+
def prototype_defaults
|
14
|
+
[]
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(method, *args, &block)
|
18
|
+
prototype.respond_to?(method) ? prototype.send(method, *args, &block) : super
|
19
|
+
end
|
20
|
+
|
21
|
+
def respond_to_missing?(method, include_private = false)
|
22
|
+
prototype.respond_to?(method) || super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/mixins/simple_init.rb
CHANGED
@@ -14,32 +14,56 @@ module BBLib
|
|
14
14
|
define_method(:initialize) do |*args, &block|
|
15
15
|
send(:simple_setup) if respond_to?(:simple_setup, true)
|
16
16
|
send(:simple_preinit, *args, &block) if respond_to?(:simple_preinit, true)
|
17
|
-
_initialize(*args)
|
17
|
+
_initialize(*args, &block)
|
18
18
|
send(:simple_init, *args, &block) if respond_to?(:simple_init, true)
|
19
|
-
|
19
|
+
if block && !_attrs.any? { |k, v| v[:options][:arg_at] == :block }
|
20
|
+
result = instance_eval(&block)
|
21
|
+
simple_init_block_result(result) if respond_to?(:simple_init_block_result, true)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if BBLib.in_opal?
|
27
|
+
base.singleton_class.class_eval do
|
28
|
+
alias __new new
|
29
|
+
|
30
|
+
def new(*args, &block)
|
31
|
+
named = BBLib.named_args(*args)
|
32
|
+
if init_foundation && named[init_foundation_method] && ((named[init_foundation_method] != self.send(init_foundation_method)) rescue false)
|
33
|
+
klass = [self, descendants].flatten.find do |k|
|
34
|
+
if init_foundation_compare
|
35
|
+
init_foundation_compare.call(k.send(init_foundation_method), named[init_foundation_method])
|
36
|
+
else
|
37
|
+
k.send(init_foundation_method).to_s == named[init_foundation_method].to_s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
raise ArgumentError, "Unknown #{init_foundation_method} \"#{named[init_foundation_method]}\" for #{self}" unless klass
|
41
|
+
klass == self ? __new(*args, &block) : klass.new(*args, &block)
|
42
|
+
else
|
43
|
+
__new(*args, &block)
|
44
|
+
end
|
45
|
+
end
|
20
46
|
end
|
21
47
|
end
|
22
48
|
end
|
23
49
|
|
24
50
|
module ClassMethods
|
25
51
|
|
26
|
-
# TODO: Currently init foundation breaks when running in opal.
|
27
52
|
unless BBLib.in_opal?
|
28
53
|
# Overriden new method that allows parent classes to dynamically generate
|
29
|
-
# instantiations of descendants by using the named
|
30
|
-
# :_class needs to be the fully qualified name of the descendant.
|
54
|
+
# instantiations of descendants by using the named init_foundation_method argument.
|
31
55
|
def new(*args, &block)
|
32
56
|
named = BBLib.named_args(*args)
|
33
57
|
if init_foundation && named[init_foundation_method] && ((named[init_foundation_method] != self.send(init_foundation_method)) rescue false)
|
34
|
-
klass = descendants.find do |k|
|
58
|
+
klass = [self, descendants].flatten.find do |k|
|
35
59
|
if init_foundation_compare
|
36
60
|
init_foundation_compare.call(k.send(init_foundation_method), named[init_foundation_method])
|
37
61
|
else
|
38
62
|
k.send(init_foundation_method).to_s == named[init_foundation_method].to_s
|
39
63
|
end
|
40
64
|
end
|
41
|
-
raise ArgumentError, "Unknown
|
42
|
-
klass.new(*args, &block)
|
65
|
+
raise ArgumentError, "Unknown #{init_foundation_method} \"#{named[init_foundation_method]}\"" unless klass
|
66
|
+
klass == self ? super : klass.new(*args, &block)
|
43
67
|
else
|
44
68
|
super
|
45
69
|
end
|
@@ -65,14 +89,14 @@ module BBLib
|
|
65
89
|
end
|
66
90
|
|
67
91
|
def init_foundation_compare(&block)
|
68
|
-
@init_foundation_compare = block if
|
92
|
+
@init_foundation_compare = block if block
|
69
93
|
@init_foundation_compare
|
70
94
|
end
|
71
95
|
|
72
96
|
def setup_init_foundation(method, &block)
|
73
97
|
self.init_foundation = true
|
74
98
|
self.init_foundation_method(method)
|
75
|
-
self.init_foundation_compare(&block) if
|
99
|
+
self.init_foundation_compare(&block) if block
|
76
100
|
end
|
77
101
|
|
78
102
|
def ancestor_init_foundation_method
|
@@ -127,17 +151,23 @@ module BBLib
|
|
127
151
|
|
128
152
|
protected
|
129
153
|
|
130
|
-
def _initialize(*args)
|
154
|
+
def _initialize(*args, &block)
|
131
155
|
named = BBLib.named_args(*args)
|
132
156
|
if self.class.respond_to?(:_attrs)
|
133
157
|
set_v_arg = self.class._attrs.map do |method, details|
|
134
|
-
next unless details[:options][:arg_at] && details[:options][:arg_at].is_a?(Integer)
|
158
|
+
next unless details[:options][:arg_at] && (details[:options][:arg_at].is_a?(Integer) || details[:options][:arg_at] == :block)
|
159
|
+
if details[:options][:arg_at] == :block
|
160
|
+
send("#{method}=", block) if block
|
161
|
+
method
|
162
|
+
else
|
135
163
|
index = details[:options][:arg_at]
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
164
|
+
if args.size > index
|
165
|
+
accept = details[:options][:arg_at_accept]
|
166
|
+
next if args[index].is_a?(Hash) && (accept.nil? || ![accept].flatten.include?(Hash))
|
167
|
+
if accept.nil? || [accept].flatten.any? { |a| a >= args[index].class }
|
168
|
+
send("#{method}=", args[index])
|
169
|
+
method
|
170
|
+
end
|
141
171
|
end
|
142
172
|
end
|
143
173
|
end.compact
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module BBLib
|
2
|
+
# Requires Simple init to be loaded first. This sets up a very basic
|
3
|
+
# init foundation by adding a method called type and setting the init
|
4
|
+
# foundation method to it.
|
5
|
+
module TypeInit
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
base.send(:bridge_method, :type)
|
10
|
+
base.send(:serialize_method, :type, always: true)
|
11
|
+
base.send(:setup_init_foundation, :type) { |a, b| a && b && a.to_s.to_sym == b.to_s.to_sym }
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def type
|
16
|
+
to_s.split('::').last.method_case.to_sym
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/number/bbnumber.rb
CHANGED
@@ -17,4 +17,94 @@ module BBLib
|
|
17
17
|
num = min if max && num > max
|
18
18
|
num
|
19
19
|
end
|
20
|
+
|
21
|
+
NUMBER_WORDS = {
|
22
|
+
special: {
|
23
|
+
0 => nil,
|
24
|
+
1 => 'one',
|
25
|
+
2 => 'two',
|
26
|
+
3 => 'three',
|
27
|
+
4 => 'four',
|
28
|
+
5 => 'five',
|
29
|
+
6 => 'six',
|
30
|
+
7 => 'seven',
|
31
|
+
8 => 'eight',
|
32
|
+
9 => 'nine',
|
33
|
+
10 => 'ten',
|
34
|
+
11 => 'eleven',
|
35
|
+
12 => 'twleve',
|
36
|
+
13 => 'thirteen',
|
37
|
+
14 => 'fourteen',
|
38
|
+
15 => 'fifteen',
|
39
|
+
16 => 'sixteen',
|
40
|
+
17 => 'seventeen',
|
41
|
+
18 => 'eighteen',
|
42
|
+
19 => 'nineteen'
|
43
|
+
},
|
44
|
+
double_range: {
|
45
|
+
2 => 'twenty',
|
46
|
+
3 => 'thirty',
|
47
|
+
4 => 'forty',
|
48
|
+
5 => 'fifty',
|
49
|
+
6 => 'sixty',
|
50
|
+
7 => 'seventy',
|
51
|
+
8 => 'eighty',
|
52
|
+
9 => 'ninety'
|
53
|
+
},
|
54
|
+
ranges: [
|
55
|
+
nil, 'thousand', 'million', 'billion', 'trillion', 'quadrillion',
|
56
|
+
'quintillion', 'sextillion', 'septillion'
|
57
|
+
]
|
58
|
+
}
|
59
|
+
|
60
|
+
def self.number_spelled_out(number, range = 0, include_and: true)
|
61
|
+
number = number.to_i
|
62
|
+
negative = number.negative?
|
63
|
+
number = number * -1 if negative
|
64
|
+
return 'zero' if number.zero?
|
65
|
+
str = []
|
66
|
+
three_digit = number > 999 ? number.to_s[-3..-1].to_i : number
|
67
|
+
case three_digit
|
68
|
+
when 1..19
|
69
|
+
str << NUMBER_WORDS[:special][three_digit]
|
70
|
+
when 20..99
|
71
|
+
str << NUMBER_WORDS[:double_range][three_digit.to_s[-2].to_i]
|
72
|
+
str << NUMBER_WORDS[:special][three_digit.to_s[-1].to_i]
|
73
|
+
when 100..999
|
74
|
+
str << NUMBER_WORDS[:special][three_digit.to_s[0].to_i]
|
75
|
+
str << 'hundred'
|
76
|
+
str << 'and' if include_and
|
77
|
+
if three_digit.to_s[-2].to_i == 1
|
78
|
+
str << NUMBER_WORDS[:special][three_digit.to_s[-2..-1].to_i]
|
79
|
+
else
|
80
|
+
str << NUMBER_WORDS[:double_range][three_digit.to_s[-2].to_i]
|
81
|
+
str << NUMBER_WORDS[:special][three_digit.to_s[-1].to_i]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
str << NUMBER_WORDS[:ranges][range] unless str.compact.empty?
|
85
|
+
(negative ? 'negative ' : '') +
|
86
|
+
((number.to_s.size > 3 ? "#{number_spelled_out(number.to_s[0..-4].to_i, range + 1)} " : '') +
|
87
|
+
str.compact.join(' ')).gsub(/\s+/, ' ')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Integer
|
92
|
+
# Convert this integer into a string with every three digits separated by a delimiter
|
93
|
+
def to_delimited_s(delim = ',')
|
94
|
+
self.to_s.reverse.gsub(/(\d{3})/, "\\1#{delim}").reverse.uncapsulate(',')
|
95
|
+
end
|
96
|
+
|
97
|
+
def spell_out
|
98
|
+
BBLib.number_spelled_out(self)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Float
|
103
|
+
# Convert this integer into a string with every three digits separated by a delimiter
|
104
|
+
# on the left side of the decimal
|
105
|
+
def to_delimited_s(delim = ',')
|
106
|
+
split = self.to_s.split('.')
|
107
|
+
split[0] = split.first.reverse.gsub(/(\d{3})/, "\\1#{delim}").reverse
|
108
|
+
split.join('.').uncapsulate(',')
|
109
|
+
end
|
20
110
|
end
|
data/lib/object/bbobject.rb
CHANGED
@@ -1,13 +1,9 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
# require_relative 'attr'
|
3
|
-
# require_relative 'hooks'
|
4
|
-
|
5
1
|
module BBLib
|
6
2
|
def self.are_all?(klass, *vars)
|
7
3
|
vars.all? { |var| var.is_a?(klass) }
|
8
4
|
end
|
9
5
|
|
10
|
-
def self.
|
6
|
+
def self.is_any?(obj, *klasses)
|
11
7
|
klasses.any? { |klass| obj.is_a?(klass) }
|
12
8
|
end
|
13
9
|
|
data/lib/opal/bbopal.rb
CHANGED
@@ -2,11 +2,16 @@ module BBLib
|
|
2
2
|
def self.in_opal?
|
3
3
|
RUBY_ENGINE == 'opal'
|
4
4
|
end
|
5
|
+
|
6
|
+
# Used when a method is called while in the wrong engine (such as Opal)
|
7
|
+
class WrongEngineError < StandardError; end
|
5
8
|
end
|
6
9
|
|
7
10
|
if BBLib.in_opal?
|
8
11
|
class Element
|
9
12
|
alias_native :replace_with, :replaceWith
|
10
13
|
alias_native :prepend
|
14
|
+
alias_native :insert_after, :insertAfter
|
15
|
+
alias_native :insert_before, :insertBefore
|
11
16
|
end
|
12
17
|
end
|
data/lib/os/bbos.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative 'bbsys'
|
2
|
-
|
3
1
|
module BBLib
|
4
2
|
module OS
|
5
3
|
def self.os
|
@@ -26,67 +24,6 @@ module BBLib
|
|
26
24
|
!(/#{builds.join('|')}/i =~ RUBY_PLATFORM).nil?
|
27
25
|
end
|
28
26
|
|
29
|
-
def self.os_info
|
30
|
-
if windows?
|
31
|
-
data = `wmic os get manufacturer,name,organization,osarchitecture,version /format:list`
|
32
|
-
data = data.split("\n").reject { |r| r.strip == '' }.map do |m|
|
33
|
-
spl = m.split('=')
|
34
|
-
[spl.first.to_clean_sym.downcase, spl[1..-1].join('=')]
|
35
|
-
end.to_h
|
36
|
-
data[:name] = data[:name].split('|').first
|
37
|
-
data[:osarchitecture] = data[:osarchitecture].extract_integers.first
|
38
|
-
data.hpath_move('osarchitecture' => 'bits')
|
39
|
-
data[:host] = `hostname`.strip
|
40
|
-
data[:os] = os
|
41
|
-
data
|
42
|
-
else
|
43
|
-
release = {}
|
44
|
-
begin
|
45
|
-
# First attempt to get release info uses lsb_release
|
46
|
-
release = `lsb_release -a`.split("\n").map do |l|
|
47
|
-
spl = l.split(':')
|
48
|
-
[
|
49
|
-
spl.first.downcase.to_clean_sym,
|
50
|
-
spl[1..-1].join(':').strip
|
51
|
-
]
|
52
|
-
end.to_h
|
53
|
-
release.hpath_move('description' => 'name', 'release' => 'name', 'distributor_id' => 'manufacturer')
|
54
|
-
rescue
|
55
|
-
# Try finding the release file and parsing it instead of lsb_release
|
56
|
-
begin
|
57
|
-
release = `cat /etc/*release`
|
58
|
-
.split("\n")
|
59
|
-
.reject { |l| !(l.include?(':') || l.include?('=')) }
|
60
|
-
.map { |l| l.msplit('=', ':') }
|
61
|
-
.map { |a| [a.first.downcase.to_clean_sym, a[1..-1].join(':').uncapsulate] }
|
62
|
-
.to_h
|
63
|
-
rescue StandardError => e
|
64
|
-
# Both attempts failed
|
65
|
-
end
|
66
|
-
end
|
67
|
-
{
|
68
|
-
release: `uname -r`.strip,
|
69
|
-
bits: `uname -r` =~ /x86_64/i ? 64 : 32,
|
70
|
-
host: `uname -n`.strip,
|
71
|
-
os: os
|
72
|
-
}.merge(release)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# The following is Windows specific code
|
77
|
-
if windows?
|
78
|
-
|
79
|
-
def self.parse_wmic(cmd)
|
80
|
-
`#{cmd} /format:list`
|
81
|
-
.split("\n\n\n").reject(&:empty?)
|
82
|
-
.map do |l|
|
83
|
-
l.split("\n\n")
|
84
|
-
.map { |l| spl = l.split('='); [spl.first.strip.downcase.to_clean_sym, spl[1..-1].join('=').strip] }.to_h
|
85
|
-
end.reject(&:empty?)
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
27
|
# Mostly platform agnost way to find the full path of an executable in the current env path.
|
91
28
|
def self.which(cmd)
|
92
29
|
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
data/lib/string/bbstring.rb
CHANGED
@@ -94,6 +94,27 @@ module BBLib
|
|
94
94
|
str_b
|
95
95
|
end
|
96
96
|
end
|
97
|
+
|
98
|
+
# Pattern render takes (by default) a mustache style template and then uses
|
99
|
+
# a context (either a Hash or Object) to then interpolate in placeholders.
|
100
|
+
# The default pattern looks for {{method_name}} within the string but can be
|
101
|
+
# customized to a different pattern by setting the pattern named argument.
|
102
|
+
def self.pattern_render(text, context = {}, pattern: /\{{2}.*?\}{2}/, field_pattern: /(?<=^\{{2}).*(?=\}{2})/)
|
103
|
+
raise ArgumentError, "Expected text argument to be a String, got a #{text.class}" unless text.is_a?(String)
|
104
|
+
txt = text.dup
|
105
|
+
txt.scan(pattern).each do |match|
|
106
|
+
field = match.scan(field_pattern).first
|
107
|
+
next unless field
|
108
|
+
value = case context
|
109
|
+
when Hash
|
110
|
+
context[field.to_sym] || context[field]
|
111
|
+
else
|
112
|
+
context.send(field) if context.respond_to?(field)
|
113
|
+
end.to_s
|
114
|
+
txt.sub!(match, value)
|
115
|
+
end
|
116
|
+
txt
|
117
|
+
end
|
97
118
|
end
|
98
119
|
|
99
120
|
class String
|
@@ -130,7 +151,7 @@ class String
|
|
130
151
|
else
|
131
152
|
encapsulator
|
132
153
|
end
|
133
|
-
patterns = delimiters.map { |d| /#{d}(?=(?:[^#{pattern}]|[#{pattern}][^#{pattern}]*[#{pattern}])*$)/}
|
154
|
+
patterns = delimiters.map { |d| /#{Regexp.escape(d)}(?=(?:[^#{pattern}]|[#{pattern}][^#{pattern}]*[#{pattern}])*$)/}
|
134
155
|
msplit(*patterns)
|
135
156
|
end
|
136
157
|
|