nrser 0.2.0.pre.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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'