json_path_attribute 0.1.1
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 +7 -0
- data/.rubocop.yml +16 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +86 -0
- data/Rakefile +12 -0
- data/lib/json_path_attribute/attribute_definition.rb +16 -0
- data/lib/json_path_attribute/type.rb +55 -0
- data/lib/json_path_attribute/version.rb +5 -0
- data/lib/json_path_attribute.rb +99 -0
- data/sig/path_attribute.rbs +4 -0
- metadata +99 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e2b74d1b64eefe49f524f7204997ba6879177092ecaa50dd1f0a39ee1a5e1f9c
|
|
4
|
+
data.tar.gz: 5d27f669c336f92ff6144cd863175bbbfe139cf3c738a2fb2778157a48deebf0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 807da0b92649d4274798f2ea5d651e62533ef3e2b97866f5c3630ec9c13f4751ab9f8c5f2bc896f39761a72ab906ff34872880684b3000a7562dfe5024f52d62
|
|
7
|
+
data.tar.gz: 7c1753866d8d7583098d3c1a65a3ea2e5c2d36641da3864d1ce9c0382f6af2a0bbc0c51d2675675dec6d768254f05c587513fff191b0ef199992518e801f05cf
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-minitest
|
|
3
|
+
- rubocop-rake
|
|
4
|
+
|
|
5
|
+
AllCops:
|
|
6
|
+
TargetRubyVersion: 3.1
|
|
7
|
+
NewCops: enable
|
|
8
|
+
Exclude:
|
|
9
|
+
- 'test/**/*'
|
|
10
|
+
- 'vendor/**/*'
|
|
11
|
+
|
|
12
|
+
Style/StringLiterals:
|
|
13
|
+
EnforcedStyle: double_quotes
|
|
14
|
+
|
|
15
|
+
Style/StringLiteralsInInterpolation:
|
|
16
|
+
EnforcedStyle: double_quotes
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Beequip B.V.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# JsonPathAttribute
|
|
2
|
+
|
|
3
|
+
JsonPathAttribute is a simple but powerful object mapper to map JSON or Hash data into Ruby objects, using [JsonPath](https://github.com/joshbuddy/jsonpath).
|
|
4
|
+
|
|
5
|
+
Although this gem has been released fairly recently, it was originally developed for [Beequip](http://beequip.com) and a very similar version has been used for a few years.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
Given a JSON (or Ruby hash) like this:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"post": {
|
|
14
|
+
"title": "How to drive on snow?",
|
|
15
|
+
"body": "Just use a low gear and slowly build up speed",
|
|
16
|
+
"likes": 12,
|
|
17
|
+
"comments": [
|
|
18
|
+
{
|
|
19
|
+
"body": "Thank you for the tip! It is very useful.",
|
|
20
|
+
"user": {
|
|
21
|
+
"name": "Charles Careful"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You can include `JsonPathAttribute` to a Ruby class:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
class Post
|
|
33
|
+
include JsonPathAttribute
|
|
34
|
+
|
|
35
|
+
json_path_attribute :title, path: 'post.title'
|
|
36
|
+
json_path_attribute :likes, path: 'post.likes', type: :integer
|
|
37
|
+
json_path_attribute :comments, path: 'post.comments[*]', type: Comment, array: true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Comment
|
|
41
|
+
include JsonPathAttribute
|
|
42
|
+
|
|
43
|
+
json_path_attribute :body, path: 'body'
|
|
44
|
+
json_path_attribute :name, path: 'user.name'
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
And it gets parsed like this
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
post = Post.parse(json) # => <Post:...>
|
|
52
|
+
post.title # => "How to drive on snow?"
|
|
53
|
+
post.body # => "Just use a low gear and slowly build up speed"
|
|
54
|
+
post.likes # => 12
|
|
55
|
+
post.comments # => [#<Comment:0x000000011e4354d0 @body="Thank you for the tip! It is very useful.", @name="Charles Careful">]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
bundle add json_path_attribute
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
gem install json_path_attribute
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Development
|
|
73
|
+
|
|
74
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
75
|
+
|
|
76
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
77
|
+
|
|
78
|
+
## Contributing
|
|
79
|
+
|
|
80
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/BEEQUIP/json_path_attribute.
|
|
81
|
+
|
|
82
|
+
## Acknowledgements
|
|
83
|
+
|
|
84
|
+
Thanks to the original authors [jdongelmans](https://github.com/jdongelmans) and [jandintel](https://github.com/jandintel).
|
|
85
|
+
|
|
86
|
+
Also thanks to [joshbuddy](https://github.com/joshbuddy) for creating [JsonPath](https://github.com/joshbuddy/jsonpath).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JsonPathAttribute
|
|
4
|
+
AttributeDefinition = Data.define(:type, :path_string, :array) do
|
|
5
|
+
def array? = !!array
|
|
6
|
+
def path = path_string.include?(" ") ? "'#{path_string}'" : path_string
|
|
7
|
+
def full_path = "$.#{path}"
|
|
8
|
+
|
|
9
|
+
def value(parsed)
|
|
10
|
+
value = JsonPath.on(parsed, full_path)
|
|
11
|
+
value = value.first unless full_path.include?("[*]") && value.present?
|
|
12
|
+
value = [] if value.nil? && array?
|
|
13
|
+
value
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_model"
|
|
4
|
+
|
|
5
|
+
module JsonPathAttribute
|
|
6
|
+
# Provides a way to cast values to a specific type
|
|
7
|
+
# This class is intended to be used internally by JsonPathAttribute
|
|
8
|
+
class Type
|
|
9
|
+
class << self
|
|
10
|
+
def cast_attribute(type, value, array: false)
|
|
11
|
+
return value if type == :source
|
|
12
|
+
return cast_object_attribute(type, value, array: array) if type.is_a?(Class)
|
|
13
|
+
|
|
14
|
+
cast_to(type, value, array: array)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def cast_to(type, value, array: false)
|
|
18
|
+
return [] if value.nil? && array
|
|
19
|
+
return false if value.nil? && type == :boolean
|
|
20
|
+
|
|
21
|
+
cast_type = ActiveModel::Type.lookup(type)
|
|
22
|
+
|
|
23
|
+
if array
|
|
24
|
+
cast_array(value, type, cast_type)
|
|
25
|
+
else
|
|
26
|
+
cast_value(value, type, cast_type)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def cast_array(values, type, cast_type)
|
|
31
|
+
values.map do |value|
|
|
32
|
+
next false if value.nil? && type == :boolean
|
|
33
|
+
|
|
34
|
+
cast_type.cast(value)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def cast_value(value, type, cast_type)
|
|
39
|
+
value = value.to_s if type == :decimal && value.is_a?(Float)
|
|
40
|
+
|
|
41
|
+
cast_type.cast(value)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def cast_object_attribute(type, value, array:)
|
|
45
|
+
raise ArgumentError, "Unable to cast #{value.inspect} to #{type} object" if value.nil?
|
|
46
|
+
|
|
47
|
+
if array
|
|
48
|
+
value.map { |element| type.parse(element) }
|
|
49
|
+
else
|
|
50
|
+
type.parse(value)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "jsonpath"
|
|
5
|
+
require "active_support/core_ext/hash/keys"
|
|
6
|
+
|
|
7
|
+
require_relative "json_path_attribute/attribute_definition"
|
|
8
|
+
require_relative "json_path_attribute/type"
|
|
9
|
+
require_relative "json_path_attribute/version"
|
|
10
|
+
|
|
11
|
+
# Include this module
|
|
12
|
+
# class Post
|
|
13
|
+
# include JsonPathAttribute
|
|
14
|
+
#
|
|
15
|
+
# json_path_attribute:title, path: 'title'
|
|
16
|
+
# end
|
|
17
|
+
module JsonPathAttribute
|
|
18
|
+
class Error < StandardError; end
|
|
19
|
+
class TypeCastError < Error; end
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# This module includes the class methods:
|
|
23
|
+
# .path_attribute
|
|
24
|
+
# .path_attribute_definitions
|
|
25
|
+
# .parse
|
|
26
|
+
# .parse_collection
|
|
27
|
+
# .parse_first
|
|
28
|
+
# See the respective methods for documentation
|
|
29
|
+
module ClassMethods
|
|
30
|
+
# This method is used to define an attribute expected at a specific path
|
|
31
|
+
# It also adds an instance variable with reader and setter method with the given name
|
|
32
|
+
# @name [Symbol] Also adds a reader and setter method with the given name
|
|
33
|
+
# @path [String] JSON Path
|
|
34
|
+
# @type [Symbol | Class] (optional) Casts the attribute to the defined type.
|
|
35
|
+
# This argument can be a symbol, like `:integer` or `:string`, or another class
|
|
36
|
+
# that includes JsonPathAttribute.
|
|
37
|
+
# @array [Boolean]
|
|
38
|
+
def json_path_attribute(name, path:, type: :source, array: false)
|
|
39
|
+
attribute_definitions[name] = AttributeDefinition.new(type: type, path_string: path, array: array)
|
|
40
|
+
|
|
41
|
+
define_method name do
|
|
42
|
+
instance_variable_get("@#{name}")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
define_method "#{name}=" do |value|
|
|
46
|
+
instance_variable_set("@#{name}", Type.cast_attribute(type, value, array: array))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def attribute_definitions
|
|
51
|
+
@attribute_definitions ||= {}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def parse(json_or_hash)
|
|
55
|
+
parsed = parse_json_hash_or_array(json_or_hash)
|
|
56
|
+
|
|
57
|
+
attributes = attribute_definitions.each_with_object({}) do |(name, definition), hash|
|
|
58
|
+
value = definition.value(parsed)
|
|
59
|
+
|
|
60
|
+
next if value.nil?
|
|
61
|
+
|
|
62
|
+
hash[name] = value
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
new(attributes.merge(response: parsed))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def parse_collection(json_array_or_array)
|
|
69
|
+
array = parse_json_hash_or_array(json_array_or_array)
|
|
70
|
+
array.map do |item|
|
|
71
|
+
parse(item)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parse_first(json_array_or_array)
|
|
76
|
+
array = parse_json_hash_or_array(json_array_or_array)
|
|
77
|
+
parse(array.first)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def parse_json_hash_or_array(json_hash_or_array)
|
|
83
|
+
json_hash_or_array = json_hash_or_array.deep_stringify_keys if json_hash_or_array.is_a?(Hash)
|
|
84
|
+
json_hash_or_array.is_a?(String) ? JSON.parse(json_hash_or_array) : json_hash_or_array
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
attr_accessor :response
|
|
89
|
+
|
|
90
|
+
def initialize(attributes = {})
|
|
91
|
+
attributes.each do |name, value|
|
|
92
|
+
public_send("#{name}=", value)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def self.included(base)
|
|
97
|
+
base.extend(ClassMethods)
|
|
98
|
+
end
|
|
99
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: json_path_attribute
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Daniël de Vries
|
|
8
|
+
- Jan van der Pas
|
|
9
|
+
- Marthyn Olthof
|
|
10
|
+
bindir: exe
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: activemodel
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - "~>"
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '7.2'
|
|
21
|
+
type: :runtime
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - "~>"
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: '7.2'
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: activesupport
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - "~>"
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '7.2'
|
|
35
|
+
type: :runtime
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - "~>"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '7.2'
|
|
42
|
+
- !ruby/object:Gem::Dependency
|
|
43
|
+
name: jsonpath
|
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - "~>"
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '1.1'
|
|
49
|
+
type: :runtime
|
|
50
|
+
prerelease: false
|
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - "~>"
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '1.1'
|
|
56
|
+
description: JsonPathAttribute is an object mapper to create Ruby objects from JSON
|
|
57
|
+
email:
|
|
58
|
+
- daniel.devries@beequip.nl
|
|
59
|
+
- jan.vanderpas@beequip.nl
|
|
60
|
+
- marthyn.olthof@beequip.nl
|
|
61
|
+
executables: []
|
|
62
|
+
extensions: []
|
|
63
|
+
extra_rdoc_files: []
|
|
64
|
+
files:
|
|
65
|
+
- ".rubocop.yml"
|
|
66
|
+
- CHANGELOG.md
|
|
67
|
+
- LICENSE.txt
|
|
68
|
+
- README.md
|
|
69
|
+
- Rakefile
|
|
70
|
+
- lib/json_path_attribute.rb
|
|
71
|
+
- lib/json_path_attribute/attribute_definition.rb
|
|
72
|
+
- lib/json_path_attribute/type.rb
|
|
73
|
+
- lib/json_path_attribute/version.rb
|
|
74
|
+
- sig/path_attribute.rbs
|
|
75
|
+
homepage: https://github.com/BEEQUIP/json_path_attribute
|
|
76
|
+
licenses:
|
|
77
|
+
- MIT
|
|
78
|
+
metadata:
|
|
79
|
+
source_code_uri: https://github.com/BEEQUIP/json_path_attribute
|
|
80
|
+
changelog_uri: https://github.com/BEEQUIP/json_path_attribute/CHANGELOG.md
|
|
81
|
+
rubygems_mfa_required: 'true'
|
|
82
|
+
rdoc_options: []
|
|
83
|
+
require_paths:
|
|
84
|
+
- lib
|
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 3.1.0
|
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0'
|
|
95
|
+
requirements: []
|
|
96
|
+
rubygems_version: 3.6.7
|
|
97
|
+
specification_version: 4
|
|
98
|
+
summary: An object mapper for mapping JSON data to a Ruby object
|
|
99
|
+
test_files: []
|