simple_representer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'field'
4
+
5
+ module SimpleRepresenter
6
+ class Computed < Field
7
+ def process(representer)
8
+ representer.public_send(field)
9
+ end
10
+ end
11
+ 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleRepresenter
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'simple_representer/representer'
4
+
5
+ module SimpleRepresenter
6
+ 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: []