simple-immutable 1.0.6 → 1.1.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/Gemfile +1 -1
- data/Makefile +5 -4
- data/README.md +13 -2
- data/VERSION +1 -1
- data/lib/simple/immutable.rb +54 -23
- data/scripts/test +4 -0
- data/test/immutable_test.rb +27 -8
- data/test/{immutable_w_null_record_test.rb → immutable_w_fallback_test.rb} +9 -9
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ecf8356b446062c2cc5eb9237759cb91eb8a91cba3f311b527f2c73aa675bd7
|
4
|
+
data.tar.gz: 2f6c6952068ff100208296f0715d7f8b444b34deb673c285c3fe24d34be38bf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e71dff987fe8ee9853348389d6d1f262bda8355e88ef0dd8847585ce803adeae1de65c2153df6904bf4727fd32f4baddc3cef72bdd6e6b973126927938a07a9e
|
7
|
+
data.tar.gz: daf98517ec14374466755c6075a5ee2b009f63cb245f213b39d39c98915ff8597ba6a1ba7dfa961a1ada6785bde8720e907a19fa6dfda8b405ae7e7e893b8885
|
data/Gemfile
CHANGED
data/Makefile
CHANGED
data/README.md
CHANGED
@@ -17,11 +17,11 @@ Turns a nested data structure into a immutable ruby object implementing dot and
|
|
17
17
|
name: "grandchildname"
|
18
18
|
}
|
19
19
|
},
|
20
|
-
"children"
|
20
|
+
"children" => [
|
21
21
|
"anna",
|
22
22
|
"arthur",
|
23
23
|
{
|
24
|
-
action
|
24
|
+
"action" => {
|
25
25
|
keep_your_mouth_shut: true
|
26
26
|
}
|
27
27
|
}
|
@@ -36,3 +36,14 @@ Turns a nested data structure into a immutable ruby object implementing dot and
|
|
36
36
|
imm.children.foo # NoMethodError (undefined method `foo' for #<Array:0x00007fb90f1be390>)
|
37
37
|
imm.children.first.foo # NoMethodError (undefined method `foo' for "anna":String)
|
38
38
|
|
39
|
+
If an attribute is not defined in the immutable (or in any sub-immutable) we raise a NameError
|
40
|
+
|
41
|
+
imm.unknown # NameError (unknown immutable attribute 'unknown')
|
42
|
+
|
43
|
+
One can check for an attribute by appending a "?". If the attribute is not defined this returns +nil+ instead of raising an error.
|
44
|
+
|
45
|
+
imm.a? # 'a-value'
|
46
|
+
imm.unknown? # nil
|
47
|
+
imm.foo?.bar? # also nil
|
48
|
+
|
49
|
+
When building an immutable one can provide a "fallback" hash in addition to the returned hash. If a key does not exist in the passed in data it will be fetched from the "fallback" data. This is rarely useful, but can provide effectove optimization in some cases.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0
|
1
|
+
1.1.0
|
data/lib/simple/immutable.rb
CHANGED
@@ -6,14 +6,17 @@ class Simple::Immutable
|
|
6
6
|
|
7
7
|
# turns an object, which can be a hash or array of hashes, arrays, and scalars
|
8
8
|
# into an object which you can use to access with dot methods.
|
9
|
-
def self.create(object, max_depth: 8, null_record: nil)
|
9
|
+
def self.create(object, max_depth: 8, null_record: nil, fallback: nil)
|
10
|
+
# Note that null_record is deprecated.
|
11
|
+
fallback ||= null_record
|
12
|
+
|
10
13
|
case object
|
11
14
|
when Array
|
12
15
|
raise ArgumentError, "Object nested too deep (or inner loop?)" if max_depth < 0
|
13
16
|
|
14
|
-
object.map { |obj| create obj,
|
17
|
+
object.map { |obj| create obj, fallback: fallback, max_depth: max_depth - 1 }
|
15
18
|
when Hash
|
16
|
-
new(object,
|
19
|
+
new(object, fallback)
|
17
20
|
else
|
18
21
|
object
|
19
22
|
end
|
@@ -23,10 +26,10 @@ class Simple::Immutable
|
|
23
26
|
case immutable
|
24
27
|
when SELF
|
25
28
|
hsh = immutable.instance_variable_get :@hsh
|
26
|
-
|
29
|
+
fallback = immutable.instance_variable_get :@fallback
|
27
30
|
|
28
|
-
if
|
29
|
-
|
31
|
+
if fallback
|
32
|
+
fallback.merge(hsh)
|
30
33
|
else
|
31
34
|
hsh
|
32
35
|
end
|
@@ -39,14 +42,15 @@ class Simple::Immutable
|
|
39
42
|
|
40
43
|
# adds to_json support.
|
41
44
|
def as_json(opts)
|
42
|
-
|
45
|
+
data = SELF.raw_data(self)
|
46
|
+
data.as_json(opts)
|
43
47
|
end
|
44
48
|
|
45
49
|
private
|
46
50
|
|
47
|
-
def initialize(hsh,
|
51
|
+
def initialize(hsh, fallback = nil)
|
48
52
|
@hsh = hsh
|
49
|
-
@
|
53
|
+
@fallback = fallback
|
50
54
|
end
|
51
55
|
|
52
56
|
# rubycop:disable Lint/IneffectiveAccessModifier
|
@@ -64,32 +68,59 @@ class Simple::Immutable
|
|
64
68
|
end
|
65
69
|
|
66
70
|
def method_missing(sym, *args, &block)
|
67
|
-
|
68
|
-
|
69
|
-
SELF.fetch_symbol_or_string_from_hash(@null_record, sym) do
|
70
|
-
# STDERR.puts "Missing attribute #{sym} for Immutable w/#{@hsh.inspect}"
|
71
|
-
super
|
72
|
-
end
|
73
|
-
end
|
71
|
+
raise ArgumentError, "Immutable: called attribute method with arguments" unless args.empty?
|
72
|
+
raise ArgumentError, "Immutable: called attribute method with arguments" if block
|
74
73
|
|
75
|
-
|
76
|
-
|
74
|
+
if sym.end_with?("?")
|
75
|
+
# If the symbol ends in "?" we do not raise a NameError, but instead
|
76
|
+
# returns nil if the attribute is not known.
|
77
|
+
sym = sym[0...-1]
|
77
78
|
|
78
|
-
|
79
|
+
# Note that sym is now a String. However, we are String/Symbol agnostic
|
80
|
+
# (in fetch_symbol_or_string_from_hash), so this is ok.
|
81
|
+
fetch_attribute!(sym, raise_when_missing: false)
|
82
|
+
else
|
83
|
+
fetch_attribute!(sym, raise_when_missing: true)
|
84
|
+
end
|
79
85
|
end
|
80
86
|
|
81
87
|
public
|
82
88
|
|
89
|
+
def fetch_attribute!(sym, raise_when_missing:)
|
90
|
+
value = SELF.fetch_symbol_or_string_from_hash(@hsh, sym) do
|
91
|
+
SELF.fetch_symbol_or_string_from_hash(@fallback, sym) do
|
92
|
+
raise NameError, "unknown immutable attribute '#{sym}'" if raise_when_missing
|
93
|
+
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
SELF.create(value)
|
99
|
+
end
|
100
|
+
|
83
101
|
def inspect
|
84
|
-
|
102
|
+
if @fallback
|
103
|
+
"<#{self.class.name}: #{@hsh.inspect}, w/fallback: #{@fallback.inspect}>"
|
104
|
+
else
|
105
|
+
"<#{self.class.name}: #{@hsh.inspect}>"
|
106
|
+
end
|
85
107
|
end
|
86
108
|
|
87
109
|
def respond_to_missing?(method_name, include_private = false)
|
88
|
-
|
89
|
-
|
90
|
-
|
110
|
+
return true if method_name.end_with?("?")
|
111
|
+
|
112
|
+
key = method_name.to_sym
|
113
|
+
return true if @hsh.key?(key)
|
114
|
+
return true if @fallback&.key?(key)
|
115
|
+
|
116
|
+
key = method_name.to_s
|
117
|
+
return true if @hsh.key?(key)
|
118
|
+
return true if @fallback&.key?(key)
|
119
|
+
|
120
|
+
super
|
91
121
|
end
|
92
122
|
|
123
|
+
# rubocop:disable Style/OptionalBooleanParameter
|
93
124
|
def respond_to?(sym, include_all = false)
|
94
125
|
super || @hsh.key?(sym.to_s) || @hsh.key?(sym.to_sym)
|
95
126
|
end
|
data/scripts/test
ADDED
data/test/immutable_test.rb
CHANGED
@@ -16,11 +16,11 @@ class Simple::Immutable::TestCase < Test::Unit::TestCase
|
|
16
16
|
name: "grandchildname"
|
17
17
|
}
|
18
18
|
},
|
19
|
-
"children"
|
19
|
+
"children" => [
|
20
20
|
"anna",
|
21
21
|
"arthur",
|
22
22
|
{
|
23
|
-
action
|
23
|
+
"action" => {
|
24
24
|
keep_your_mouth_shut: true
|
25
25
|
}
|
26
26
|
}
|
@@ -60,6 +60,22 @@ class Simple::Immutable::TestCase < Test::Unit::TestCase
|
|
60
60
|
assert_equal true, immutable.children[2].action.keep_your_mouth_shut
|
61
61
|
end
|
62
62
|
|
63
|
+
def test_child_access_w_question_mark
|
64
|
+
child = immutable.child?
|
65
|
+
assert_kind_of(Immutable, child)
|
66
|
+
assert_equal "childname", immutable.child.name?
|
67
|
+
assert_equal "grandchildname", immutable.child?.grandchild?.name?
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_array_access_w_question_mark
|
71
|
+
assert_kind_of(Array, immutable.children?)
|
72
|
+
assert_equal 3, immutable.children?.length
|
73
|
+
assert_equal "anna", immutable.children?[0]
|
74
|
+
|
75
|
+
assert_kind_of(Immutable, immutable.children?[2])
|
76
|
+
assert_equal true, immutable.children[2].action?.keep_your_mouth_shut?
|
77
|
+
end
|
78
|
+
|
63
79
|
def test_base_class
|
64
80
|
assert_nothing_raised do
|
65
81
|
immutable.object_id
|
@@ -67,19 +83,22 @@ class Simple::Immutable::TestCase < Test::Unit::TestCase
|
|
67
83
|
end
|
68
84
|
|
69
85
|
def test_missing_keys
|
70
|
-
assert_raise(
|
86
|
+
assert_raise(NameError) do
|
71
87
|
immutable.foo
|
72
88
|
end
|
73
89
|
end
|
74
90
|
|
75
91
|
def test_skip_when_args_or_block
|
76
|
-
# raise
|
77
|
-
assert_raise(
|
92
|
+
# raise ArgumentError when called with arguments
|
93
|
+
assert_raise(ArgumentError) do
|
78
94
|
immutable.a(1, 2, 3)
|
79
95
|
end
|
96
|
+
assert_raise(ArgumentError) do
|
97
|
+
immutable.a(one: 1)
|
98
|
+
end
|
80
99
|
|
81
|
-
# raise
|
82
|
-
assert_raise(
|
100
|
+
# raise ArgumentError when called with a block argument
|
101
|
+
assert_raise(ArgumentError) do
|
83
102
|
immutable.a { :dummy }
|
84
103
|
end
|
85
104
|
end
|
@@ -88,6 +107,6 @@ class Simple::Immutable::TestCase < Test::Unit::TestCase
|
|
88
107
|
expected = hsh
|
89
108
|
|
90
109
|
assert_equal(expected, Immutable.raw_data(immutable))
|
91
|
-
assert_equal(expected[
|
110
|
+
assert_equal(expected["children"], Immutable.raw_data(immutable.children))
|
92
111
|
end
|
93
112
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "test-unit"
|
4
4
|
require_relative "../lib/simple-immutable"
|
5
5
|
|
6
|
-
class Simple::Immutable::
|
6
|
+
class Simple::Immutable::WithFallbackTestCase < Test::Unit::TestCase
|
7
7
|
Immutable = ::Simple::Immutable
|
8
8
|
|
9
9
|
def hsh
|
@@ -16,11 +16,11 @@ class Simple::Immutable::WithNullRecordTestCase < Test::Unit::TestCase
|
|
16
16
|
name: "grandchildname"
|
17
17
|
}
|
18
18
|
},
|
19
|
-
"children"
|
19
|
+
"children" => [
|
20
20
|
"anna",
|
21
21
|
"arthur",
|
22
22
|
{
|
23
|
-
action
|
23
|
+
"action" => {
|
24
24
|
keep_your_mouth_shut: true
|
25
25
|
}
|
26
26
|
}
|
@@ -28,7 +28,7 @@ class Simple::Immutable::WithNullRecordTestCase < Test::Unit::TestCase
|
|
28
28
|
}
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
31
|
+
def fallback
|
32
32
|
{
|
33
33
|
foo: "foo-value",
|
34
34
|
"bar" => "bar-value"
|
@@ -36,7 +36,7 @@ class Simple::Immutable::WithNullRecordTestCase < Test::Unit::TestCase
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def immutable
|
39
|
-
Immutable.create hsh,
|
39
|
+
Immutable.create hsh, fallback: fallback
|
40
40
|
end
|
41
41
|
|
42
42
|
def test_hash_access
|
@@ -44,20 +44,20 @@ class Simple::Immutable::WithNullRecordTestCase < Test::Unit::TestCase
|
|
44
44
|
assert_equal "b-value", immutable.b
|
45
45
|
end
|
46
46
|
|
47
|
-
def
|
47
|
+
def test_fallback_access
|
48
48
|
assert_equal "foo-value", immutable.foo
|
49
49
|
assert_equal "bar-value", immutable.bar
|
50
50
|
end
|
51
51
|
|
52
52
|
def test_missing_keys
|
53
|
-
assert_raise(
|
53
|
+
assert_raise(NameError) do
|
54
54
|
immutable.unknown
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
58
|
def test_raw_data
|
59
|
-
# raw data does contain
|
60
|
-
expected = hsh.merge(
|
59
|
+
# raw data does contain fallback
|
60
|
+
expected = hsh.merge(fallback)
|
61
61
|
assert_equal(expected, Immutable.raw_data(immutable))
|
62
62
|
end
|
63
63
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple-immutable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- radiospiel
|
8
8
|
- mediapeers GmbH
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-10-06 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Immutable ruby objects implementing dot and [] accessors.
|
15
15
|
email: eno@radiospiel.org
|
@@ -31,13 +31,14 @@ files:
|
|
31
31
|
- lib/simple/immutable.rb
|
32
32
|
- scripts/release
|
33
33
|
- scripts/release.rb
|
34
|
+
- scripts/test
|
34
35
|
- simple-immutable.gemspec
|
35
36
|
- test/immutable_test.rb
|
36
|
-
- test/
|
37
|
+
- test/immutable_w_fallback_test.rb
|
37
38
|
homepage: http://github.com/radiospiel/simple-immutable
|
38
39
|
licenses: []
|
39
40
|
metadata: {}
|
40
|
-
post_install_message:
|
41
|
+
post_install_message:
|
41
42
|
rdoc_options: []
|
42
43
|
require_paths:
|
43
44
|
- lib
|
@@ -53,9 +54,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
54
|
version: '0'
|
54
55
|
requirements: []
|
55
56
|
rubygems_version: 3.1.4
|
56
|
-
signing_key:
|
57
|
+
signing_key:
|
57
58
|
specification_version: 4
|
58
59
|
summary: Immutable ruby objects
|
59
60
|
test_files:
|
60
61
|
- test/immutable_test.rb
|
61
|
-
- test/
|
62
|
+
- test/immutable_w_fallback_test.rb
|