key_value_coding 0.1.0 → 0.2.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: 7d930f48bc39ac156387930313364ad48abd30dc75720025cf6b13f6d57c1f1b
4
- data.tar.gz: 2e9327e5880a0725081d2a7ca1566c1d0ced487d7664d31e889c7361506e503e
3
+ metadata.gz: 24584f6b8d22bd6093300ce6b4d69ee97265d21127c4bc939e696979cb951d4f
4
+ data.tar.gz: 0bfdff95bc24f469ea8805d87cd147ce1f6c9a6f0761630cd077daf655bab786
5
5
  SHA512:
6
- metadata.gz: 1acec129c687cde176832bd4800526200f271a6d939128e0c28b4ad5050bcde81fa813b7e496f57f28f76d367b0958df87b7ef1c65658a8027734fa6a346ef54
7
- data.tar.gz: 67a7dd6162051bce818add934625e74149b7fc99c2be70fc0fdac0a5b2073a18597939ab7d55102a9b294a69da899d79260d15d715b7a2408e18548e5ab1ef8e
6
+ metadata.gz: c5fa4a4c31d0fe521a08baca2abd5ad188e5112b4db3980e6ad3d078bc1797c5e639d7fe0d57804d9017ba9f787e74349de0a4d7aae5f065f718ccf503b405b5
7
+ data.tar.gz: a3272ffe2dea7c57108c78002bcf84bc3cb06114dc6e2f69b19f7c6332f0ca627d76a492b436057f1dd6bd7d794b1cac55fd53890e47074079f9a638d84f2f94
data/CHANGELOG.md CHANGED
@@ -1,4 +1,6 @@
1
- ## [Unreleased]
1
+ ## [0.2.0] - 2026-02-02
2
+
3
+ - Support symbols as key paths
2
4
 
3
5
  ## [0.1.0] - 2026-02-02
4
6
 
data/README.md CHANGED
@@ -37,37 +37,35 @@ object.value_for_key(:company) # Works with both!
37
37
  ```ruby
38
38
  company.address[:street] # Meh
39
39
  data.dig(:company, :address).street # Ugh
40
- data.value_for_key_path("company.address.street") # Nice!
40
+ data.value_for_key_path(:company, :address :street) # Also nice!
41
41
  ```
42
42
 
43
43
  ### Operating on multiple collection items at once
44
44
 
45
45
  ```ruby
46
46
  company.managers.map { it[:name] }.map(&:upcase) # Meh
47
- company.value_for_key_path("name.upcase") # Nice!
47
+ company.value_for_key_path(:name, :upcase) # Nice!
48
48
  ```
49
49
 
50
50
  ### Set a value on a mixed hierarchy of PORO and hashes
51
51
 
52
52
  ```ruby
53
- record.company?.address?[:street] = "Ash Tree Lane 12" # Meh
54
- record.set_value("Ash Tree Lane 12" for_key_path: "company.address.street") # Nice!
53
+ record.company.address[:street] = "Ash Tree Lane 12" # Meh
54
+ record.set_value("Ash Tree Lane 12" for_key_path: [:company, :address, :street]) # Nice!
55
55
  ```
56
56
 
57
57
  ## Installation
58
58
 
59
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
60
-
61
59
  Install the gem and add to the application's Gemfile by executing:
62
60
 
63
61
  ```bash
64
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
62
+ bundle add key_value_coding
65
63
  ```
66
64
 
67
65
  If bundler is not being used to manage dependencies, install the gem by executing:
68
66
 
69
67
  ```bash
70
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
68
+ gem install key_value_coding
71
69
  ```
72
70
 
73
71
  ## Usage
@@ -111,12 +109,13 @@ If one of the keys doesn't exist, `value_for_undefined_key` is called (which usu
111
109
  ### `Object#value_for_key_path`
112
110
 
113
111
  ```ruby
114
- manager.value_for_key_path("name.length") # 10
112
+ manager.value_for_key_path(:name, :length) # 10
113
+ manager.value_for_key_path("name.length") # Also works with dot-separated components
115
114
  ```
116
115
 
117
116
  Get the value of a chain of keys (like object traversal, but works also on hashes).
118
117
 
119
- For each dot-separated component of the key-path, `value_for_key` is used to retrieve the value.
118
+ For each symbol or dot-separated component of the key-path, `value_for_key` is used to retrieve the value.
120
119
 
121
120
  If one of the keys doesn't exist, `value_for_undefined_key` is called (which usually results in a `KeyValueCoding::UndefinedKey` exception).
122
121
 
@@ -133,12 +132,13 @@ If the key doesn't exist, `value_for_undefined_key` is called (which usually res
133
132
  ### `Object#set_value(value, for_key_path:)`
134
133
 
135
134
  ```ruby
136
- record.set_value("Richard Whitman", for_key_path: "manager.name")
135
+ record.set_value("Richard Whitman", for_key_path: [:manager, :name])
136
+ record.set_value("Richard Whitman", for_key_path: "manager.name") # Also works with dot-separated components
137
137
  ```
138
138
 
139
139
  Set the value of a key-path (like object assignement, but also work on hashes).
140
140
 
141
- For each dot-separated component of the key-path, `value_for_key` is invoked to retrieve the value. On the last component, `set_value(value, for_key:)` is invoked to set the value.
141
+ For each symbol or dot-separated component of the key-path, `value_for_key` is invoked to retrieve the value. On the last component, `set_value(value, for_key:)` is invoked to set the value.
142
142
 
143
143
  If one of the keys doesn't exist, `value_for_undefined_key` is called (which usually results in a `KeyValueCoding::UndefinedKey` exception).
144
144
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "debug"
4
-
5
3
  class Hash
6
4
  # Allow both symbol and strings, for ease of use
7
5
  def value_for_key(key)
@@ -17,17 +17,15 @@ class Object
17
17
  keys.map { |k| [k, value_for_key(k)] }.to_h
18
18
  end
19
19
 
20
- def value_for_key_path(key_path)
21
- key_path
22
- .split(".")
23
- .reduce(self) { |object, key| object.value_for_key(key) }
20
+ def value_for_key_path(*key_path, **nil)
21
+ normalized_key_path(*key_path).reduce(self) { |object, key| object.value_for_key(key) }
24
22
  end
25
23
 
26
24
  def set_value(value, for_key: nil, for_key_path: nil)
27
25
  if for_key
28
26
  set_value_for_key(value, for_key)
29
27
  elsif for_key_path
30
- set_value_for_key_path(value, for_key_path)
28
+ set_value_for_key_path(value, *for_key_path)
31
29
  else
32
30
  raise ArgumentError("Either `for_key` or `for_key_path` must be provided")
33
31
  end
@@ -43,8 +41,8 @@ class Object
43
41
  end
44
42
  end
45
43
 
46
- def set_value_for_key_path(value, key_path)
47
- *path, key = key_path.split(".")
44
+ def set_value_for_key_path(value, *key_path)
45
+ *path, key = normalized_key_path(*key_path)
48
46
  target = value_for_key_path(path.join('.'))
49
47
  target.set_value(value, for_key: key)
50
48
  end
@@ -56,4 +54,15 @@ class Object
56
54
  def set_value_for_undefined_key(value, key)
57
55
  raise KeyValueCoding::UndefinedKey.new("Undefined key '#{key}' for object #{self}")
58
56
  end
57
+
58
+ private
59
+
60
+ def normalized_key_path(*key_path)
61
+ case key_path
62
+ in [String] then key_path.first.split(".")
63
+ in [Symbol] then key_path
64
+ in [Symbol, *] if key_path.all? { |k| k.is_a?(Symbol)} then key_path
65
+ else raise KeyValueCoding::InvalidPath.new("Expected a string or an array of symbols, but got '#{key_path}'")
66
+ end
67
+ end
59
68
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KeyValueCoding
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -7,4 +7,5 @@ require_relative "key_value_coding/hash"
7
7
 
8
8
  module KeyValueCoding
9
9
  class UndefinedKey < StandardError; end
10
+ class InvalidPath < StandardError; end
10
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: key_value_coding
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pierre de La Morinerie