bblib 0.4.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
- define_method(sev) do |msg = nil, &block|
10
- logger.send(sev) { "[#{self.class}] #{msg ? msg : block.call}" }
11
- end
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
- define_method(sev) do |msg = nil, &block|
25
- logger.send(sev) { "[#{self}] #{msg ? msg : block.call}" }
26
- end
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
@@ -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
- instance_eval(&block) if block
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 :_class argument.
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 class type #{named[init_foundation_method]}" unless klass
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 block_given?
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 block_given?
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
- if args.size > index
137
- accept = details[:options][:arg_at_accept]
138
- if accept.nil? || [accept].flatten.any? { |a| a >= args[index].class }
139
- send("#{method}=", args[index])
140
- method
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
@@ -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
@@ -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.is_a?(obj, *klasses)
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|
@@ -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