simple_representer 1.0.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 +7 -0
- data/.github/workflows/main.yml +20 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +34 -0
- data/LICENSE +21 -0
- data/README.md +112 -0
- data/lib/simple_representer/callable_hash.rb +18 -0
- data/lib/simple_representer/collection.rb +24 -0
- data/lib/simple_representer/computed.rb +11 -0
- data/lib/simple_representer/definable.rb +40 -0
- data/lib/simple_representer/field.rb +36 -0
- data/lib/simple_representer/property.rb +13 -0
- data/lib/simple_representer/representer.rb +47 -0
- data/lib/simple_representer/version.rb +5 -0
- data/lib/simple_representer.rb +6 -0
- data/simple_representer.gemspec +19 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 43c5d72e991bc2429713d2e1c6f45ca3cfdc0ccc38649e761d40c4f569a8e5ef
|
4
|
+
data.tar.gz: 7aaf16de7f25dd8078da3dcbf55ea9f82057081ca2bc32c31ccd7fd419a59e22
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce011f9f391a5f471fd2c74e6012312e28fd3a4a3f5add071444990c59dcbab68cb7fb9b1491f2f29f559eb011714b1d57d47fdc0543a9400be5ce88baf12412
|
7
|
+
data.tar.gz: 75b4bbb3eaca37409d4f6d26d1c5c4ab68cede90a2cb8b7136bcf1aea3fcf3133e7150f364a19160e95ac99cce0e1d7e15d263ce4731306f5aaed86e8f9270f7
|
@@ -0,0 +1,20 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
ruby: [2.5, 2.6, 2.7, 3.0]
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- name: Set up Ruby
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
18
|
+
bundler-cache: true
|
19
|
+
- name: Run RSpec
|
20
|
+
run: bundle exec rspec
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
simple_representer (1.0.0)
|
5
|
+
oj (~> 3.10.16)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.4.4)
|
11
|
+
oj (3.10.18)
|
12
|
+
rspec (3.10.0)
|
13
|
+
rspec-core (~> 3.10.0)
|
14
|
+
rspec-expectations (~> 3.10.0)
|
15
|
+
rspec-mocks (~> 3.10.0)
|
16
|
+
rspec-core (3.10.1)
|
17
|
+
rspec-support (~> 3.10.0)
|
18
|
+
rspec-expectations (3.10.1)
|
19
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
20
|
+
rspec-support (~> 3.10.0)
|
21
|
+
rspec-mocks (3.10.1)
|
22
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
23
|
+
rspec-support (~> 3.10.0)
|
24
|
+
rspec-support (3.10.1)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
rspec (~> 3.10.0)
|
31
|
+
simple_representer!
|
32
|
+
|
33
|
+
BUNDLED WITH
|
34
|
+
2.2.3
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 Paladin Software
|
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,112 @@
|
|
1
|
+
# SimpleRepresenter
|
2
|
+
Simple solution to represent your objects as hash or json.
|
3
|
+
|
4
|
+
## Instalation
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'simple_representer'
|
9
|
+
```
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle install
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
Create a class that inherits from `SimpleRepresenter::Representer`
|
17
|
+
and define your representation using `property` (to access methods defined in represented object)
|
18
|
+
and `computed` (to use methods defined inside representer) class methods.
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
class UserRepresenter < SimpleRepresenter::Representer
|
22
|
+
property :id
|
23
|
+
computed :full_name
|
24
|
+
computed :is_active
|
25
|
+
|
26
|
+
def full_name
|
27
|
+
"#{represented.first_name} #{represented.last_name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def is_active
|
31
|
+
!represented.activated_at.nil?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
Pass your object as argument in initializer and call `to_h`/`to_hash` or `to_json`.
|
36
|
+
You can also represent hashes like normal objects (see: [SimpleRepresenter::CallableHash](./lib/simple_representer/callable_hash.rb))
|
37
|
+
```ruby
|
38
|
+
user = User.find(1)
|
39
|
+
UserRepresenter.new(user).to_json
|
40
|
+
=> "{\"id\":1,\"full_name\":\"Jon Doe\",\"is_active\":false}"
|
41
|
+
UserRepresenter.new(user).to_hash
|
42
|
+
=> {:id=>1, :full_name=>"Jon Doe", :is_active=>false}
|
43
|
+
```
|
44
|
+
|
45
|
+
### Collections
|
46
|
+
To use SimpleRepresenter with collection of objects use `for_collection` method:
|
47
|
+
```ruby
|
48
|
+
UserRepresenter.for_collection(users).to_json
|
49
|
+
=> "[{\"id\":1,\"full_name\":\"Jon Doe\",\"is_active\":false},{\"id\":2,\"full_name\":\"Jon Wick\",\"is_active\":true}]"
|
50
|
+
```
|
51
|
+
|
52
|
+
### Options
|
53
|
+
Both `property` and `computed` have following options:
|
54
|
+
- `if` to make execution dependent on condition:
|
55
|
+
```ruby
|
56
|
+
property :full_name, if: -> { first_name && last_name }
|
57
|
+
```
|
58
|
+
- `as` to rename field in representation:
|
59
|
+
```ruby
|
60
|
+
property :is_active?, as: :is_active
|
61
|
+
```
|
62
|
+
- `default` to set default value:
|
63
|
+
```ruby
|
64
|
+
property :name, default: 'Paladin'
|
65
|
+
```
|
66
|
+
- `render_nil` to render or skip nil value (default is false):
|
67
|
+
```ruby
|
68
|
+
# will render { name: nil } if name is nil
|
69
|
+
property :name, render_nil: true
|
70
|
+
```
|
71
|
+
- `representer` to use different representer for nested objects.
|
72
|
+
If it's an array `for_collection` will be automatically called.
|
73
|
+
```ruby
|
74
|
+
property :comments, representer: CommentsRepresenter
|
75
|
+
```
|
76
|
+
|
77
|
+
### Additional arguments
|
78
|
+
You can pass additional arguments to initializer. You can access them inside computed methods or any other place using `options`:
|
79
|
+
```ruby
|
80
|
+
class UserRepresenter < SimpleRepresenter::Representer
|
81
|
+
property :id
|
82
|
+
property :full_name, if: -> { options[:display_name] }
|
83
|
+
end
|
84
|
+
```
|
85
|
+
```ruby
|
86
|
+
user = OpenStruct.new({ id: 5, full_name: 'Batman' })
|
87
|
+
UserRepresenter.new(user, display_name: false).to_json
|
88
|
+
=> "{\"id\":5}"
|
89
|
+
```
|
90
|
+
You can set default options for properties by using `defaults`:
|
91
|
+
```ruby
|
92
|
+
class UserRepresenter < SimpleRepresenter::Representer
|
93
|
+
defaults render_nil: true # render nil properties
|
94
|
+
property :id
|
95
|
+
end
|
96
|
+
```
|
97
|
+
## Migrating from Roar
|
98
|
+
Replace all occurrences of `exec_context: :decorator` to `computed`:
|
99
|
+
```ruby
|
100
|
+
property :full_name, exec_context: :decorator
|
101
|
+
=>
|
102
|
+
computed :full_name
|
103
|
+
```
|
104
|
+
Replace `decorator` with `representer` and `collection` with `computed` or `property` for nested objects:
|
105
|
+
```ruby
|
106
|
+
collection :songs, decorator: SongRepresenter
|
107
|
+
=>
|
108
|
+
property :songs, representer: SongRepresenter
|
109
|
+
```
|
110
|
+
|
111
|
+
## Specs
|
112
|
+
Just run `rspec`.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleRepresenter
|
4
|
+
module CallableHash
|
5
|
+
private
|
6
|
+
|
7
|
+
def method_missing(symbol, *args)
|
8
|
+
return self[symbol] if include?(symbol)
|
9
|
+
return self[symbol.to_s] if include?(symbol.to_s)
|
10
|
+
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def respond_to_missing?(symbol, include_private = false)
|
15
|
+
include?(symbol) || include?(symbol.to_s) || super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'oj'
|
4
|
+
|
5
|
+
module SimpleRepresenter
|
6
|
+
class Collection
|
7
|
+
attr_reader :representer, :collection, :options
|
8
|
+
|
9
|
+
def initialize(representer, collection = [], options = {})
|
10
|
+
@representer = representer
|
11
|
+
@collection = collection
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_h
|
16
|
+
collection.map { |elem| representer.new(elem, **options).to_h }
|
17
|
+
end
|
18
|
+
alias to_hash to_h
|
19
|
+
|
20
|
+
def to_json(*_args)
|
21
|
+
::Oj.generate(to_h)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'property'
|
4
|
+
require_relative 'computed'
|
5
|
+
|
6
|
+
module SimpleRepresenter
|
7
|
+
module Definable
|
8
|
+
def self.included(host_class)
|
9
|
+
host_class.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def property(field, **options)
|
14
|
+
definitions << Property.new(field, default_options.merge(options))
|
15
|
+
end
|
16
|
+
|
17
|
+
def computed(field, **options)
|
18
|
+
definitions << Computed.new(field, default_options.merge(options))
|
19
|
+
end
|
20
|
+
|
21
|
+
def defaults(**options)
|
22
|
+
default_options.merge!(options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def definitions
|
26
|
+
@definitions ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_options
|
30
|
+
@default_options ||= {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def inherited(subclass)
|
34
|
+
super
|
35
|
+
subclass.instance_variable_set('@definitions', instance_variable_get('@definitions').clone)
|
36
|
+
subclass.instance_variable_set('@default_options', instance_variable_get('@default_options').clone)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleRepresenter
|
4
|
+
class Field
|
5
|
+
attr_reader :field, :options
|
6
|
+
|
7
|
+
def initialize(field, options)
|
8
|
+
@field = field.to_sym
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(representer)
|
13
|
+
return if options[:if] && !representer.instance_exec(&options[:if])
|
14
|
+
|
15
|
+
value = process(representer)
|
16
|
+
value = options[:default] if value.nil?
|
17
|
+
value = nested_representer(value) if options[:representer] && !value.nil?
|
18
|
+
|
19
|
+
build_field(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def nested_representer(value)
|
25
|
+
return options[:representer].for_collection(value).to_h if value.is_a?(Array)
|
26
|
+
|
27
|
+
options[:representer].new(value).to_h
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_field(value)
|
31
|
+
return if value.nil? && !options.fetch(:render_nil, false)
|
32
|
+
|
33
|
+
[(options[:as] || field).to_sym, value]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'field'
|
4
|
+
|
5
|
+
module SimpleRepresenter
|
6
|
+
class Property < Field
|
7
|
+
def process(representer)
|
8
|
+
return nil unless representer.represented.respond_to?(field)
|
9
|
+
|
10
|
+
representer.represented.public_send(field)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'oj'
|
4
|
+
|
5
|
+
require_relative 'callable_hash'
|
6
|
+
require_relative 'definable'
|
7
|
+
require_relative 'collection'
|
8
|
+
|
9
|
+
module SimpleRepresenter
|
10
|
+
class Representer
|
11
|
+
include Definable
|
12
|
+
|
13
|
+
attr_reader :represented, :options
|
14
|
+
|
15
|
+
def initialize(represented, **options)
|
16
|
+
@represented = represented.is_a?(Hash) ? represented.clone.extend(CallableHash) : represented
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
# return hash with symbols as keys
|
21
|
+
def to_h
|
22
|
+
build_result do |obj, result|
|
23
|
+
obj[result[0]] = result[1]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias to_hash to_h
|
27
|
+
|
28
|
+
def to_json(*_args)
|
29
|
+
::Oj.generate(to_h)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.for_collection(collection, **options)
|
33
|
+
Collection.new(self, collection, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def build_result
|
39
|
+
self.class.definitions.each_with_object({}) do |definition, obj|
|
40
|
+
result = definition.call(self)
|
41
|
+
next unless result
|
42
|
+
|
43
|
+
yield obj, result
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'lib/simple_representer/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'simple_representer'
|
5
|
+
s.version = SimpleRepresenter::VERSION
|
6
|
+
s.summary = 'Simple solution to represent your objects as hash or json.'
|
7
|
+
s.authors = ['Karol Bąk']
|
8
|
+
s.license = 'MIT'
|
9
|
+
s.homepage = 'https://github.com/paladinsoftware/simple_representer'
|
10
|
+
|
11
|
+
s.files = Dir.chdir(File.expand_path(__dir__)) do
|
12
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
13
|
+
end
|
14
|
+
s.require_paths = ['lib']
|
15
|
+
|
16
|
+
s.add_runtime_dependency 'oj', '~> 3.10.16'
|
17
|
+
|
18
|
+
s.add_development_dependency 'rspec', '~> 3.10.0'
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_representer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Karol Bąk
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: oj
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.10.16
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.10.16
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.10.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.10.0
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- ".github/workflows/main.yml"
|
48
|
+
- Gemfile
|
49
|
+
- Gemfile.lock
|
50
|
+
- LICENSE
|
51
|
+
- README.md
|
52
|
+
- lib/simple_representer.rb
|
53
|
+
- lib/simple_representer/callable_hash.rb
|
54
|
+
- lib/simple_representer/collection.rb
|
55
|
+
- lib/simple_representer/computed.rb
|
56
|
+
- lib/simple_representer/definable.rb
|
57
|
+
- lib/simple_representer/field.rb
|
58
|
+
- lib/simple_representer/property.rb
|
59
|
+
- lib/simple_representer/representer.rb
|
60
|
+
- lib/simple_representer/version.rb
|
61
|
+
- simple_representer.gemspec
|
62
|
+
homepage: https://github.com/paladinsoftware/simple_representer
|
63
|
+
licenses:
|
64
|
+
- MIT
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubygems_version: 3.1.4
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: Simple solution to represent your objects as hash or json.
|
85
|
+
test_files: []
|