nrser 0.2.0.pre.3 → 0.2.0

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nrser/ext/enumerable.rb +12 -3
  3. data/lib/nrser/ext/module.rb +62 -0
  4. data/lib/nrser/ext.rb +1 -0
  5. data/lib/nrser/functions/binding.rb +33 -0
  6. data/lib/nrser/functions/enumerable/associate.rb +103 -0
  7. data/lib/nrser/functions/enumerable/map_keys.rb +0 -0
  8. data/lib/nrser/functions/enumerable/map_values.rb +94 -0
  9. data/lib/nrser/functions/enumerable.rb +2 -87
  10. data/lib/nrser/functions/module/methods.rb +206 -0
  11. data/lib/nrser/functions/module/source_locations.rb +213 -0
  12. data/lib/nrser/functions/module.rb +2 -0
  13. data/lib/nrser/functions.rb +1 -0
  14. data/lib/nrser/logging/appender/sync.rb +148 -0
  15. data/lib/nrser/logging/appender.rb +3 -0
  16. data/lib/nrser/logging/formatters/color.rb +165 -0
  17. data/lib/nrser/logging/formatters.rb +1 -0
  18. data/lib/nrser/logging.rb +353 -0
  19. data/lib/nrser/refinements/module.rb +5 -0
  20. data/lib/nrser/refinements.rb +1 -0
  21. data/lib/nrser/rspex/described.rb +99 -0
  22. data/lib/nrser/rspex/example_group/describe_called_with.rb +2 -2
  23. data/lib/nrser/rspex/example_group/describe_class.rb +31 -0
  24. data/lib/nrser/rspex/example_group/describe_instance.rb +1 -1
  25. data/lib/nrser/rspex/example_group/describe_method.rb +40 -0
  26. data/lib/nrser/rspex/example_group.rb +2 -34
  27. data/lib/nrser/rspex/format.rb +19 -6
  28. data/lib/nrser/rspex.rb +1 -1
  29. data/lib/nrser/types/numbers.rb +16 -16
  30. data/lib/nrser/version.rb +1 -1
  31. data/lib/nrser.rb +2 -5
  32. data/spec/design/mapping_spec.rb +42 -0
  33. data/spec/lib/nrser/mean_streak/identity_instance_spec.rb +7 -5
  34. data/spec/spec_helper.rb +23 -105
  35. data/spec/support/shared/types.rb +92 -0
  36. data/spec/support/shared.rb +1 -0
  37. metadata +27 -24
  38. data/lib/nrser/labs/unicode_math.rb +0 -48
  39. data/lib/nrser/labs/where.rb +0 -50
  40. data/lib/nrser/logger.rb +0 -457
  41. data/spec/lib/nrser/logger/dest_spec.rb +0 -15
  42. data/spec/lib/nrser/logger/die_spec.rb +0 -41
  43. data/spec/lib/nrser/logger/install_spec.rb +0 -98
  44. data/spec/lib/nrser/logger/level_int_spec.rb +0 -22
  45. data/spec/lib/nrser/logger/level_name_spec.rb +0 -23
  46. data/spec/lib/nrser/logger/level_sym_spec.rb +0 -22
  47. data/spec/lib/nrser/logger/send_log_spec.rb +0 -63
  48. data/spec/lib/nrser/logger/use_spec.rb +0 -16
@@ -0,0 +1,213 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ using NRSER
5
+
6
+ # Definitions
7
+ # =======================================================================
8
+
9
+ module NRSER
10
+
11
+ # @!group Module Functions
12
+ # ==========================================================================
13
+
14
+
15
+ # @todo Document map_to_source_locations method.
16
+ #
17
+ # @param [type] arg_name
18
+ # @todo Add name param description.
19
+ #
20
+ # @return [Hash<#source_location, Array<(String?, Fixnum?)>?>]
21
+ # Map of objects
22
+ #
23
+ def self.map_to_source_locations methods
24
+ methods.assoc_to &:source_location
25
+ end # .map_to_source_locations
26
+
27
+
28
+ # Map class method {Methods} objects to the `#source_location` for `mod`
29
+ # class methods.
30
+ #
31
+ # @see https://ruby-doc.org/core/Method.html#method-i-source_location
32
+ #
33
+ # @param (see NRSER.class_method_objects_for)
34
+ #
35
+ # @return [Hash<Symbol, Array<(String?, Fixnum?)>?>]
36
+ # Hash mapping method name {Symbol}s to results of calling their
37
+ # {Method#source_location}, which seems to be able to be:
38
+ #
39
+ # 1. `Array<(String, Fixnum)>` - two-entry array of file path,
40
+ # line number.
41
+ #
42
+ # 2. `nil` - if this method was not defined in Ruby (C extension, etc.)
43
+ #
44
+ # 3. `Array<(nil, nil)>` - Not listed as a possibility in the docs, but
45
+ # I swear I've seen it, so watch out.
46
+ #
47
+ def self.class_method_source_locations_for *args
48
+ map_to_source_locations class_method_objects_for( *args )
49
+ end # .class_method_source_locations
50
+
51
+
52
+ # Map *own* (not inherited) class method {Methods} objects to the
53
+ # `#source_location` for `mod` class methods.
54
+ #
55
+ # @see https://ruby-doc.org/core/Method.html#method-i-source_location
56
+ #
57
+ # @param (see .own_class_method_objects_for)
58
+ # @return (see .class_method_objects_for)
59
+ #
60
+ def self.own_class_method_source_locations_for *args
61
+ map_to_source_locations own_class_method_objects_for( *args )
62
+ end # .own_class_method_source_locations_for
63
+
64
+
65
+ # Map instance method {Methods} objects to the `#source_location` for `mod`
66
+ # class methods.
67
+ #
68
+ # @see https://ruby-doc.org/core/Method.html#method-i-source_location
69
+ #
70
+ # @param (see .instance_method_objects_for)
71
+ # @return (see .class_method_source_locations_for)
72
+ #
73
+ def self.instance_method_source_locations_for *args
74
+ map_to_source_locations instance_method_objects_for( *args )
75
+ end # .class_method_source_locations
76
+
77
+
78
+ # Map *own* (not inherited) class method {Methods} objects to the
79
+ # `#source_location` for `mod` class methods.
80
+ #
81
+ # @see https://ruby-doc.org/core/Method.html#method-i-source_location
82
+ #
83
+ # @param (see NRSER.own_class_method_objects_for)
84
+ # @return (see NRSER.class_method_objects_for)
85
+ #
86
+ def self.own_instance_method_source_locations_for *args
87
+ map_to_source_locations own_instance_methods_objects_for( *args )
88
+ end # .own_class_method_source_locations_for
89
+
90
+
91
+ # @todo Document method_source_locations_for method.
92
+ #
93
+ # @param [type] mod
94
+ # @todo Add name param description.
95
+ #
96
+ # @return [return_type]
97
+ # @todo Document return value.
98
+ #
99
+ def self.method_source_locations_for mod,
100
+ include_super = false,
101
+ sort: true
102
+ class_method_source_locations_for(
103
+ mod,
104
+ include_super,
105
+ sort: sort,
106
+ ).
107
+ # map_keys { |name| ".#{ name }" }.
108
+ merge instance_method_source_locations_for(
109
+ mod,
110
+ include_super,
111
+ sort: sort,
112
+ # Think it makes sense to *always* include `#initialize` since at this
113
+ # point we're interested in where *all* the methods are, and that
114
+ # should def be included.
115
+ include_initialize: true,
116
+ ) # .map_keys { |name| "##{ name }" }
117
+ end # .method_source_locations_for
118
+
119
+
120
+ def self.own_method_source_locations_for mod,
121
+ sort: true
122
+ method_source_locations_for \
123
+ mod,
124
+ false,
125
+ sort: sort
126
+ end # .method_source_locations_for
127
+
128
+
129
+
130
+ # @todo Document module_source_locations method.
131
+ #
132
+ # @param [type] arg_name
133
+ # @todo Add name param description.
134
+ #
135
+ # @return [return_type]
136
+ # @todo Document return value.
137
+ #
138
+ def self.module_source_locations mod
139
+ # Get all the src locs for `mod`'s methods
140
+ own_method_source_locations_for( mod ).values.
141
+ # Filter out any that don't have full src loc info
142
+ reject { |src_loc| src_loc.nil? || src_loc.any?( &:nil? ) }
143
+ end # .module_source_locations
144
+
145
+
146
+
147
+ # Get a reasonable file and line for the class.
148
+ #
149
+ # @param [Class] cls
150
+ # Class to try and locate.
151
+ #
152
+ # @return [Array<(String, Fixnum)>]
153
+ # Two entry array; first entry is the string file path, second is the
154
+ # line number.
155
+ #
156
+ def self.module_source_location mod
157
+ # If any files end with the "canonical path" then use that. It's a path
158
+ # suffix
159
+ #
160
+ # "my_mod/sub_mod/some_class.rb"
161
+ #
162
+ # the for a class
163
+ #
164
+ # MyMod::SubMod::SomeClass
165
+ #
166
+ canonical_path_end = \
167
+ ActiveSupport::Inflector.underscore( mod.name ) + '.rb'
168
+
169
+ # Get all the src locs for `mod`'s methods
170
+ src_locs = module_source_locations mod
171
+
172
+ # Find first line in canonical path (if any)
173
+ canonical_path_src_loc = src_locs.
174
+ find_all { |(path, line)| path.end_with? canonical_path_end }.
175
+ min_by { |(path, line)| line }
176
+
177
+ # If we found one, we're done!
178
+ return canonical_path_src_loc if canonical_path_src_loc
179
+
180
+ raise "HERE"
181
+
182
+ klass.
183
+ # Get an array of all instance methods, excluding inherited ones
184
+ # (the `false` arg)
185
+ instance_methods( false ).
186
+ # Add `#initialize` since it isn't in `#instance_methods` for some
187
+ # reason
188
+ <<( :initialize ).
189
+ # Map those to their {UnboundMethod} objects
190
+ map { |sym| klass.instance_method sym }.
191
+ # Toss any `nil` values (TODO how/why?)
192
+ compact.
193
+ # Get the source locations
194
+ map( &:source_location ).
195
+ # Get rid of `[nil, nil]` results, which seems to come from C exts?
196
+ reject { |(path, line)| path.nil? || line.nil? }.
197
+ # Get the first line in the shortest path
198
+ min_by { |(path, line)| [path.length, line] }
199
+
200
+ # Another approach I thought of... (untested)
201
+ #
202
+ # Get the path
203
+ # # Get frequency of the paths
204
+ # count_by { |(path, line)| path }.
205
+ # # Get the one with the most occurrences
206
+ # max_by { |path, count| count }.
207
+ # # Get just the path (not the count)
208
+ # first
209
+ end # .module_source_location
210
+
211
+ # @!endgroup Module Functions
212
+
213
+ end # module NRSER
@@ -0,0 +1,2 @@
1
+ require_relative './module/methods'
2
+ require_relative './module/source_locations'
@@ -17,3 +17,4 @@ require_relative './functions/merge_by'
17
17
  require_relative './functions/tree'
18
18
  require_relative './functions/path'
19
19
  require_relative './functions/git'
20
+ require_relative './functions/module'
@@ -0,0 +1,148 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+
5
+ # Definitions
6
+ # =======================================================================
7
+
8
+ # Replacement for {SemanticLogger::Appender::Async} that implements the
9
+ # same interface but just logs synchronously in the current thread.
10
+ #
11
+ # Basically just implements the {SemanticLogger::Appender::Async} API,
12
+ # returning mostly with fake / nonsense values, but it seems to work, and
13
+ # just let's writes go strait through to the {#appender} (which is actually
14
+ # a {SemanticLogger::Processor}).
15
+ #
16
+ # Useful for CLI applications where you want to see output in sync with
17
+ # operations.
18
+ #
19
+ class NRSER::Logging::Appender::Sync
20
+
21
+ # Mixins
22
+ # ============================================================================
23
+
24
+ # Macros for forwarding to {#appender}
25
+ extend Forwardable
26
+
27
+
28
+ #
29
+ # ============================================================================
30
+
31
+ # The appender we forward to, which is a {SemanticLogger::Processor}
32
+ # in practice, since it wouldn't make any sense to wrap a regular
33
+ # appender in a Sync.
34
+ #
35
+ # @return [SemanticLogger::Processor]
36
+ #
37
+ attr_accessor :appender
38
+
39
+ # Forward methods that can be called directly
40
+ def_delegator :@appender, :name
41
+ def_delegator :@appender, :should_log?
42
+ def_delegator :@appender, :filter
43
+ def_delegator :@appender, :host
44
+ def_delegator :@appender, :application
45
+ def_delegator :@appender, :level
46
+ def_delegator :@appender, :level=
47
+ def_delegator :@appender, :logger
48
+
49
+ # Added for sync
50
+ def_delegator :@appender, :log
51
+ def_delegator :@appender, :on_log
52
+ def_delegator :@appender, :flush
53
+ def_delegator :@appender, :close
54
+
55
+
56
+ # A fake {Queue} that just implements a {.size} method that returns `0`.
57
+ #
58
+ # Sync appender doesn't need a queue, but Semantic Logger expects one, so
59
+ # telling it the length is always zero seems to make sense.
60
+ #
61
+ class FakeQueue
62
+ # @return [0]
63
+ # Fake queue is always empty.
64
+ def self.size
65
+ 0
66
+ end
67
+ end # class FakeQueue
68
+
69
+
70
+ # Construct a sync appender.
71
+ #
72
+ # @param [SemanticLogger]
73
+ # name: [String]
74
+ # Name to use for the log thread and the log name when logging any errors from this appender.
75
+ #
76
+ # lag_threshold_s [Float]
77
+ # Log a warning when a log message has been on the queue for longer than this period in seconds.
78
+ # Default: 30
79
+ #
80
+ # lag_check_interval: [Integer]
81
+ # Number of messages to process before checking for slow logging.
82
+ # Default: 1,000
83
+ def initialize appender:, name: appender.class.name
84
+ @appender = appender
85
+ end
86
+
87
+ # Needs to be there to support {SemanticLogger::Processor.queue_size},
88
+ # which gets the queue and returns it's size (which will always be zero
89
+ # for us).
90
+ #
91
+ # We return {FakeQueue}, which only implements a `size` method that
92
+ # returns zero.
93
+ #
94
+ # @return [#size]
95
+ #
96
+ def queue; FakeQueue; end
97
+
98
+
99
+ # @return [-1]
100
+ # Nonsense value meant to indicate there is no lag check interval.
101
+ def lag_check_interval; -1; end
102
+
103
+
104
+ # @raise [NotImplementedError]
105
+ # Sync appender doesn't support setting lag check interval.
106
+ def lag_check_interval= value
107
+ raise NotImplementedError,
108
+ "Can't set `lag_check_interval` on Sync appender"
109
+ end
110
+
111
+
112
+ # @return [-1]
113
+ # Nonsense value meant to indicate there is no lag threshold.
114
+ def lag_threshold_s; -1; end
115
+
116
+
117
+ # @raise [NotImplementedError]
118
+ # Sync appender doesn't support setting log threshold.
119
+ def lag_threshold_s= value
120
+ raise NotImplementedError,
121
+ "Can't set `lag_threshold_s` on Sync appender"
122
+ end
123
+
124
+
125
+ # @return [false]
126
+ # Sync appender is of course not size-capped.
127
+ def capped?; false; end
128
+
129
+
130
+ # The {SemanticLogger::Appender::Async} worker thread is exposed via
131
+ # this method, which creates it if it doesn't exist and returns it, but
132
+ # it doesn't seem like the returned value is ever used; the method
133
+ # call is just invoked to start the thread.
134
+ #
135
+ # Hence it seems to make most sense to just return `nil` since we don't
136
+ # have a thread, and figure out what to do if that causes errors (so far
137
+ # it seems fine).
138
+ #
139
+ # @return [nil]
140
+ #
141
+ def thread; end
142
+
143
+
144
+ # @return [true]
145
+ # Sync appender is always active
146
+ def active?; true; end
147
+
148
+ end # class NRSER::Logging::Appender::Sync
@@ -0,0 +1,3 @@
1
+ module NRSER::Logging::Appender; end
2
+
3
+ require_relative './appender/sync'
@@ -0,0 +1,165 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Stdlib
5
+ # -----------------------------------------------------------------------
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+ require 'awesome_print'
10
+ require 'semantic_logger'
11
+
12
+ # Project / Package
13
+ # -----------------------------------------------------------------------
14
+
15
+
16
+ # Refinements
17
+ # =======================================================================
18
+
19
+
20
+ # Declarations
21
+ # =======================================================================
22
+
23
+ module NRSER::Logging; end
24
+ module NRSER::Logging::Formatters; end
25
+
26
+
27
+ # Definitions
28
+ # =======================================================================
29
+
30
+ class NRSER::Logging::Formatters::Color < ::SemanticLogger::Formatters::Color
31
+
32
+ # Constants
33
+ # ======================================================================
34
+
35
+ # ANSI escape sequence to start "Dark Gray" color.
36
+ #
37
+ # @return [String]
38
+ #
39
+ ANSI_ESC_DARK_GRAY = "\e[1;30m"
40
+
41
+
42
+ # Class Methods
43
+ # ======================================================================
44
+
45
+ # @todo Document default_color_map method.
46
+ #
47
+ # @param [type] arg_name
48
+ # @todo Add name param description.
49
+ #
50
+ # @return [SemanticLogger::Formatters::Color::ColorMap]
51
+ #
52
+ def self.default_color_map
53
+ SemanticLogger::Formatters::Color::ColorMap.new(
54
+ debug: SemanticLogger::AnsiColors::MAGENTA,
55
+ trace: ANSI_ESC_DARK_GRAY,
56
+ )
57
+ end # .default_color_map
58
+
59
+
60
+ # Attributes
61
+ # ======================================================================
62
+
63
+
64
+ # Constructor
65
+ # ======================================================================
66
+
67
+ # Instantiate a new `ColorFormatter`.
68
+ def initialize ap: {multiline: true},
69
+ color_map: self.class.default_color_map,
70
+ time_format: ::SemanticLogger::Formatters::Base::TIME_FORMAT,
71
+ log_host: false,
72
+ log_application: false
73
+ super ap: ap,
74
+ color_map: color_map,
75
+ time_format: time_format,
76
+ log_host: log_host,
77
+ log_application: log_application
78
+ end # #initialize
79
+
80
+
81
+ # Instance Methods
82
+ # ======================================================================
83
+
84
+
85
+ # Upcase the log level.
86
+ #
87
+ # @return [String]
88
+ #
89
+ def level
90
+ "#{ color }#{ log.level.upcase }#{ color_map.clear }"
91
+ end
92
+
93
+
94
+ # Create the log entry text. Overridden to customize appearance -
95
+ # generally reduce amount of info and put payload on it's own line.
96
+ #
97
+ # We need to replace *two* super functions, the first being
98
+ # [SemanticLogger::Formatters::Color#call][]:
99
+ #
100
+ # def call(log, logger)
101
+ # self.color = color_map[log.level]
102
+ # super(log, logger)
103
+ # end
104
+ #
105
+ # [SemanticLogger::Formatters::Color#call]: https://github.com/rocketjob/semantic_logger/blob/v4.2.0/lib/semantic_logger/formatters/color.rb#L98
106
+ #
107
+ # which doesn't do all too much, and the next being it's super-method,
108
+ # [SemanticLogger::Formatters::Default#call][]:
109
+ #
110
+ # # Default text log format
111
+ # # Generates logs of the form:
112
+ # # 2011-07-19 14:36:15.660235 D [1149:ScriptThreadProcess] Rails -- Hello World
113
+ # def call(log, logger)
114
+ # self.log = log
115
+ # self.logger = logger
116
+ #
117
+ # [time, level, process_info, tags, named_tags, duration, name, message, payload, exception].compact.join(' ')
118
+ # end
119
+ #
120
+ # [SemanticLogger::Formatters::Default#call]: https://github.com/rocketjob/semantic_logger/blob/v4.2.0/lib/semantic_logger/formatters/default.rb#L64
121
+ #
122
+ # which does most the real assembly.
123
+ #
124
+ # @param [SemanticLogger::Log] log
125
+ # The log entry to format.
126
+ #
127
+ # See [SemanticLogger::Log](https://github.com/rocketjob/semantic_logger/blob/v4.2.0/lib/semantic_logger/log.rb)
128
+ #
129
+ # @param [SemanticLogger::Logger] logger
130
+ # The logger doing the logging (pretty sure, haven't checked).
131
+ #
132
+ # See [SemanticLogger::Logger](https://github.com/rocketjob/semantic_logger/blob/v4.2.0/lib/semantic_logger/logger.rb)
133
+ #
134
+ # @return [return_type]
135
+ # @todo Document return value.
136
+ #
137
+ def call log, logger
138
+ # SemanticLogger::Formatters::Color code
139
+ self.color = color_map[log.level]
140
+
141
+ # SemanticLogger::Formatters::Default code
142
+ self.log = log
143
+ self.logger = logger
144
+
145
+ [
146
+ time, # annoyingly noisy and don't really need for local CLI app
147
+ level,
148
+ process_info,
149
+ tags,
150
+ named_tags,
151
+ duration,
152
+ name,
153
+ ].compact.join( ' ' ) +
154
+ "\n" +
155
+ [
156
+ message,
157
+ payload,
158
+ exception,
159
+ ].compact.join(' ') +
160
+ "\n" # I like extra newline to space shit out
161
+
162
+ end # #call
163
+
164
+
165
+ end # class Color
@@ -0,0 +1 @@
1
+ require_relative './formatters/color'