nrser 0.2.2 → 0.3.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.
- checksums.yaml +4 -4
- data/lib/nrser/char/alpha_numeric_sub.rb +1 -2
- data/lib/nrser/char.rb +0 -6
- data/lib/nrser/core_ext/array.rb +120 -0
- data/lib/nrser/core_ext/binding.rb +44 -0
- data/lib/nrser/{functions → core_ext}/enumerable/find_map.rb +18 -15
- data/lib/nrser/{ext → core_ext}/enumerable.rb +10 -24
- data/lib/nrser/core_ext/exception.rb +30 -0
- data/lib/nrser/core_ext/hash/extract_values_at.rb +49 -0
- data/lib/nrser/core_ext/hash/transform_values_with_keys.rb +24 -0
- data/lib/nrser/core_ext/hash.rb +50 -0
- data/lib/nrser/core_ext/module/method_objects.rb +96 -0
- data/lib/nrser/core_ext/module/names.rb +69 -0
- data/lib/nrser/core_ext/module/source_locations.rb +214 -0
- data/lib/nrser/core_ext/module.rb +2 -0
- data/lib/nrser/core_ext/object/lazy_var.rb +31 -0
- data/lib/nrser/core_ext/object.rb +46 -0
- data/lib/nrser/core_ext/open_struct.rb +6 -0
- data/lib/nrser/{ext → core_ext}/pathname.rb +8 -5
- data/lib/nrser/{ext → core_ext}/string.rb +6 -12
- data/lib/nrser/core_ext/symbol.rb +13 -0
- data/lib/nrser/core_ext/time.rb +46 -0
- data/lib/nrser/core_ext.rb +13 -0
- data/lib/nrser/errors/abstract_method_error.rb +150 -0
- data/lib/nrser/errors/argument_error.rb +42 -0
- data/lib/nrser/errors/nicer_error.rb +298 -72
- data/lib/nrser/errors/type_error.rb +46 -0
- data/lib/nrser/errors.rb +4 -53
- data/lib/nrser/ext/tree.rb +3 -0
- data/lib/nrser/functions/enumerable/associate.rb +6 -9
- data/lib/nrser/functions/enumerable/include_slice.rb +2 -3
- data/lib/nrser/functions/enumerable.rb +1 -3
- data/lib/nrser/functions/exception.rb +1 -1
- data/lib/nrser/functions/hash.rb +0 -6
- data/lib/nrser/functions/merge_by.rb +2 -2
- data/lib/nrser/functions/module/method_objects.rb +77 -0
- data/lib/nrser/functions/module.rb +1 -2
- data/lib/nrser/functions/open_struct.rb +25 -35
- data/lib/nrser/functions/proc.rb +1 -6
- data/lib/nrser/functions/string/looks_like.rb +32 -1
- data/lib/nrser/functions/string.rb +1 -40
- data/lib/nrser/functions/text/lines.rb +2 -1
- data/lib/nrser/functions.rb +0 -1
- data/lib/nrser/graph/tsorter.rb +41 -0
- data/lib/nrser/labs/core_ext/binding.rb +37 -0
- data/lib/nrser/labs/stash.rb +372 -0
- data/lib/nrser/{logging → log}/appender/sync.rb +3 -3
- data/lib/nrser/log/appender.rb +3 -0
- data/lib/nrser/{logging → log}/formatters/color.rb +47 -20
- data/lib/nrser/log/formatters/mixin.rb +270 -0
- data/lib/nrser/{logging → log}/formatters.rb +0 -0
- data/lib/nrser/log/logger.rb +229 -0
- data/lib/nrser/log/mixin.rb +56 -0
- data/lib/nrser/log.rb +723 -0
- data/lib/nrser/message.rb +24 -3
- data/lib/nrser/meta/source/location.rb +158 -0
- data/lib/nrser/meta.rb +1 -1
- data/lib/nrser/props/class_methods.rb +118 -0
- data/lib/nrser/props/immutable/hash.rb +111 -0
- data/lib/nrser/props/immutable/hash_variable.rb +82 -0
- data/lib/nrser/props/immutable/instance_variables.rb +48 -0
- data/lib/nrser/props/immutable/vector.rb +107 -0
- data/lib/nrser/props/instance_methods.rb +184 -0
- data/lib/nrser/props/metadata.rb +359 -0
- data/lib/nrser/props/mutable/instance_variables.rb +60 -0
- data/lib/nrser/props/mutable/stash.rb +199 -0
- data/lib/nrser/{meta/props → props}/prop.rb +217 -112
- data/lib/nrser/props/storage/instance_variable.rb +85 -0
- data/lib/nrser/props/storage/instance_variables.rb +67 -0
- data/lib/nrser/props/storage/key.rb +88 -0
- data/lib/nrser/props.rb +9 -0
- data/lib/nrser/refinements/sugar.rb +41 -0
- data/lib/nrser/refinements/types.rb +2 -2
- data/lib/nrser/refinements.rb +14 -16
- data/lib/nrser/rspex/example_group/describe_attribute.rb +24 -0
- data/lib/nrser/rspex/example_group/describe_called_with.rb +1 -6
- data/lib/nrser/rspex/example_group/{describe_use_case.rb → describe_case.rb} +6 -3
- data/lib/nrser/rspex/example_group/describe_class.rb +1 -0
- data/lib/nrser/rspex/example_group/describe_group.rb +29 -0
- data/lib/nrser/rspex/example_group/describe_instance_method.rb +2 -2
- data/lib/nrser/rspex/example_group/describe_message.rb +35 -0
- data/lib/nrser/rspex/example_group/describe_method.rb +23 -2
- data/lib/nrser/rspex/example_group/describe_module.rb +19 -0
- data/lib/nrser/rspex/example_group/describe_response_to.rb +32 -0
- data/lib/nrser/rspex/example_group/describe_section.rb +38 -0
- data/lib/nrser/rspex/example_group/describe_sent_to.rb +52 -0
- data/lib/nrser/rspex/example_group/describe_source_file.rb +49 -0
- data/lib/nrser/rspex/example_group/describe_spec_file.rb +41 -108
- data/lib/nrser/rspex/example_group/describe_when.rb +14 -7
- data/lib/nrser/rspex/example_group/describe_x.rb +39 -12
- data/lib/nrser/rspex/example_group/overrides.rb +66 -0
- data/lib/nrser/rspex/example_group.rb +20 -252
- data/lib/nrser/rspex/format.rb +83 -17
- data/lib/nrser/rspex.rb +4 -34
- data/lib/nrser/sugar/method_missing_forwarder.rb +50 -0
- data/lib/nrser/{env → sys/env}/path.rb +1 -2
- data/lib/nrser/{env.rb → sys/env.rb} +2 -1
- data/lib/nrser/sys.rb +5 -0
- data/lib/nrser/types/any.rb +36 -7
- data/lib/nrser/types/{array.rb → arrays.rb} +32 -81
- data/lib/nrser/types/attrs.rb +68 -15
- data/lib/nrser/types/booleans.rb +95 -34
- data/lib/nrser/types/bounded.rb +12 -10
- data/lib/nrser/types/combinators.rb +74 -37
- data/lib/nrser/types/errors/check_error.rb +86 -0
- data/lib/nrser/types/errors/from_string_error.rb +82 -0
- data/lib/nrser/types/factory.rb +91 -0
- data/lib/nrser/types/hashes.rb +171 -26
- data/lib/nrser/types/in.rb +25 -12
- data/lib/nrser/types/is.rb +50 -18
- data/lib/nrser/types/is_a.rb +52 -33
- data/lib/nrser/types/labels.rb +6 -33
- data/lib/nrser/types/maybe.rb +12 -4
- data/lib/nrser/types/nil.rb +24 -4
- data/lib/nrser/types/not.rb +6 -16
- data/lib/nrser/types/numbers.rb +94 -57
- data/lib/nrser/types/pairs.rb +57 -57
- data/lib/nrser/types/paths.rb +112 -133
- data/lib/nrser/types/responds.rb +64 -74
- data/lib/nrser/types/shape.rb +29 -24
- data/lib/nrser/types/strings.rb +25 -17
- data/lib/nrser/types/symbols.rb +19 -17
- data/lib/nrser/types/trees.rb +18 -70
- data/lib/nrser/types/tuples.rb +36 -40
- data/lib/nrser/types/type.rb +342 -91
- data/lib/nrser/types/when.rb +40 -18
- data/lib/nrser/types/where.rb +94 -9
- data/lib/nrser/types.rb +72 -63
- data/lib/nrser/version.rb +1 -1
- data/lib/nrser.rb +18 -18
- data/spec/lib/nrser/{functions/binding/template_spec.rb → core_ext/binding/erb_spec.rb} +5 -5
- data/spec/lib/nrser/{functions → core_ext}/enumerable/find_map_spec.rb +8 -6
- data/spec/lib/nrser/{refinements → core_ext}/hash_spec.rb +9 -22
- data/spec/lib/nrser/errors/abstract_method_error_spec.rb +12 -5
- data/spec/lib/nrser/functions/enumerable/{to_h_by_spec.rb → associate_spec.rb} +1 -1
- data/spec/lib/nrser/functions/merge_by_spec.rb +1 -1
- data/spec/lib/nrser/functions/tree/each_branch_spec.rb +3 -3
- data/spec/lib/nrser/functions/tree/transform_spec.rb +14 -15
- data/spec/lib/nrser/gem_ext/hamster/json_spec.rb +4 -0
- data/spec/lib/nrser/meta/source/location_spec.rb +86 -0
- data/spec/lib/nrser/props/immutable/hash_spec.rb +297 -0
- data/spec/lib/nrser/props/immutable/vector_spec.rb +296 -0
- data/spec/lib/nrser/{meta/props_spec.rb → props/original_props_spec.rb} +11 -16
- data/spec/lib/nrser/{meta/props → props}/to_and_from_data_spec.rb +10 -8
- data/spec/lib/nrser/refinements/array_spec.rb +2 -15
- data/spec/lib/nrser/refinements/erb_spec.rb +5 -7
- data/spec/lib/nrser/refinements/set_spec.rb +2 -15
- data/spec/lib/nrser/{env → sys/env}/path/insert_spec.rb +4 -2
- data/spec/lib/nrser/{env → sys/env}/path_spec.rb +4 -2
- data/spec/lib/nrser/types/array_spec.rb +8 -8
- data/spec/lib/nrser/types/paths_spec.rb +15 -18
- data/spec/spec_helper.rb +4 -0
- metadata +109 -69
- data/lib/nrser/ext/binding.rb +0 -36
- data/lib/nrser/ext/module.rb +0 -62
- data/lib/nrser/ext.rb +0 -8
- data/lib/nrser/functions/binding.rb +0 -76
- data/lib/nrser/functions/enumerable/map_keys.rb +0 -0
- data/lib/nrser/functions/enumerable/map_values.rb +0 -94
- data/lib/nrser/functions/hash/deep_merge.rb +0 -57
- data/lib/nrser/functions/hash/except_keys.rb +0 -44
- data/lib/nrser/functions/hash/slice_keys.rb +0 -43
- data/lib/nrser/functions/hash/stringify_keys.rb +0 -55
- data/lib/nrser/functions/hash/symbolize_keys.rb +0 -57
- data/lib/nrser/functions/hash/transform_keys.rb +0 -140
- data/lib/nrser/functions/module/methods.rb +0 -206
- data/lib/nrser/functions/module/source_locations.rb +0 -213
- data/lib/nrser/logging/appender.rb +0 -3
- data/lib/nrser/logging.rb +0 -353
- data/lib/nrser/meta/props/base.rb +0 -31
- data/lib/nrser/meta/props.rb +0 -357
- data/lib/nrser/refinements/array.rb +0 -133
- data/lib/nrser/refinements/binding.rb +0 -6
- data/lib/nrser/refinements/enumerator.rb +0 -5
- data/lib/nrser/refinements/exception.rb +0 -35
- data/lib/nrser/refinements/hash.rb +0 -150
- data/lib/nrser/refinements/module.rb +0 -5
- data/lib/nrser/refinements/object.rb +0 -42
- data/lib/nrser/refinements/open_struct.rb +0 -28
- data/lib/nrser/refinements/pathname.rb +0 -5
- data/lib/nrser/refinements/set.rb +0 -5
- data/lib/nrser/refinements/string.rb +0 -5
- data/lib/nrser/refinements/symbol.rb +0 -20
- data/lib/nrser/rspex/described.rb +0 -99
- data/spec/design/mapping_spec.rb +0 -42
- data/spec/lib/nrser/functions/hash_spec.rb +0 -41
- data/spec/lib/nrser/functions/string/truncate_spec.rb +0 -11
- data/spec/lib/nrser/refinements/truncate_spec.rb +0 -10
@@ -0,0 +1,184 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Requirements
|
5
|
+
# =======================================================================
|
6
|
+
|
7
|
+
# Stdlib
|
8
|
+
# -----------------------------------------------------------------------
|
9
|
+
|
10
|
+
# Deps
|
11
|
+
# -----------------------------------------------------------------------
|
12
|
+
|
13
|
+
# Project / Package
|
14
|
+
# -----------------------------------------------------------------------
|
15
|
+
|
16
|
+
|
17
|
+
# Refinements
|
18
|
+
# =======================================================================
|
19
|
+
|
20
|
+
|
21
|
+
# Declarations
|
22
|
+
# =======================================================================
|
23
|
+
|
24
|
+
module NRSER::Props; end
|
25
|
+
|
26
|
+
|
27
|
+
# Definitions
|
28
|
+
# =======================================================================
|
29
|
+
|
30
|
+
# Instance methods to mix in to classes that include {NRSER::Props}.
|
31
|
+
#
|
32
|
+
module NRSER::Props::InstanceMethods
|
33
|
+
|
34
|
+
# Initialize the properties from a hash.
|
35
|
+
#
|
36
|
+
# Called from `#initialize` in {NRSER::Props::Base}, but if you just
|
37
|
+
# mix in {NRSER::Props::Props} you need to call it yourself.
|
38
|
+
#
|
39
|
+
# @param [Hash<(String | Symbol) => Object>] values
|
40
|
+
# Property values. Keys will be normalized to symbols.
|
41
|
+
#
|
42
|
+
# @return [nil]
|
43
|
+
#
|
44
|
+
# def initialize_props source_values
|
45
|
+
# array = []
|
46
|
+
# hash = {}
|
47
|
+
#
|
48
|
+
# self.class.props(only_primary: true).values.each { |prop|
|
49
|
+
# value = prop.create_value self, source_values
|
50
|
+
# case prop.key
|
51
|
+
# when Integer
|
52
|
+
# array[key] = value
|
53
|
+
# when Symbol
|
54
|
+
# hash[key] = value
|
55
|
+
# else
|
56
|
+
# raise "SHOULD NEVER HAPPEN, prop.key: #{ prop.pretty_inspect }"
|
57
|
+
# end
|
58
|
+
# }
|
59
|
+
#
|
60
|
+
# # TODO Now trigger all eager defaults (check prop getting trigger
|
61
|
+
# # correctly)
|
62
|
+
#
|
63
|
+
# # Check additional type invariants
|
64
|
+
# self.class.invariants.each do |type|
|
65
|
+
# type.check self
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# nil
|
69
|
+
# end # #initialize_props
|
70
|
+
|
71
|
+
|
72
|
+
# @param [Proc<(KEY, CURRENT, UPDATE) => VALUE>] &block
|
73
|
+
# Optional block to handle conflicts.
|
74
|
+
#
|
75
|
+
def merge other, &block
|
76
|
+
self.class.new \
|
77
|
+
self.to_h( only_primary: true ).
|
78
|
+
merge( other.symbolize_keys, &block )
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def dup
|
83
|
+
self.class.new \
|
84
|
+
self.to_h( only_primary: true )
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
# Create a new hash with property names mapped to values.
|
89
|
+
#
|
90
|
+
# @param [Boolean] only_own:
|
91
|
+
# When `true`, don't include parent properties.
|
92
|
+
#
|
93
|
+
# @param [Boolean] only_primary:
|
94
|
+
# When `true`, don't include sourced properties.
|
95
|
+
#
|
96
|
+
# @return [Hash<Symbol, Object>]
|
97
|
+
# Map of prop names to values.
|
98
|
+
#
|
99
|
+
def to_h only_own: false, only_primary: false, compact: true
|
100
|
+
hash = self.class.
|
101
|
+
props(only_own: only_own, only_primary: only_primary).
|
102
|
+
transform_values { |prop| prop.get self }
|
103
|
+
|
104
|
+
hash.compact! if compact
|
105
|
+
|
106
|
+
hash
|
107
|
+
end # #to_h
|
108
|
+
|
109
|
+
|
110
|
+
# Create a "data" representation suitable for transport, storage, etc.
|
111
|
+
#
|
112
|
+
# The result is meant to consist of only basic data types and structures -
|
113
|
+
# strings, numbers, arrays, hashes, datetimes, etc... though it depends on
|
114
|
+
# any custom objects it encounters correctly responding to `#to_data` for
|
115
|
+
# this to happen (as is implemented from classes that mix in Props here).
|
116
|
+
#
|
117
|
+
# Prop names are converted to strings (from symbols) since though YAML
|
118
|
+
# supports symbol values, they have poor portability across languages,
|
119
|
+
# and they mean the same thing in this situation.
|
120
|
+
#
|
121
|
+
# @param [Boolean] only_own:
|
122
|
+
# When `true`, don't include parent properties.
|
123
|
+
#
|
124
|
+
# @param [Boolean] only_primary:
|
125
|
+
# When `true`, don't include sourced properties.
|
126
|
+
#
|
127
|
+
# @param [Boolean] add_class:
|
128
|
+
# Add a special key with the class' name as the value.
|
129
|
+
#
|
130
|
+
# @param [String] class_key:
|
131
|
+
# Name for special class key.
|
132
|
+
#
|
133
|
+
# @return [Hash<String, *>]
|
134
|
+
# Map of property names as strings to their "data" value, plus the special
|
135
|
+
# class identifier key and value, if requested.
|
136
|
+
#
|
137
|
+
def to_data only_own: false,
|
138
|
+
only_primary: false,
|
139
|
+
add_class: true,
|
140
|
+
class_key: '__class__',
|
141
|
+
compact: true
|
142
|
+
# class_key: NRSER::Props::DEFAULT_CLASS_KEY
|
143
|
+
|
144
|
+
hash = self.class.props(only_own: only_own, only_primary: only_primary).
|
145
|
+
map { |name, prop|
|
146
|
+
[name.to_s, prop.to_data(self)]
|
147
|
+
}.
|
148
|
+
to_h
|
149
|
+
|
150
|
+
hash.compact! if compact
|
151
|
+
hash[class_key] = self.class.safe_name if add_class
|
152
|
+
|
153
|
+
hash
|
154
|
+
end # #to_data
|
155
|
+
|
156
|
+
|
157
|
+
# Language Inter-Op
|
158
|
+
# ---------------------------------------------------------------------
|
159
|
+
|
160
|
+
# Get a JSON {String} encoding the instance's data.
|
161
|
+
#
|
162
|
+
# @param [Array] *args
|
163
|
+
# I really don't know. `#to_json` takes at last one argument, but I've
|
164
|
+
# had trouble finding a spec for it :/
|
165
|
+
#
|
166
|
+
# @return [String]
|
167
|
+
#
|
168
|
+
def to_json *args
|
169
|
+
to_data.to_json *args
|
170
|
+
end # #to_json
|
171
|
+
|
172
|
+
|
173
|
+
# Get a YAML {String} encoding the instance's data.
|
174
|
+
#
|
175
|
+
# @param [Array] *args
|
176
|
+
# I really don't know... whatever {YAML.dump} sends to it i guess.
|
177
|
+
#
|
178
|
+
# @return [String]
|
179
|
+
#
|
180
|
+
def to_yaml *args
|
181
|
+
to_data.to_yaml *args
|
182
|
+
end
|
183
|
+
|
184
|
+
end # module NRSER::Props::ClassMethods
|
@@ -0,0 +1,359 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Requirements
|
5
|
+
# =======================================================================
|
6
|
+
|
7
|
+
# Project / Package
|
8
|
+
# -----------------------------------------------------------------------
|
9
|
+
|
10
|
+
require 'nrser/refinements/types'
|
11
|
+
|
12
|
+
require 'nrser/graph/tsorter'
|
13
|
+
|
14
|
+
require_relative './prop'
|
15
|
+
|
16
|
+
|
17
|
+
# Refinements
|
18
|
+
# =======================================================================
|
19
|
+
|
20
|
+
using NRSER::Types
|
21
|
+
|
22
|
+
|
23
|
+
# Declarations
|
24
|
+
# =======================================================================
|
25
|
+
|
26
|
+
module NRSER::Props; end
|
27
|
+
|
28
|
+
|
29
|
+
# Definitions
|
30
|
+
# =======================================================================
|
31
|
+
|
32
|
+
# @todo document NRSER::Props::ClassMetadata class.
|
33
|
+
class NRSER::Props::Metadata
|
34
|
+
|
35
|
+
# Constants
|
36
|
+
# ======================================================================
|
37
|
+
|
38
|
+
VARIABLE_NAME = :@__NRSER_metadata__
|
39
|
+
|
40
|
+
|
41
|
+
# Class Methods
|
42
|
+
# ======================================================================
|
43
|
+
|
44
|
+
# @todo Document has_metadata? method.
|
45
|
+
#
|
46
|
+
# @param [type] arg_name
|
47
|
+
# @todo Add name param description.
|
48
|
+
#
|
49
|
+
# @return [return_type]
|
50
|
+
# @todo Document return value.
|
51
|
+
#
|
52
|
+
def self.has_metadata? klass
|
53
|
+
klass.instance_variable_defined? VARIABLE_NAME
|
54
|
+
end # .has_metadata?
|
55
|
+
|
56
|
+
|
57
|
+
def self.metadata_for klass
|
58
|
+
klass.instance_variable_get VARIABLE_NAME
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# Attributes
|
63
|
+
# ======================================================================
|
64
|
+
|
65
|
+
|
66
|
+
# Constructor
|
67
|
+
# ======================================================================
|
68
|
+
|
69
|
+
# Instantiate a new `NRSER::Props::ClassMetadata`.
|
70
|
+
def initialize klass
|
71
|
+
@klass = klass
|
72
|
+
@props = {}
|
73
|
+
@invariants = Set.new
|
74
|
+
@storage = nil
|
75
|
+
end # #initialize
|
76
|
+
|
77
|
+
|
78
|
+
# Instance Methods
|
79
|
+
# ======================================================================
|
80
|
+
|
81
|
+
def superclass_has_metadata?
|
82
|
+
@klass.superclass.respond_to? :metadata
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def superclass_metadata
|
87
|
+
@klass.superclass.metadata
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def get_prop name
|
92
|
+
name = name.to_s.to_sym unless name.is_a?( Symbol )
|
93
|
+
|
94
|
+
return @props[name] if @props.key?( name )
|
95
|
+
|
96
|
+
if superclass_has_metadata?
|
97
|
+
superclass_metadata.get_prop name
|
98
|
+
else
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
alias_method :[], :get_prop
|
104
|
+
|
105
|
+
|
106
|
+
# @todo
|
107
|
+
# Cache / optimize
|
108
|
+
#
|
109
|
+
def prop_names only_own: false, only_primary: false
|
110
|
+
Set.new props( only_own: only_own, only_primary: only_primary ).keys
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
# Get a map of property names to property instances.
|
115
|
+
#
|
116
|
+
# @param [Boolean] only_own:
|
117
|
+
# Don't include super-class properties.
|
118
|
+
#
|
119
|
+
# @param [Boolean] only_primary:
|
120
|
+
# Don't include properties that have a {NRSER::Props::Prop#source}.
|
121
|
+
#
|
122
|
+
# @return [Hash{ Symbol => NRSER::Props::Prop }]
|
123
|
+
# Hash mapping property name to property instance.
|
124
|
+
#
|
125
|
+
def props only_own: false, only_primary: false
|
126
|
+
result = if !only_own && superclass_has_metadata?
|
127
|
+
superclass_metadata.props only_own: only_own,
|
128
|
+
only_primary: only_primary
|
129
|
+
else
|
130
|
+
{}
|
131
|
+
end
|
132
|
+
|
133
|
+
if only_primary
|
134
|
+
@props.each {|name, prop| result[name] = prop if prop.primary? }
|
135
|
+
else
|
136
|
+
result.merge! @props
|
137
|
+
end
|
138
|
+
|
139
|
+
result
|
140
|
+
end # #props
|
141
|
+
|
142
|
+
|
143
|
+
# Define a property.
|
144
|
+
#
|
145
|
+
# @param [Symbol] name
|
146
|
+
# The name of the property.
|
147
|
+
#
|
148
|
+
# @param [Hash{ Symbol => Object }] **opts
|
149
|
+
# Constructor options for {NRSER::Props::Prop}.
|
150
|
+
#
|
151
|
+
# @return [NRSER::Props::Prop]
|
152
|
+
# The newly created prop, thought you probably don't need it (it's
|
153
|
+
# already all bound up on the class at this point), but why not?
|
154
|
+
#
|
155
|
+
def prop name, **opts
|
156
|
+
t.non_empty_sym.check! name
|
157
|
+
|
158
|
+
if @props.key? name
|
159
|
+
raise ArgumentError.new binding.erb <<~END
|
160
|
+
Prop <%= name.inspect %> already set for <%= @klass %>:
|
161
|
+
|
162
|
+
<%= @props[name].inspect %>
|
163
|
+
END
|
164
|
+
end
|
165
|
+
|
166
|
+
prop = NRSER::Props::Prop.new @klass, name, **opts
|
167
|
+
@props[name] = prop
|
168
|
+
|
169
|
+
prop.names.each do |name|
|
170
|
+
if prop.create_reader? name
|
171
|
+
@klass.class_eval do
|
172
|
+
define_method name do
|
173
|
+
prop.get self
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
if prop.create_writer? name
|
179
|
+
@klass.class_eval do
|
180
|
+
define_method "#{ name }=" do |value|
|
181
|
+
prop.set self, value
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
prop
|
188
|
+
end # #prop
|
189
|
+
|
190
|
+
|
191
|
+
# Check primary prop values and fill in defaults, yielding `(Prop, VALUE)`
|
192
|
+
# to the `&block`.
|
193
|
+
#
|
194
|
+
# Used when initializing instances.
|
195
|
+
#
|
196
|
+
# @param [#each_pair | #each_index] values
|
197
|
+
# Collection of prop values iterable by key/value pairs or by indexed
|
198
|
+
# entries.
|
199
|
+
#
|
200
|
+
# @param [Proc<(NRSER::Props::Prop, VALUE)>] &block
|
201
|
+
# Block that will receive primary prop and value pairs.
|
202
|
+
#
|
203
|
+
# @raise [TypeError]
|
204
|
+
# If a value is does not satisfy it's {NRSER::Props::Prop#type}.
|
205
|
+
#
|
206
|
+
# @raise [ArgumentError]
|
207
|
+
# If `values` doesn't respond to `#each_pair` or `#each_index`.
|
208
|
+
#
|
209
|
+
# @raise [NameError]
|
210
|
+
# If a value is not provided for a primary prop and a default can not
|
211
|
+
# be created.
|
212
|
+
#
|
213
|
+
# @raise [TSort::Cyclic]
|
214
|
+
# If any of the primary prop's {NRSER::Props::Prop#deps} for dependency
|
215
|
+
# cycles.
|
216
|
+
#
|
217
|
+
def each_primary_prop_value_from values, &block
|
218
|
+
primary_props = props only_primary: true
|
219
|
+
|
220
|
+
# Normalize values to a `Hash<Symbol, VALUE>` so everything can deal with
|
221
|
+
# one form. Default values will be set here as they're resolved and made
|
222
|
+
# available to subsequent {Prop#default} calls.
|
223
|
+
values_by_name = {}
|
224
|
+
|
225
|
+
if values.respond_to? :each_pair
|
226
|
+
values.each_pair { |key, value|
|
227
|
+
# Figure out the prop name {Symbol}
|
228
|
+
name = case key
|
229
|
+
when Symbol
|
230
|
+
key
|
231
|
+
when String
|
232
|
+
key.to_sym
|
233
|
+
else
|
234
|
+
key.to_s.to_sym
|
235
|
+
end
|
236
|
+
|
237
|
+
# If the `name` corresponds to a primary prop set it in the values by
|
238
|
+
# name
|
239
|
+
#
|
240
|
+
# TODO Should check that the name is not already set?
|
241
|
+
#
|
242
|
+
values_by_name[name] = value if primary_props.key? name
|
243
|
+
}
|
244
|
+
elsif values.respond_to? :each_index
|
245
|
+
indexed = []
|
246
|
+
|
247
|
+
primary_props.each_value do |prop|
|
248
|
+
indexed[prop.index] = prop unless prop.index.nil?
|
249
|
+
end
|
250
|
+
|
251
|
+
values.each_index { |index|
|
252
|
+
prop = indexed[index]
|
253
|
+
values_by_name[prop.name] = values[index] if prop
|
254
|
+
}
|
255
|
+
else
|
256
|
+
raise ArgumentError.new binding.erb <<~END
|
257
|
+
`source` argument must respond to `#each_pair` or `#each_index`
|
258
|
+
|
259
|
+
Found:
|
260
|
+
|
261
|
+
<%= source.pretty_inspect %>
|
262
|
+
|
263
|
+
END
|
264
|
+
end
|
265
|
+
|
266
|
+
# Topological sort the primary props by their default dependencies.
|
267
|
+
#
|
268
|
+
NRSER::Graph::TSorter.new(
|
269
|
+
primary_props.each_value
|
270
|
+
) { |prop, &on_dep_prop|
|
271
|
+
#
|
272
|
+
# This block is responsible for receiving a {Prop} and a callback
|
273
|
+
# block and invoking that callback block on each of the prop's
|
274
|
+
# dependencies (if any).
|
275
|
+
#
|
276
|
+
prop.deps.each { |name|
|
277
|
+
if primary_props.key? name
|
278
|
+
on_dep_prop.call primary_props[name]
|
279
|
+
else
|
280
|
+
raise RuntimeError.new binding.erb <<~END
|
281
|
+
Property <%= prop.full_name %> depends on prop `<%= name %>`,
|
282
|
+
but no primary prop with that name could be found!
|
283
|
+
END
|
284
|
+
end
|
285
|
+
}
|
286
|
+
}.tsort_each do |prop|
|
287
|
+
# {Prop} instances will now be yielded in an order that allows any
|
288
|
+
# inter-dependencies to be resolved (as long as there weren't dependency
|
289
|
+
# cycles, which {NRSER::Graph::TSorter} will raise if it finds)
|
290
|
+
|
291
|
+
# If we have a value for the prop, just check that
|
292
|
+
if values_by_name.key? prop.name
|
293
|
+
prop.check! values_by_name[prop.name]
|
294
|
+
else
|
295
|
+
# Otherwise, get the default value, providing the values we already
|
296
|
+
# know in case the default is a {Proc} that needs some of them.
|
297
|
+
#
|
298
|
+
# We set that value in `values_by_name` so that subsequent
|
299
|
+
# {Prop#default} calls can use it.
|
300
|
+
#
|
301
|
+
values_by_name[prop.name] = prop.default **values_by_name
|
302
|
+
end
|
303
|
+
|
304
|
+
# Yield the {Prop} and it's value back to the `&block`
|
305
|
+
block.call prop, values_by_name[prop.name]
|
306
|
+
end # .tsort_each
|
307
|
+
end # #each_primary_prop_value_from
|
308
|
+
|
309
|
+
|
310
|
+
def invariants only_own: false
|
311
|
+
result = if !only_own && superclass_has_metadata?
|
312
|
+
superclass_metadata.invariants only_own: false
|
313
|
+
else
|
314
|
+
Set.new
|
315
|
+
end
|
316
|
+
|
317
|
+
result + @invariants
|
318
|
+
end
|
319
|
+
|
320
|
+
|
321
|
+
def invariant type
|
322
|
+
@invariants.add type
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
def default_storage
|
327
|
+
if superclass_has_metadata?
|
328
|
+
superclass_metadata.storage
|
329
|
+
else
|
330
|
+
@storage = NRSER::Props::Storage::InstanceVariable.new
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
|
335
|
+
def storage value = nil
|
336
|
+
if value.nil?
|
337
|
+
if @storage.nil?
|
338
|
+
default_storage
|
339
|
+
else
|
340
|
+
@storage
|
341
|
+
end
|
342
|
+
else
|
343
|
+
@storage = value
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
def freeze
|
349
|
+
super()
|
350
|
+
|
351
|
+
if superclass_has_metadata? &&
|
352
|
+
!superclass_metadata.frozen?
|
353
|
+
superclass_metadata.freeze
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
end # class NRSER::Props::ClassMetadata
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Requirements
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
# Stdlib
|
5
|
+
# -----------------------------------------------------------------------
|
6
|
+
|
7
|
+
# Deps
|
8
|
+
# -----------------------------------------------------------------------
|
9
|
+
|
10
|
+
# Project / Package
|
11
|
+
# -----------------------------------------------------------------------
|
12
|
+
require_relative '../../props'
|
13
|
+
require_relative '../storage/key'
|
14
|
+
require_relative '../storage/instance_variables'
|
15
|
+
|
16
|
+
|
17
|
+
# Declarations
|
18
|
+
# ========================================================================
|
19
|
+
|
20
|
+
module NRSER::Props::Mutable; end
|
21
|
+
|
22
|
+
|
23
|
+
# Definitions
|
24
|
+
# =======================================================================
|
25
|
+
|
26
|
+
# Mix-in to store property values in instance variables of the same name.
|
27
|
+
#
|
28
|
+
module NRSER::Props::Mutable::InstanceVariables
|
29
|
+
|
30
|
+
STORAGE = NRSER::Props::Storage::InstanceVariables.new immutable: false
|
31
|
+
|
32
|
+
|
33
|
+
# Class Methods
|
34
|
+
# ======================================================================
|
35
|
+
|
36
|
+
def self.included base
|
37
|
+
base.include NRSER::Props
|
38
|
+
base.metadata.storage STORAGE
|
39
|
+
base.metadata.freeze
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Instance Methods
|
44
|
+
# ======================================================================
|
45
|
+
|
46
|
+
# Since the {NRSER::Props::Immutable::InstanceVariables} mix-in does *not*
|
47
|
+
# need to tap into the initialize chain,
|
48
|
+
#
|
49
|
+
def initialize_props values = {}
|
50
|
+
self.class.metadata.each_primary_prop_value_from( values ) { |prop, value|
|
51
|
+
instance_variable_set "@#{ prop.name }", value
|
52
|
+
}
|
53
|
+
|
54
|
+
# Check additional type invariants
|
55
|
+
self.class.invariants.each do |type|
|
56
|
+
type.check self
|
57
|
+
end
|
58
|
+
end # #initialize_props
|
59
|
+
|
60
|
+
end # module NRSER::Props::Immutable
|