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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b64e7bb0ef88ead17af2b5d1bca5ea4dc3a19c566a441ee8fe519afe7ac47c3
4
- data.tar.gz: 4dedaf9317e816780dc86427b99a2b070a1ec46da22ca56446e8ceb120ba615a
3
+ metadata.gz: 4ecf8356b446062c2cc5eb9237759cb91eb8a91cba3f311b527f2c73aa675bd7
4
+ data.tar.gz: 2f6c6952068ff100208296f0715d7f8b444b34deb673c285c3fe24d34be38bf8
5
5
  SHA512:
6
- metadata.gz: '0029d88bfa9788f39948eacc0707af394d2702b31800ec86ef83e76f9e1b4dbeb672ce619877a5f0e9f09da93b9b1edcdf3a0dc471c84b73fd1fd8415f168a6e'
7
- data.tar.gz: abd71b228155f72f7953868cd445722c013b3ce9067d9ecc2075cd5c518a12905a0cd3ed768d7adec505291b53f6f383ce0fc6b63e2b1181ff0887f2fdf7c09c
6
+ metadata.gz: e71dff987fe8ee9853348389d6d1f262bda8355e88ef0dd8847585ce803adeae1de65c2153df6904bf4727fd32f4baddc3cef72bdd6e6b973126927938a07a9e
7
+ data.tar.gz: daf98517ec14374466755c6075a5ee2b009f63cb245f213b39d39c98915ff8597ba6a1ba7dfa961a1ada6785bde8720e907a19fa6dfda8b405ae7e7e893b8885
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in {gemname}.gemspec
4
4
  gemspec
5
5
 
6
- gem "rubocop", "~> 0.87.1"
6
+ gem "rubocop", "~> 1.20"
data/Makefile CHANGED
@@ -1,10 +1,11 @@
1
- default: test
1
+ default: test rubocop
2
2
 
3
- .PHONY: test
3
+ .PHONY: test rubocop release
4
4
 
5
5
  test:
6
- ruby test/immutable_test.rb
7
- ruby test/immutable_w_null_record_test.rb
6
+ scripts/test
7
+
8
+ rubocop:
8
9
  bin/rubocop
9
10
 
10
11
  release:
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.6
1
+ 1.1.0
@@ -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, null_record: null_record, max_depth: max_depth - 1 }
17
+ object.map { |obj| create obj, fallback: fallback, max_depth: max_depth - 1 }
15
18
  when Hash
16
- new(object, null_record)
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
- null_record = immutable.instance_variable_get :@null_record
29
+ fallback = immutable.instance_variable_get :@fallback
27
30
 
28
- if null_record
29
- null_record.merge(hsh)
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
- @hsh.as_json(opts)
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, null_record = nil)
51
+ def initialize(hsh, fallback = nil)
48
52
  @hsh = hsh
49
- @null_record = null_record
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
- if args.empty? && !block
68
- value = SELF.fetch_symbol_or_string_from_hash(@hsh, sym) do
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
- return SELF.create(value)
76
- end
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
- super
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
- "<#{self.class.name}: #{@hsh.inspect}>"
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
- @hsh.key?(method_name.to_sym) ||
89
- @hsh.key?(method_name.to_s) ||
90
- super
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
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ Dir.glob("test/**/*_test.rb").sort.each do |file|
3
+ load file
4
+ end
@@ -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(NoMethodError) do
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 NoMethodError when called with arguments
77
- assert_raise(NoMethodError) do
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 NoMethodError when called with a block argument
82
- assert_raise(NoMethodError) do
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[:children], Immutable.raw_data(immutable.children))
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::WithNullRecordTestCase < Test::Unit::TestCase
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 null_record
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, null_record: null_record
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 test_null_record_access
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(NoMethodError) do
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 null_record
60
- expected = hsh.merge(null_record)
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.6
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-07-13 00:00:00.000000000 Z
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/immutable_w_null_record_test.rb
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/immutable_w_null_record_test.rb
62
+ - test/immutable_w_fallback_test.rb