nrser 0.3.1 → 0.3.2

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: bbe345fdd17d3661a6ab8424078866fca405486c
4
- data.tar.gz: 011ea7b181ad4f101ce83a1edc9a44ea5a4728fb
3
+ metadata.gz: 1c1aebae949432774359be83991d38a1f2401011
4
+ data.tar.gz: 4c30490c2fd5fdab98b488e20a1fbc3a1b7b6273
5
5
  SHA512:
6
- metadata.gz: cef54bd329af2caee3fb30220f39a57510d4a110836f86dfa7f2af744b24df8592e09f04ca27f27cbe548bb2229532e36afa2c668e2fc431ad564c4fd07b5194
7
- data.tar.gz: 3b4f1a99cf7397b170559177ceca023b3e7b22f5bfe632a0c77f26c62c815ef488fea667b781035025ae2283258a57c391185272fbe077ecefd84a51317e02be
6
+ metadata.gz: 4d57b3ce6c67e7bc25834d9109c290a86c64a56798c47ed23b3f01ca2ce3011ea90263cf3785fa6f76a3db00d14c2c9d04595e7fa1f35006f87cbaffebad823c
7
+ data.tar.gz: fbd33e894a99dadbe17b8dbc7569607871e829c161518e20f0f6eb7bcea537856a1a3a9d85edcd7b7a004be01ec1928b098bfac4f332913332cd059b329c7904
@@ -21,12 +21,14 @@ class String
21
21
  end
22
22
 
23
23
 
24
- # See {NRSER.constantize}
25
- def constantize
26
- NRSER.constantize self
24
+ def to_const
25
+ safe_constantize
27
26
  end
28
27
 
29
- alias_method :to_const, :constantize
28
+
29
+ def to_const!
30
+ constantize
31
+ end
30
32
 
31
33
 
32
34
  # @return [Pathname]
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Project / Package
8
+ # -----------------------------------------------------------------------
9
+
10
+ require_relative './nicer_error'
11
+
12
+
13
+ # Definitions
14
+ # =======================================================================
15
+
16
+ # General error for raising when something conflicts with something else -
17
+ # it's not the type or an argument, but something about the data or
18
+ # configuration just isn't ok.
19
+ #
20
+ module NRSER
21
+ class ConflictError < ::StandardError
22
+ include NRSER::NicerError
23
+ end; end # class NRSER::ConflictError
data/lib/nrser/errors.rb CHANGED
@@ -16,15 +16,11 @@ require_relative './errors/count_error'
16
16
  require_relative './errors/argument_error'
17
17
  require_relative './errors/type_error'
18
18
  require_relative './errors/abstract_method_error'
19
+ require_relative './errors/conflict_error'
19
20
 
20
21
 
21
22
  module NRSER
22
23
 
23
- # Indicates some piece of application state is in conflict with the attempted
24
- # operation.
25
- class ConflictError < StandardError; end
26
-
27
-
28
24
  # A wrapper error around a list of other errors.
29
25
  #
30
26
  class MultipleErrors < StandardError
@@ -233,59 +233,6 @@ module NRSER
233
233
 
234
234
  end # .smart_ellipsis
235
235
 
236
-
237
- # Get the constant identified by a string.
238
- #
239
- # @pure Return value depends only on parameters.
240
- #
241
- # @example
242
- #
243
- # SomeClass == NRSER.constantize(SomeClass.name)
244
- #
245
- # Lifted from ActiveSupport.
246
- #
247
- # @param [String] camel_cased_word
248
- # The constant's camel-cased, double-colon-separated "name",
249
- # like "NRSER::Types::Array".
250
- #
251
- # @return [Object]
252
- #
253
- # @raise [NameError]
254
- # When the name is not in CamelCase or is not initialized.
255
- #
256
- def self.constantize(camel_cased_word)
257
- names = camel_cased_word.split('::')
258
-
259
- # Trigger a built-in NameError exception including the ill-formed constant in the message.
260
- Object.const_get(camel_cased_word) if names.empty?
261
-
262
- # Remove the first blank element in case of '::ClassName' notation.
263
- names.shift if names.size > 1 && names.first.empty?
264
-
265
- names.inject(Object) do |constant, name|
266
- if constant == Object
267
- constant.const_get(name)
268
- else
269
- candidate = constant.const_get(name)
270
- next candidate if constant.const_defined?(name, false)
271
- next candidate unless Object.const_defined?(name)
272
-
273
- # Go down the ancestors to check if it is owned directly. The check
274
- # stops when we reach Object or the end of ancestors tree.
275
- constant = constant.ancestors.inject do |const, ancestor|
276
- break const if ancestor == Object
277
- break ancestor if ancestor.const_defined?(name, false)
278
- const
279
- end
280
-
281
- # owner is in Object, so raise
282
- constant.const_get(name, false)
283
- end
284
- end
285
- end # .constantize
286
-
287
- singleton_class.send :alias_method, :to_const, :constantize
288
-
289
236
  # @!endgroup String Functions
290
237
 
291
238
  end # module NRSER
@@ -98,7 +98,7 @@ class NRSER::Log::Formatters::Color < ::SemanticLogger::Formatters::Color
98
98
  # ======================================================================
99
99
 
100
100
  # Instantiate a new `ColorFormatter`.
101
- def initialize ap: {multiline: true, raw: true},
101
+ def initialize ap: {multiline: true }, # raw: true},
102
102
  color_map: self.class.default_color_map,
103
103
  time_format: ::SemanticLogger::Formatters::Base::TIME_FORMAT,
104
104
  log_host: false,
@@ -16,15 +16,26 @@ module NRSER::Log::Mixin
16
16
  # Methods to mix into the including class.
17
17
  #
18
18
  module ClassMethods
19
+
20
+ protected
21
+ # ========================================================================
22
+
23
+ def create_logger
24
+ NRSER::Log[self]
25
+ end
26
+
27
+ public # end protected ***************************************************
28
+
19
29
  # Returns [SemanticLogger::Logger] class level logger
20
30
  def logger
21
- @semantic_logger ||= NRSER::Log[ self ]
31
+ @semantic_logger ||= create_logger
22
32
  end
23
33
 
24
34
  # Replace instance class level logger
25
35
  def logger= logger
26
36
  @semantic_logger = logger
27
37
  end
38
+
28
39
  end
29
40
 
30
41
 
@@ -42,9 +53,52 @@ module NRSER::Log::Mixin
42
53
  # Instance Methods
43
54
  # ========================================================================
44
55
 
45
- # Returns [SemanticLogger::Logger] instance level logger
56
+ # Gets the {NRSER::Log:Logger} for use in the instance. This will be the
57
+ # class logger from {ClassMethods#logger} unless the instance has a
58
+ # `#create_logger` method.
59
+ #
60
+ # The `#create_logger` method is expected to return a {NRSER::Log::Logger}
61
+ # instance, which will be saved in the `@semantic_logger` instance variable
62
+ # for future use.
63
+ #
64
+ # That method does not need to return a different logger for every instance
65
+ # - if you just want to have a different logger for *all* instance with a
66
+ # different level or formatter or whatever, you can save it somewhere else
67
+ # and always return that same instance.
68
+ #
69
+ # If you are dealing with frozen instances make sure to call `#logger` before
70
+ # you freeze (likely in the constructor). If you don't want to save the
71
+ # logger at all, just override this method itself.
72
+ #
73
+ # This is a departure from {SemanticLogger::Loggable} that started because
74
+ # if the instance is frozen then attempting to set `@semantic_logger` will
75
+ # raise an error.
76
+ #
77
+ # I also started ending up with classes that wanted to individually
78
+ # configure their loggers, so it seemed like we could take out two birds
79
+ # with this stone.
80
+ #
81
+ # @return [SemanticLogger::Logger]
82
+ # Instance level logger, if {ClassMethods.instance_logger?},
83
+ # otherwise the class level logger from {ClassMethods#logger}.
84
+ #
46
85
  def logger
47
- @semantic_logger ||= self.class.logger
86
+ return @semantic_logger if @semantic_logger
87
+
88
+ if respond_to?( :create_logger, true )
89
+ @semantic_logger = begin
90
+ create_logger
91
+ rescue Exception => error
92
+ self.class.logger.warn \
93
+ "Error creating instance logger",
94
+ { instance: self.inspect },
95
+ error
96
+
97
+ self.class.logger
98
+ end
99
+ else
100
+ self.class.logger
101
+ end
48
102
  end
49
103
 
50
104
 
data/lib/nrser/log.rb CHANGED
@@ -88,7 +88,7 @@ module NRSER::Log
88
88
  # Class Methods
89
89
  # ========================================================================
90
90
 
91
- def self.[] subject
91
+ def self.logger_for subject
92
92
  # key = logger_type_and_name_from subject
93
93
  #
94
94
  # if @__loggers__.key? key
@@ -100,6 +100,9 @@ module NRSER::Log
100
100
  instance = NRSER::Log::Logger.new subject
101
101
  end
102
102
 
103
+ singleton_class.send :alias_method, :[], :logger_for
104
+
105
+
103
106
 
104
107
  # @!group Utility Class Methods
105
108
  # ------------------------------------------------------------------------
@@ -560,9 +563,16 @@ module NRSER::Log
560
563
  end
561
564
 
562
565
  if will_say_hi
566
+ loggable_dest = case dest
567
+ when Hash
568
+ dest.map { |k, v| [ k, v.to_s.truncate( 42 ) ] }.to_h
569
+ else
570
+ dest.to_s.truncate 42
571
+ end
572
+
563
573
  logger.info "Hi! Logging is setup",
564
574
  level: self.level,
565
- dest: dest,
575
+ dest: loggable_dest,
566
576
  sync: sync
567
577
  end
568
578
 
@@ -62,6 +62,15 @@ module NRSER::Props::ClassMethods
62
62
  end
63
63
 
64
64
 
65
+ def prop_for name_or_alias, *props_args
66
+ sym = name_or_alias.to_sym
67
+
68
+ props( *props_args ).each_value.find { |prop|
69
+ prop.name == sym || prop.aliases.include?( sym )
70
+ }
71
+ end
72
+
73
+
65
74
  def invariants *args, &block
66
75
  metadata.invariants *args, &block
67
76
  end
@@ -89,7 +98,6 @@ module NRSER::Props::ClassMethods
89
98
  #
90
99
  def from_data data
91
100
  values = {}
92
- props = self.props
93
101
 
94
102
  unless data.respond_to? :each_pair
95
103
  raise NRSER::ArgumentError.new \
@@ -107,7 +115,7 @@ module NRSER::Props::ClassMethods
107
115
  end
108
116
 
109
117
  if prop_key &&
110
- (prop = props[prop_key])
118
+ (prop = prop_for( prop_key, only_primary: true ))
111
119
  values[prop_key] = prop.value_from_data data_value
112
120
  end
113
121
  end
@@ -115,4 +123,23 @@ module NRSER::Props::ClassMethods
115
123
  self.new values
116
124
  end # #from_data
117
125
 
126
+
127
+ # Get an instance from a source.
128
+ #
129
+ # @experimental
130
+ #
131
+ # @param [self | String | Hash] source
132
+ # @return [self]
133
+ #
134
+ def self.from source
135
+ return source if source.is_a?( self )
136
+ return from_s( source ) if source.is_a?( String )
137
+ return from_data( source ) if source.respond_to?( :each_pair )
138
+ return from_data( source.to_h ) if source.respond_to?( :to_h )
139
+
140
+ raise NRSER::ArgumentError.new \
141
+ "Unable to load #{ self } from source",
142
+ source: source
143
+ end # .from
144
+
118
145
  end # module NRSER::Props::ClassMethods
@@ -95,9 +95,10 @@ module NRSER::Props::Immutable::Hash
95
95
 
96
96
  super_values = {}
97
97
 
98
- self.class.metadata.each_primary_prop_value_from( values ) { |prop, value|
99
- super_values[prop.name] = value
100
- }
98
+ self.class.metadata.
99
+ each_primary_prop_value_from( values ) { |prop, value|
100
+ super_values[prop.name] = value
101
+ }
101
102
 
102
103
  super super_values
103
104
 
@@ -38,6 +38,12 @@ class NRSER::Props::Metadata
38
38
  VARIABLE_NAME = :@__NRSER_metadata__
39
39
 
40
40
 
41
+ # Mixins
42
+ # ========================================================================
43
+
44
+ include NRSER::Log::Mixin
45
+
46
+
41
47
  # Class Methods
42
48
  # ======================================================================
43
49
 
@@ -217,6 +223,24 @@ class NRSER::Props::Metadata
217
223
  def each_primary_prop_value_from values, &block
218
224
  primary_props = props only_primary: true
219
225
 
226
+ props_by_names_and_aliases = {}
227
+ primary_props.each_value do |prop|
228
+ [prop.name, *prop.aliases].each do |sym|
229
+ if props_by_names_and_aliases.key? sym
230
+ other_prop = props_by_names_and_aliases[sym]
231
+
232
+ prop_sym_is = sym == prop.name ? 'name' : 'alias'
233
+ other_prop_sym_is = sym == other_prop.name ? 'name' : 'alias'
234
+
235
+ raise NRSER::ConflictError.new \
236
+ "Prop", prop.to_desc, prop_sym_is, sym.inspect,
237
+ "conflicts with", other_prop.to_desc, "of", other_prop_sym_is
238
+ end
239
+
240
+ props_by_names_and_aliases[sym] = prop
241
+ end
242
+ end
243
+
220
244
  # Normalize values to a `Hash<Symbol, VALUE>` so everything can deal with
221
245
  # one form. Default values will be set here as they're resolved and made
222
246
  # available to subsequent {Prop#default} calls.
@@ -237,9 +261,9 @@ class NRSER::Props::Metadata
237
261
  # If the `name` corresponds to a primary prop set it in the values by
238
262
  # name
239
263
  #
240
- # TODO Should check that the name is not already set?
241
- #
242
- values_by_name[name] = value if primary_props.key? name
264
+ if props_by_names_and_aliases.key? name
265
+ values_by_name[ props_by_names_and_aliases[name].name ] = value
266
+ end
243
267
  }
244
268
  elsif values.respond_to? :each_index
245
269
  indexed = []
@@ -263,6 +287,14 @@ class NRSER::Props::Metadata
263
287
  END
264
288
  end
265
289
 
290
+ # Way to noisy, even for trace
291
+ # logger.trace "Ready to start loading values",
292
+ # values: values,
293
+ # values_by_name: values_by_name,
294
+ # props_by_names_and_aliases: props_by_names_and_aliases.map { |k, v|
295
+ # [ k, v.to_desc ]
296
+ # }.to_h
297
+
266
298
  # Topological sort the primary props by their default dependencies.
267
299
  #
268
300
  NRSER::Graph::TSorter.new(
@@ -18,11 +18,11 @@ require 'nrser/refinements/types'
18
18
  using NRSER::Types
19
19
 
20
20
 
21
- # Declarations
21
+ # Namespace
22
22
  # =======================================================================
23
23
 
24
- module NRSER; end
25
- module NRSER::Props; end
24
+ module NRSER
25
+ module Props
26
26
 
27
27
 
28
28
  # Definitions
@@ -33,10 +33,17 @@ module NRSER::Props; end
33
33
  #
34
34
  # Props are immutable by design.
35
35
  #
36
- class NRSER::Props::Prop
36
+ class Prop
37
+
38
+ # Mixins
39
+ # ========================================================================
37
40
 
38
41
  include NRSER::Log::Mixin
39
42
 
43
+
44
+ # Attributes
45
+ # ========================================================================
46
+
40
47
  # The class the prop was defined in.
41
48
  #
42
49
  # @return [Class]
@@ -154,8 +161,17 @@ class NRSER::Props::Prop
154
161
  @to_data = to_data
155
162
  @from_data = from_data
156
163
 
157
- @reader = t.bool?.check! reader
158
- @writer = t.bool?.check! writer
164
+ @reader, @writer = [ reader, writer ].map do |value|
165
+ t.match value,
166
+ t.bool?,
167
+ value,
168
+
169
+ t.hash_( keys: t.sym, values: t.bool ),
170
+ :freeze.to_proc,
171
+
172
+ t.hash_( keys: t.label, values: t.bool ),
173
+ ->( hash ) { hash.map { |k, v| [ k.to_sym, v ] }.to_h.freeze }
174
+ end
159
175
 
160
176
  @aliases = t.array( t.sym ).check! aliases
161
177
 
@@ -201,7 +217,7 @@ class NRSER::Props::Prop
201
217
  defaults don't make any sense.
202
218
 
203
219
  Attempted to construct prop <%= name.inspect %> for class
204
- {<%= defined_in.name %>} with:
220
+ {<%= defined_in_name %>} with:
205
221
 
206
222
  default:
207
223
 
@@ -267,7 +283,7 @@ class NRSER::Props::Prop
267
283
  <%= default.pretty_inspect %>
268
284
 
269
285
  when constructing prop <%= name.inspect %>
270
- for class <%= defined_in.name %>
286
+ for class <%= defined_in_name %>
271
287
  END
272
288
  end
273
289
 
@@ -290,12 +306,27 @@ class NRSER::Props::Prop
290
306
  # Used by the {NRSER::Props::Props::ClassMethods.prop} "macro" method to
291
307
  # determine if it should create a reader method on the propertied class.
292
308
  #
309
+ # @param [Symbol] name
310
+ # The prop name or alias in question.
311
+ #
293
312
  # @return [Boolean]
294
313
  # `true` if a reader method should be created for the prop value.
295
314
  #
296
315
  def create_reader? name
297
- # If the options was explicitly provided then return that
298
- return @reader unless @reader.nil?
316
+ t.sym.check! name
317
+
318
+ # If the options was explicitly provided then use that
319
+ case @reader
320
+ when nil
321
+ # Fall through
322
+ when Hash
323
+ return @reader[name] if @reader.key? name
324
+ # else fall through
325
+ when true, false
326
+ return @reader
327
+ end
328
+
329
+ # return @reader unless @reader.nil?
299
330
 
300
331
  # Always create readers for primary props
301
332
  return true if primary?
@@ -321,7 +352,16 @@ class NRSER::Props::Prop
321
352
  # Always `false` for the moment.
322
353
  #
323
354
  def create_writer? name
324
- return @writer unless @writer.nil?
355
+ # If the options was explicitly provided then use that
356
+ case @writer
357
+ when nil
358
+ # Fall through
359
+ when Hash
360
+ return @writer[name] if @writer.key? name
361
+ # else fall through
362
+ when true, false
363
+ return @writer
364
+ end
325
365
 
326
366
  storage_immutable = defined_in.metadata.storage.try( :immutable? )
327
367
 
@@ -331,6 +371,12 @@ class NRSER::Props::Prop
331
371
  end # #create_writer?
332
372
 
333
373
 
374
+ def defined_in_name
375
+ return defined_in.safe_name if defined_in.respond_to?( :safe_name )
376
+ defined_in.to_s
377
+ end
378
+
379
+
334
380
  # Full name with class prop was defined in.
335
381
  #
336
382
  # @example
@@ -340,7 +386,7 @@ class NRSER::Props::Prop
340
386
  # @return [String]
341
387
  #
342
388
  def full_name
343
- "#{ defined_in.name }##{ name }"
389
+ "#{ defined_in_name }##{ name }"
344
390
  end # #full_name
345
391
 
346
392
 
@@ -616,7 +662,9 @@ class NRSER::Props::Prop
616
662
  # This {Prop} does not have any custom `from_data` instructions, which
617
663
  # means we must rely on the {#type} to covert *data* to a *value*.
618
664
  #
619
- if type.has_from_data?
665
+ if data.is_a?( String ) && type.has_from_s?
666
+ type.from_s data
667
+ elsif type.has_from_data?
620
668
  type.from_data data
621
669
  else
622
670
  data
@@ -660,11 +708,23 @@ class NRSER::Props::Prop
660
708
  end # #value_from_data
661
709
 
662
710
 
711
+ def to_desc
712
+ "#{ full_name }:#{ type }"
713
+ end
714
+
715
+
663
716
  # @return [String]
664
717
  # a short string describing the instance.
665
718
  #
666
719
  def to_s
667
- "#<#{ self.class.safe_name } #{ full_name }:#{ type }>"
720
+ "#<#{ self.class.safe_name } #{ to_desc }>"
668
721
  end # #to_s
669
722
 
670
- end # class NRSER::Props::Prop
723
+ end # class Prop
724
+
725
+
726
+ # /Namespace
727
+ # =======================================================================
728
+
729
+ end # module Props
730
+ end # module NRSER
@@ -4,6 +4,10 @@ module NRSER
4
4
  def t
5
5
  NRSER::Types
6
6
  end
7
+
8
+ def to_type
9
+ NRSER::Types.make self
10
+ end
7
11
  end
8
12
  end # module Types
9
13
  end # module NRSER
@@ -151,6 +151,9 @@ module NRSER::Types
151
151
  #
152
152
  # @return [NRSER::Types::Type]
153
153
  #
154
+ # @todo
155
+ # Make `list` into it's own looser interface for "array-like" object API.
156
+ #
154
157
  def_factory(
155
158
  :array,
156
159
  aliases: [:list],
@@ -191,9 +191,12 @@ module NRSER::Types
191
191
  # @return [NRSER::Types::Type]
192
192
  # Newly constructed hash type from `args`.
193
193
  #
194
+ # @todo
195
+ # Make `map` into it's own looser interface for "hash-like" object API.
196
+ #
194
197
  def_factory(
195
198
  :hash_type,
196
- aliases: [ :dict, :hash_ ]
199
+ aliases: [ :dict, :hash_, :map ]
197
200
  ) do |**kwds|
198
201
  if kwds.key?( :keys ) || kwds.key?( :values )
199
202
  HashOfType.new **kwds
@@ -85,6 +85,21 @@ module NRSER::Types
85
85
  end
86
86
 
87
87
 
88
+ # A relative path.
89
+ #
90
+ # @todo
91
+ # Quick addition, not sure if it's totally right with regard to tilde
92
+ # paths and such.
93
+ #
94
+ def_factory :rel_path do |name: 'RelPath', **options|
95
+ intersection \
96
+ path,
97
+ ~abs_path,
98
+ name: name,
99
+ **options
100
+ end
101
+
102
+
88
103
  # A {NRSER::Types.path} that is a directory.
89
104
  #
90
105
  # @param [Hash] **options
data/lib/nrser/version.rb CHANGED
@@ -18,7 +18,7 @@ module NRSER
18
18
  #
19
19
  # @return [String]
20
20
  #
21
- VERSION = '0.3.1'
21
+ VERSION = '0.3.2'
22
22
 
23
23
 
24
24
  module Version
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nrser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - nrser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-03 00:00:00.000000000 Z
11
+ date: 2018-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hamster
@@ -256,6 +256,7 @@ files:
256
256
  - lib/nrser/errors/abstract_method_error.rb
257
257
  - lib/nrser/errors/argument_error.rb
258
258
  - lib/nrser/errors/attr_error.rb
259
+ - lib/nrser/errors/conflict_error.rb
259
260
  - lib/nrser/errors/count_error.rb
260
261
  - lib/nrser/errors/nicer_error.rb
261
262
  - lib/nrser/errors/type_error.rb