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,86 @@
|
|
1
|
+
require 'nrser/meta/source/location'
|
2
|
+
|
3
|
+
describe_spec_file(
|
4
|
+
spec_path: __FILE__,
|
5
|
+
class: NRSER::Meta::Source::Location,
|
6
|
+
) do
|
7
|
+
|
8
|
+
shared_examples described_class do |valid:, file:, line:|
|
9
|
+
it { is_expected.to be_a( described_class ) }
|
10
|
+
|
11
|
+
describe_attribute :valid? do
|
12
|
+
it { is_expected.to be valid }
|
13
|
+
end # Attribute valid? Description
|
14
|
+
|
15
|
+
describe_attribute :file do
|
16
|
+
it { is_expected.to eq file }
|
17
|
+
end # Attribute file Description
|
18
|
+
|
19
|
+
describe_attribute :line do
|
20
|
+
it { is_expected.to eq line }
|
21
|
+
end # Attribute line Description
|
22
|
+
|
23
|
+
describe_attribute :to_s do
|
24
|
+
it { is_expected.to eq "#{ file || '???' }:#{ line || '???' }" }
|
25
|
+
end # Attribute to_s Description
|
26
|
+
|
27
|
+
describe_method :[] do
|
28
|
+
describe_called_with 0 do
|
29
|
+
it { is_expected.to eq file }
|
30
|
+
end # called with 0
|
31
|
+
|
32
|
+
describe_called_with 1 do
|
33
|
+
it { is_expected.to eq line }
|
34
|
+
end # called with 1
|
35
|
+
end # Method [] Description
|
36
|
+
|
37
|
+
describe_attribute :to_a do
|
38
|
+
it { is_expected.to eq [file, line] }
|
39
|
+
end # Attribute to_a Description
|
40
|
+
|
41
|
+
describe_attribute :to_h do
|
42
|
+
it { is_expected.to be_a Hash }
|
43
|
+
it { is_expected.to eq( {file: file, line: line}.compact ) }
|
44
|
+
end # Attribute to_h Description
|
45
|
+
|
46
|
+
end # described_class from:
|
47
|
+
|
48
|
+
|
49
|
+
context "{Method#source_location} with non-nil file and line" do
|
50
|
+
loc = NRSER.own_class_Methods.
|
51
|
+
map( &:source_location ).
|
52
|
+
find { |src_loc|
|
53
|
+
!src_loc.nil? &&
|
54
|
+
!src_loc[0].nil? &&
|
55
|
+
!src_loc[1].nil?
|
56
|
+
}
|
57
|
+
|
58
|
+
context "instantiate from source location array" do
|
59
|
+
describe_instance loc do
|
60
|
+
it_behaves_like described_class,
|
61
|
+
valid: true,
|
62
|
+
file: loc[0],
|
63
|
+
line: loc[1]
|
64
|
+
end # instance loc
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
context "instantiate from prop value hash" do
|
69
|
+
describe_instance file: loc[0], line: loc[1] do
|
70
|
+
it_behaves_like described_class,
|
71
|
+
valid: true,
|
72
|
+
file: loc[0],
|
73
|
+
line: loc[1]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end # {Method#source_location} with non-nil file and line
|
77
|
+
|
78
|
+
|
79
|
+
context "`nil` source location" do
|
80
|
+
describe_instance nil do
|
81
|
+
it_behaves_like described_class, valid: false, file: nil, line: nil
|
82
|
+
end
|
83
|
+
end # `nil` source location
|
84
|
+
|
85
|
+
|
86
|
+
end # spec file
|
@@ -0,0 +1,297 @@
|
|
1
|
+
# Propertied Immutable Hashes
|
2
|
+
# ============================================================================
|
3
|
+
#
|
4
|
+
# The {NRSER::Props::Immutable::Hash} module can be mixed into subclasses of
|
5
|
+
# {Hamster::Hash} (an immutable {Hash}) to provide property encoding,
|
6
|
+
# decoding and reading backed by the elements of the hash itself.
|
7
|
+
#
|
8
|
+
# This is extremely similar to {NRSER::Props::Immutable::Vector} - which I
|
9
|
+
# wrote and tested first - so check their for more comments / details.
|
10
|
+
#
|
11
|
+
# Requirements
|
12
|
+
# ----------------------------------------------------------------------------
|
13
|
+
#
|
14
|
+
# We're going to use the {NRSER::Types} refinements to specify types for our
|
15
|
+
# props.
|
16
|
+
|
17
|
+
require 'nrser/refinements/types'
|
18
|
+
|
19
|
+
# And we're going to need the mixin module itself.
|
20
|
+
|
21
|
+
require 'nrser/props/immutable/hash'
|
22
|
+
|
23
|
+
#
|
24
|
+
# Refinements
|
25
|
+
# ----------------------------------------------------------------------------
|
26
|
+
#
|
27
|
+
# Declare that we're going to use the {NRSER::Types} refinements, which just
|
28
|
+
# provide the global `t` shortcut to {NRSER::Types}.
|
29
|
+
|
30
|
+
using NRSER::Types
|
31
|
+
|
32
|
+
#
|
33
|
+
# Examples
|
34
|
+
# ----------------------------------------------------------------------------
|
35
|
+
#
|
36
|
+
|
37
|
+
describe_spec_file(
|
38
|
+
spec_path: __FILE__,
|
39
|
+
module: NRSER::Props::Immutable::Hash,
|
40
|
+
) do
|
41
|
+
|
42
|
+
describe_setup "Simple 2D Integer Point" do
|
43
|
+
# ==========================================================================
|
44
|
+
|
45
|
+
# Complete Fixture Class
|
46
|
+
# --------------------------------------------------------------------------
|
47
|
+
#
|
48
|
+
# First, let's look at a working example:
|
49
|
+
#
|
50
|
+
|
51
|
+
subject :point_2d_int do
|
52
|
+
Class.new( Hamster::Hash ) do
|
53
|
+
include NRSER::Props::Immutable::Hash
|
54
|
+
|
55
|
+
# So that error messages look right. You don't need this in "regularly"
|
56
|
+
# defined classes.
|
57
|
+
def self.name; 'Point2DInt'; end
|
58
|
+
def self.inspect; name; end
|
59
|
+
|
60
|
+
# It's vital that we include the `key:` keyword argument, and that the
|
61
|
+
# values are non-negative integer indexes for the vector.
|
62
|
+
prop :x, type: t.int
|
63
|
+
prop :y, type: t.int
|
64
|
+
|
65
|
+
# Just for fun and to show the capabilities, define a `#to_s` to nicely
|
66
|
+
# print the point.
|
67
|
+
def to_s
|
68
|
+
"(#{ x }, #{ y })"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# and let's define our expectations for a successfully created point:
|
74
|
+
|
75
|
+
shared_examples "Point2DInt" do |x:, y:|
|
76
|
+
|
77
|
+
# It should be an instance of the class
|
78
|
+
|
79
|
+
it { is_expected.to be_a point_2d_int }
|
80
|
+
|
81
|
+
# as well as {Hamster::Vector}!
|
82
|
+
|
83
|
+
it { is_expected.to be_a Hamster::Hash }
|
84
|
+
|
85
|
+
|
86
|
+
# and it should have the `x` and `y` accessible as hash values via
|
87
|
+
# it's `#[]` method:
|
88
|
+
|
89
|
+
describe_method :[] do
|
90
|
+
describe_called_with :x do
|
91
|
+
it { is_expected.to be x }
|
92
|
+
end
|
93
|
+
|
94
|
+
describe_called_with :y do
|
95
|
+
it { is_expected.to be y }
|
96
|
+
end
|
97
|
+
end # Method [] Description
|
98
|
+
|
99
|
+
# as well as via the `#x` and `#y` attribute reader methods that
|
100
|
+
# {NRSER::Props} creates:
|
101
|
+
|
102
|
+
describe_attribute :x do
|
103
|
+
it { is_expected.to be x }
|
104
|
+
end
|
105
|
+
|
106
|
+
describe_attribute :y do
|
107
|
+
it { is_expected.to be y }
|
108
|
+
end
|
109
|
+
|
110
|
+
# Converting to an array should be equal to `[[:x, x], [:y, y]]`:
|
111
|
+
|
112
|
+
describe_attribute :to_a do
|
113
|
+
it { is_expected.to include [:x, x], [:y, y] }
|
114
|
+
end
|
115
|
+
|
116
|
+
# We should get a regular hash out of `#to_h`
|
117
|
+
|
118
|
+
describe_attribute :to_h do
|
119
|
+
it { is_expected.to eq x: x, y: y }
|
120
|
+
end
|
121
|
+
|
122
|
+
# We should see our custom `#to_s` value
|
123
|
+
|
124
|
+
describe_attribute :to_s do
|
125
|
+
it { is_expected.to eq "(#{ x }, #{ y })" }
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
describe_setup "Creating a Point from `source`" do
|
132
|
+
# ========================================================================
|
133
|
+
|
134
|
+
# Our subject will be a Point2dInt created from a `source` value that we
|
135
|
+
# will define for each example.
|
136
|
+
|
137
|
+
subject do
|
138
|
+
point_2d_int.new source
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
describe_case "From an `{x: Integer, y: Integer}` literal `Hash`" do
|
143
|
+
# ------------------------------------------------------------------------
|
144
|
+
|
145
|
+
# Let the `source` be the {Hash} `{x: 1, y: 2}`.
|
146
|
+
|
147
|
+
describe_when source: {x: 1, y: 2} do
|
148
|
+
|
149
|
+
# Now we should be able to construct a point. Test that it behaves
|
150
|
+
# like we defined above:
|
151
|
+
|
152
|
+
it_behaves_like "Point2DInt", source
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
describe_case "From a `Hamster::Hash[x: Integer, y: Integer]`" do
|
159
|
+
# --------------------------------------------------------------------
|
160
|
+
#
|
161
|
+
# All that `#initialize` cares about is that it can access the `source`
|
162
|
+
# via `#[]` and that it can tell if it should use names or integer
|
163
|
+
# index by looking for `#each_pair` and `#each_index`, respectively.
|
164
|
+
#
|
165
|
+
|
166
|
+
describe_when source: Hamster::Hash[x: 1, y: 2] do
|
167
|
+
|
168
|
+
# Now we should be able to construct a point. Test that it behaves
|
169
|
+
# like we defined above:
|
170
|
+
|
171
|
+
it_behaves_like "Point2DInt", source.to_h
|
172
|
+
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end # .new - Creating a Point
|
176
|
+
# ************************************************************************
|
177
|
+
|
178
|
+
|
179
|
+
describe_section "Deriving a new point from another" do
|
180
|
+
# ========================================================================
|
181
|
+
#
|
182
|
+
# Our point instances are immutable, but we can use {Hamster::Vector}'s
|
183
|
+
# methods for deriving new instances to derive new points.
|
184
|
+
#
|
185
|
+
|
186
|
+
# Let's create a point subject to start off with
|
187
|
+
|
188
|
+
subject do
|
189
|
+
point_2d_int.new x: 1, y: 2
|
190
|
+
end
|
191
|
+
|
192
|
+
# and we can play around with a few {Hamster::Vector} methods...
|
193
|
+
|
194
|
+
describe_method :put do
|
195
|
+
|
196
|
+
# Change the value at entry 0 to 2
|
197
|
+
|
198
|
+
describe_called_with :x, 2 do
|
199
|
+
it_behaves_like "Point2DInt", x: 2, y: 2
|
200
|
+
end # called with 0, 2
|
201
|
+
|
202
|
+
# Type checking should still be in effect, of course
|
203
|
+
|
204
|
+
describe_called_with :x, 'hey' do
|
205
|
+
it { expect { subject }.to raise_error TypeError }
|
206
|
+
end # called with 0, 'hey'
|
207
|
+
|
208
|
+
end # Method put Description
|
209
|
+
|
210
|
+
|
211
|
+
describe_method :map do
|
212
|
+
|
213
|
+
# We have to do some manual funkiness here 'cause
|
214
|
+
# `describe_called_with` doesn't take a block...
|
215
|
+
#
|
216
|
+
### TODO
|
217
|
+
#
|
218
|
+
# We should be able to handle this by passing a {NRSER::Message}
|
219
|
+
# to `describe_called_with`, but that's not yet implemented
|
220
|
+
# (RSpex need s a *lot* of work).
|
221
|
+
#
|
222
|
+
# describe_called_with(
|
223
|
+
# NRSER::Message.new { |value| value + 1 }
|
224
|
+
# ) do ...
|
225
|
+
#
|
226
|
+
# or something like that.
|
227
|
+
#
|
228
|
+
|
229
|
+
describe "add 1 to each entry" do
|
230
|
+
subject do
|
231
|
+
super().call { |key, value| [key, value + 1] }
|
232
|
+
end
|
233
|
+
|
234
|
+
it_behaves_like "Point2DInt", x: 2, y: 3
|
235
|
+
end
|
236
|
+
|
237
|
+
# Type checking should still be enforced
|
238
|
+
|
239
|
+
describe "try to turn entries into Strings" do
|
240
|
+
subject do
|
241
|
+
super().call { |key, value| [key, "value: #{ value }"] }
|
242
|
+
end
|
243
|
+
|
244
|
+
it do
|
245
|
+
expect { subject }.to raise_error TypeError
|
246
|
+
end
|
247
|
+
end # "try to turn entries into Strings"
|
248
|
+
|
249
|
+
end # Method map Description
|
250
|
+
|
251
|
+
|
252
|
+
describe_section "Adding extra keys and values" do
|
253
|
+
# ======================================================================
|
254
|
+
#
|
255
|
+
# At the moment, we *can* add *extra* keys and values to the point.
|
256
|
+
#
|
257
|
+
|
258
|
+
# Let's try adding a `:z` key with value `zee`
|
259
|
+
|
260
|
+
describe_method :put do
|
261
|
+
describe_called_with :z, 'zee' do
|
262
|
+
|
263
|
+
# It works! And the new point still behaves as expected.
|
264
|
+
|
265
|
+
it_behaves_like "Point2DInt", x: 1, y: 2
|
266
|
+
|
267
|
+
# but it *also* has a `:z` key with value `'zee'`, which is not
|
268
|
+
# type checked in any way because it has no corresponding prop
|
269
|
+
|
270
|
+
it "also has `z: 'zee`" do
|
271
|
+
expect( subject[:z] ).to eq 'zee'
|
272
|
+
end
|
273
|
+
|
274
|
+
### TODO
|
275
|
+
#
|
276
|
+
# In the future (soon?!), we will have options to configure how
|
277
|
+
# to handle extra values... thinking:
|
278
|
+
#
|
279
|
+
# 1. Allow (what happens now)
|
280
|
+
# 2. Prohibit (raise an error)
|
281
|
+
# 3. Discard (just toss them)
|
282
|
+
#
|
283
|
+
# Of course, we will still discard derived prop values found in
|
284
|
+
# the source so that we can always dump and re-load data values.
|
285
|
+
#
|
286
|
+
|
287
|
+
end # called with :z, 3
|
288
|
+
end # Method put Description
|
289
|
+
|
290
|
+
end # section Adding extra keys and values
|
291
|
+
# **********************************************************************
|
292
|
+
|
293
|
+
end # section Deriving a new point from another
|
294
|
+
# ************************************************************************
|
295
|
+
|
296
|
+
end # Simple 2D Integer Point
|
297
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
# Propertied Immutable Vectors
|
2
|
+
# ============================================================================
|
3
|
+
#
|
4
|
+
# The {NRSER::Props::Immutable::Vector} module can be mixed into subclasses of
|
5
|
+
# {Hamster::Vector} (an immutable {Array}) to provide property encoding,
|
6
|
+
# decoding and reading backed by the elements of the vector itself.
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# Requirements
|
10
|
+
# ----------------------------------------------------------------------------
|
11
|
+
#
|
12
|
+
# We're going to use the {NRSER::Types} refinements to specify types for our
|
13
|
+
# props.
|
14
|
+
|
15
|
+
require 'nrser/refinements/types'
|
16
|
+
|
17
|
+
# And we're going to need the mixin module itself.
|
18
|
+
|
19
|
+
require 'nrser/props/immutable/vector'
|
20
|
+
|
21
|
+
#
|
22
|
+
# Refinements
|
23
|
+
# ----------------------------------------------------------------------------
|
24
|
+
#
|
25
|
+
# Declare that we're going to use the {NRSER::Types} refinements, which just
|
26
|
+
# provide the global `t` shortcut to {NRSER::Types}.
|
27
|
+
|
28
|
+
using NRSER::Types
|
29
|
+
|
30
|
+
#
|
31
|
+
# Examples
|
32
|
+
# ----------------------------------------------------------------------------
|
33
|
+
#
|
34
|
+
|
35
|
+
describe NRSER::Props::Immutable::Vector do
|
36
|
+
|
37
|
+
# TODO Fix this shit!
|
38
|
+
#
|
39
|
+
# describe_spec_file(
|
40
|
+
# spec_path: __FILE__,
|
41
|
+
# ) do
|
42
|
+
|
43
|
+
describe "Simple 2D Integer Point" do
|
44
|
+
# ==========================================================================
|
45
|
+
|
46
|
+
# Complete Fixture Class
|
47
|
+
# --------------------------------------------------------------------------
|
48
|
+
#
|
49
|
+
# First, let's look at a working example:
|
50
|
+
#
|
51
|
+
|
52
|
+
let :point_2d_int do
|
53
|
+
Class.new( Hamster::Vector ) do
|
54
|
+
include NRSER::Props::Immutable::Vector
|
55
|
+
|
56
|
+
# So that error messages look right. You don't need this in "regularly"
|
57
|
+
# defined classes.
|
58
|
+
def self.name; 'Point2DInt'; end
|
59
|
+
|
60
|
+
# It's vital that we include the `key:` keyword argument, and that the
|
61
|
+
# values are non-negative integer indexes for the vector.
|
62
|
+
prop :x, type: t.int, index: 0
|
63
|
+
prop :y, type: t.int, index: 1
|
64
|
+
|
65
|
+
# Just for fun and to show the capabilities, define a `#to_s` to nicely
|
66
|
+
# print the point.
|
67
|
+
def to_s
|
68
|
+
"(#{ x }, #{ y })"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# and let's define our expectations for a successfully created point:
|
74
|
+
|
75
|
+
shared_examples "Point2DInt" do |x:, y:|
|
76
|
+
|
77
|
+
# It should be an instance of the class
|
78
|
+
|
79
|
+
it { is_expected.to be_a point_2d_int }
|
80
|
+
|
81
|
+
# as well as {Hamster::Vector}!
|
82
|
+
|
83
|
+
it { is_expected.to be_a Hamster::Vector }
|
84
|
+
|
85
|
+
|
86
|
+
# and it should have the `x` and `y` accessible as vector entries via
|
87
|
+
# it's `#[]` method:
|
88
|
+
|
89
|
+
describe_method :[] do
|
90
|
+
describe_called_with 0 do
|
91
|
+
it { is_expected.to be x }
|
92
|
+
end
|
93
|
+
|
94
|
+
describe_called_with 1 do
|
95
|
+
it { is_expected.to be y }
|
96
|
+
end
|
97
|
+
end # Method [] Description
|
98
|
+
|
99
|
+
# as well as via the `#x` and `#y` attribute reader methods that
|
100
|
+
# {NRSER::Props} creates:
|
101
|
+
|
102
|
+
describe_attribute :x do
|
103
|
+
it { is_expected.to be x }
|
104
|
+
end
|
105
|
+
|
106
|
+
describe_attribute :y do
|
107
|
+
it { is_expected.to be y }
|
108
|
+
end
|
109
|
+
|
110
|
+
# Converting to an array should be equal to `[x, y]`:
|
111
|
+
|
112
|
+
describe_attribute :to_a do
|
113
|
+
it { is_expected.to eq [x, y] }
|
114
|
+
end
|
115
|
+
|
116
|
+
# However, {NRSER::Props} *overrides* `#to_h` to provide a {Hash} of
|
117
|
+
# the props by name, so that will behave differently
|
118
|
+
|
119
|
+
describe_attribute :to_h do
|
120
|
+
it { is_expected.to eq x: x, y: y }
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
describe_section "Creating a Point" do
|
127
|
+
# ========================================================================
|
128
|
+
|
129
|
+
# Our subject will be a Point2dInt created from a `source` value that we
|
130
|
+
# will define for each example.
|
131
|
+
|
132
|
+
subject do
|
133
|
+
point_2d_int.new source
|
134
|
+
end
|
135
|
+
|
136
|
+
# We're going to define these sources at the example group level so that
|
137
|
+
# we can use them easily in `it_behaves_like`, but `subject` needs them
|
138
|
+
# at the example level, so we provide this little helper:
|
139
|
+
|
140
|
+
def source
|
141
|
+
self.class.source
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
describe "From an [x, y] pair Array" do
|
146
|
+
# ------------------------------------------------------------------------
|
147
|
+
|
148
|
+
# Let the `source` be the {Array} `[1, 2]`.
|
149
|
+
|
150
|
+
def self.source
|
151
|
+
[1, 2]
|
152
|
+
end
|
153
|
+
|
154
|
+
# Now we should be able to construct a point. Test that it behaves like
|
155
|
+
# we defined above:
|
156
|
+
|
157
|
+
it_behaves_like "Point2DInt", x: source[0], y: source[1]
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
describe "From an {x:, y:} Hash" do
|
163
|
+
# ------------------------------------------------------------------------
|
164
|
+
|
165
|
+
# Let the `source` be the {Hash} `{x: 1, y: 2}`.
|
166
|
+
|
167
|
+
def self.source
|
168
|
+
{x: 1, y: 2}
|
169
|
+
end
|
170
|
+
|
171
|
+
# Now we should be able to construct a point. Test that it behaves like
|
172
|
+
# we defined above:
|
173
|
+
|
174
|
+
it_behaves_like "Point2DInt", source
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "From a Hamster::Vector[x, y] pair" do
|
179
|
+
# ------------------------------------------------------------------------
|
180
|
+
#
|
181
|
+
# All that `#initialize` cares about is that it can access the `source`
|
182
|
+
# via `#[]` and that it can tell if it should use names or integer
|
183
|
+
# index by looking for `#each_pair` and `#each_index`, respectively.
|
184
|
+
#
|
185
|
+
|
186
|
+
# Let the `source` be the `Hamster::Vector[1, 2]`.
|
187
|
+
|
188
|
+
def self.source
|
189
|
+
Hamster::Vector[1, 2]
|
190
|
+
end
|
191
|
+
|
192
|
+
# Now we should be able to construct a point. Test that it behaves like
|
193
|
+
# we defined above:
|
194
|
+
|
195
|
+
it_behaves_like "Point2DInt", x: source[0], y: source[1]
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
describe "From a Hamster::Hash[x:, y:]" do
|
200
|
+
# ------------------------------------------------------------------------
|
201
|
+
#
|
202
|
+
# All that `#initialize` cares about is that it can access the `source`
|
203
|
+
# via `#[]` and that it can tell if it should use names or integer
|
204
|
+
# index by looking for `#each_pair` and `#each_index`, respectively.
|
205
|
+
#
|
206
|
+
|
207
|
+
# Let the `source` be the `Hamster::Hash[x: 1, y: 2]`.
|
208
|
+
|
209
|
+
def self.source
|
210
|
+
Hamster::Hash[x: 1, y: 2]
|
211
|
+
end
|
212
|
+
|
213
|
+
# Now we should be able to construct a point. Test that it behaves like
|
214
|
+
# we defined above:
|
215
|
+
|
216
|
+
it_behaves_like "Point2DInt", source.to_h
|
217
|
+
|
218
|
+
end
|
219
|
+
end # section Creating a Point
|
220
|
+
# ************************************************************************
|
221
|
+
|
222
|
+
|
223
|
+
describe_section "Deriving a new point from another" do
|
224
|
+
# ========================================================================
|
225
|
+
#
|
226
|
+
# Our point instances are immutable, but we can use {Hamster::Vector}'s
|
227
|
+
# methods for deriving new instances to derive new points.
|
228
|
+
#
|
229
|
+
|
230
|
+
# Let's create a point subject to start off with
|
231
|
+
|
232
|
+
subject do
|
233
|
+
point_2d_int.new x: 1, y: 2
|
234
|
+
end
|
235
|
+
|
236
|
+
# and we can play around with a few {Hamster::Vector} methods...
|
237
|
+
|
238
|
+
describe_method :put do
|
239
|
+
|
240
|
+
# Change the value at entry 0 to 2
|
241
|
+
|
242
|
+
describe_called_with 0, 2 do
|
243
|
+
it_behaves_like "Point2DInt", x: 2, y: 2
|
244
|
+
end # called with 0, 2
|
245
|
+
|
246
|
+
# Type checking should still be in effect, of course
|
247
|
+
|
248
|
+
describe_called_with 0, 'hey' do
|
249
|
+
it { expect { subject }.to raise_error TypeError }
|
250
|
+
end # called with 0, 'hey'
|
251
|
+
|
252
|
+
end # Method put Description
|
253
|
+
|
254
|
+
|
255
|
+
describe_method :map do
|
256
|
+
|
257
|
+
# We have to do some manual funkiness here 'cause
|
258
|
+
# `describe_called_with` doesn't take a block...
|
259
|
+
#
|
260
|
+
# TODO We should be able to handle this by passing a {NRSER::Message}
|
261
|
+
# to `describe_called_with`, but that's not yet implemented
|
262
|
+
# (RSpex need s a *lot* of work).
|
263
|
+
#
|
264
|
+
# describe_called_with(
|
265
|
+
# NRSER::Message.new { |value| value + 1 }
|
266
|
+
# ) do ...
|
267
|
+
#
|
268
|
+
# or something like that.
|
269
|
+
|
270
|
+
describe "add 1 to each entry" do
|
271
|
+
subject do
|
272
|
+
super().call { |value| value + 1 }
|
273
|
+
end
|
274
|
+
|
275
|
+
it_behaves_like "Point2DInt", x: 2, y: 3
|
276
|
+
end
|
277
|
+
|
278
|
+
# Type checking should still be enforced
|
279
|
+
|
280
|
+
describe "try to turn entries into Strings" do
|
281
|
+
subject do
|
282
|
+
super().call { |value| "value: #{ value }" }
|
283
|
+
end
|
284
|
+
|
285
|
+
it do
|
286
|
+
expect { subject }.to raise_error TypeError
|
287
|
+
end
|
288
|
+
end # "try to turn entries into Strings"
|
289
|
+
|
290
|
+
end # Method map Description
|
291
|
+
|
292
|
+
end # section Deriving a new point from another
|
293
|
+
# ************************************************************************
|
294
|
+
|
295
|
+
end # Simple 2D Integer Point
|
296
|
+
end
|