supa 0.1.4 → 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.
data/.travis.yml CHANGED
@@ -2,11 +2,19 @@ sudo: false
2
2
  language: ruby
3
3
  rvm:
4
4
  - 2.3.1
5
- before_install: gem install bundler -v 1.13.1
5
+
6
+ notifications:
7
+ email: false
8
+
6
9
  addons:
7
10
  code_climate:
8
- repo_token: eb758a7a8b04369f929aa1b962bc9764b6517539971b785600065cf5ad93e282
11
+ repo_token: ad743cd1c267d4a84bfa620869f906a68efba51baa3aadc0638b88330ce3ab3a
12
+
13
+ cache:
14
+ - bundler
15
+
16
+ before_install: gem install bundler -v 1.13.7
17
+ script:
18
+ - bundle exec rake
9
19
  after_success:
10
20
  - bundle exec codeclimate-test-reporter
11
- notifications:
12
- email: false
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in supa.gemspec
4
3
  gemspec
data/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  Ruby object → JSON serialization.
4
4
 
5
- [![Build Status](https://travis-ci.org/dasnotme/supa.svg?branch=master)](https://travis-ci.org/dasnotme/supa)
6
- [![Code Climate](https://codeclimate.com/github/dasnotme/supa/badges/gpa.svg)](https://codeclimate.com/github/dasnotme/supa)
7
- [![Test Coverage](https://codeclimate.com/github/dasnotme/supa/badges/coverage.svg)](https://codeclimate.com/github/dasnotme/supa/coverage)
8
- [![Issue Count](https://codeclimate.com/github/dasnotme/supa/badges/issue_count.svg)](https://codeclimate.com/github/dasnotme/supa)
5
+ [![Build Status](https://travis-ci.org/distribusion/supa.svg?branch=master)](https://travis-ci.org/distribusion/supa)
6
+ [![Code Climate](https://codeclimate.com/repos/587387071c36ea7203000e0d/badges/19b714c64bf6f028a58c/gpa.svg)](https://codeclimate.com/repos/587387071c36ea7203000e0d/feed)
7
+ [![Test Coverage](https://codeclimate.com/repos/587387071c36ea7203000e0d/badges/19b714c64bf6f028a58c/coverage.svg)](https://codeclimate.com/repos/587387071c36ea7203000e0d/coverage)
8
+ [![Issue Count](https://codeclimate.com/repos/587387071c36ea7203000e0d/badges/19b714c64bf6f028a58c/issue_count.svg)](https://codeclimate.com/repos/587387071c36ea7203000e0d/feed)
9
9
 
10
10
  ## Introduction
11
11
 
@@ -83,7 +83,7 @@ class ArticleRepresenter
83
83
  end
84
84
  end
85
85
 
86
- polymorphic :included, getter: proc { [self.author] } do
86
+ collection :included, getter: proc { [self.author] } do
87
87
  attribute :id
88
88
  attribute :type, getter: proc { 'authors' }
89
89
 
@@ -93,7 +93,7 @@ class ArticleRepresenter
93
93
  end
94
94
  end
95
95
 
96
- polymorphic :included, getter: proc { self.comments } do
96
+ collection :included, getter: proc { self.comments }, squash: true do
97
97
  attribute :id
98
98
  attribute :type, getter: proc { 'comments' }
99
99
 
@@ -177,7 +177,52 @@ ArticleRepresenter.new(Article.new).to_json
177
177
 
178
178
  ### `collection`
179
179
 
180
- ### `polymorphic`
180
+ #### `:squash` option
181
+
182
+ Passing `true` to `:squash` option results in merging collection with the previous one
183
+
184
+ ```ruby
185
+ class AnimalsRepresenter
186
+ include Supa::Representable
187
+
188
+ define do
189
+ collection :animals, getter: -> { [{name: 'Rex', type: 'dogs'}] } do
190
+ attribute :name
191
+ attribute :type
192
+ end
193
+
194
+ collection :animals, getter: -> { [{name: 'Tom', type: 'cats'}] }, squash: true do
195
+ attribute :name
196
+ attribute :type
197
+ end
198
+ end
199
+ end
200
+ ```
201
+
202
+ ```ruby
203
+ AnimalsRepresenter.new(nil).to_hash
204
+ ```
205
+
206
+ ```ruby
207
+ {
208
+ animals: [
209
+ {name: 'Rex', type: 'dogs'},
210
+ {name: 'Tom', type: 'cats'}
211
+ ]
212
+ }
213
+ ```
214
+
215
+ ### `:getter` option
216
+
217
+ Avoid passing Proc objects to `:getter` option because this is little slower than method name passing
218
+
219
+ ```ruby
220
+ # Bad
221
+ attribute :name, getter: -> { fetch_name }
222
+
223
+ # Good
224
+ attribute :name, getter: :fetch_name
225
+ ```
181
226
 
182
227
  ## Development
183
228
 
@@ -203,10 +248,9 @@ bin/console
203
248
 
204
249
  ## Contributing
205
250
 
206
- Bug reports and pull requests are welcome on GitHub at https://github.com/dasnotme/supa. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
207
-
251
+ Bug reports and pull requests are welcome on GitHub at https://github.com/distribusion/supa.
252
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
208
253
 
209
254
  ## License
210
255
 
211
256
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
212
-
data/Rakefile CHANGED
@@ -1,14 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
- require 'rake/testtask'
2
+ require 'rspec/core/rake_task'
3
3
 
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << %w(spec lib)
6
- t.test_files = FileList['spec/**/*_spec.rb']
7
- end
4
+ RSpec::Core::RakeTask.new(:spec)
8
5
 
9
- Rake::TestTask.new(:bench) do |t|
10
- t.libs << %w(spec lib)
11
- t.test_files = FileList['spec/benchmarks/**/*_bench.rb']
12
- end
13
-
14
- task :default => :test
6
+ task default: :spec
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "supa"
3
+ require 'bundler/setup'
4
+ require 'supa'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "supa"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
14
- IRB.start
13
+ require 'pry-byebug'
14
+ Pry.start
data/lib/supa/builder.rb CHANGED
@@ -1,39 +1,34 @@
1
- require 'supa/commands/attribute'
2
- require 'supa/commands/object'
3
- require 'supa/commands/namespace'
4
- require 'supa/commands/collection'
5
- require 'supa/commands/polymorphic'
6
-
7
1
  module Supa
8
2
  class Builder
9
- COMMANDS = %w(attribute object namespace collection polymorphic).freeze
3
+ COMMANDS = %w(attribute object namespace collection).freeze
10
4
 
11
5
  COMMANDS.each do |command|
12
6
  klass = Supa::Commands.const_get(command.capitalize)
13
7
 
14
8
  define_method command do |name, options = {}, &block|
15
- klass.new(context: context,
16
- tree: tree,
17
- name: name,
18
- options: options,
19
- &block).represent
9
+ klass.new(
10
+ @object,
11
+ tree: @tree,
12
+ representer: @representer,
13
+ name: name,
14
+ options: options,
15
+ &block
16
+ ).represent
20
17
  end
21
18
  end
22
19
 
23
- def initialize(context:, tree:)
24
- @context = context
20
+ def initialize(object, tree:, representer:)
21
+ @object = object
25
22
  @tree = tree
23
+ @representer = representer
26
24
  end
27
25
 
28
26
  def to_hash
29
- tree.to_hash
27
+ @tree.to_hash
30
28
  end
31
29
 
32
30
  def to_json
33
31
  to_hash.to_json
34
32
  end
35
-
36
- private
37
- attr_reader :context, :tree
38
33
  end
39
34
  end
data/lib/supa/command.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  module Supa
2
2
  class Command
3
- def initialize(context:, tree:, name:, options: {}, &block)
4
- @context = context
3
+ def initialize(object, tree:, representer:, name:, options: {}, &block)
4
+ @object = object
5
5
  @tree = tree
6
+ @representer = representer
6
7
  @name = name
7
8
  @options = options
8
9
  @block = block
@@ -13,14 +14,43 @@ module Supa
13
14
  end
14
15
 
15
16
  private
16
- attr_reader :context, :tree, :name, :options, :block
17
17
 
18
- def get_value
19
- if options[:getter].is_a?(Proc)
20
- context.instance_exec(&options[:getter])
21
- else
22
- context.is_a?(Hash) ? context[name] : context.send(name)
18
+ def value
19
+ return instance_exec(&value_accessor) if value_accessor.respond_to?(:call)
20
+
21
+ extracted_value = derived_value_from_object(@object) || derived_value_from_object(@representer)
22
+ extracted_value ||= literal_value
23
+
24
+ raise_no_method_error(value_accessor) if extracted_value.nil?
25
+ extracted_value
26
+ end
27
+
28
+ def value_accessor
29
+ @value_accessor ||= @options.fetch(:getter, @name)
30
+ end
31
+
32
+ def derived_value_from_object(object)
33
+ if value_accessor.respond_to?(:to_sym) && object.respond_to?(value_accessor)
34
+ object.send(value_accessor.to_sym)
35
+ elsif object.is_a?(Hash)
36
+ object.dig(value_accessor)
23
37
  end
24
38
  end
39
+
40
+ def literal_value
41
+ return if value_accessor.respond_to?(:call)
42
+ value_accessor unless value_accessor.is_a?(Symbol) || value_accessor.is_a?(Enumerable)
43
+ end
44
+
45
+ def raise_no_method_error(method_sym)
46
+ raise NoMethodError, "undefined method `#{method_sym}' for #{@object} or #{@representer}"
47
+ end
48
+
49
+ def method_missing(method_sym, *args, &block)
50
+ return @representer.send(method_sym, *args, &block) if @representer.respond_to?(method_sym)
51
+ return @object.send(method_sym, *args, &block) if @object.respond_to?(method_sym)
52
+
53
+ raise_no_method_error(method_sym)
54
+ end
25
55
  end
26
56
  end
@@ -1,10 +1,8 @@
1
- require 'supa/command'
2
-
3
1
  module Supa
4
2
  module Commands
5
3
  class Attribute < Supa::Command
6
4
  def represent
7
- tree[name] = get_value
5
+ @tree[@name] = value
8
6
  end
9
7
  end
10
8
  end
@@ -1,15 +1,15 @@
1
- require 'supa/command'
2
-
3
1
  module Supa
4
2
  module Commands
5
3
  class Collection < Supa::Command
4
+ include Supa::Commands::Collectionable
5
+
6
6
  def represent
7
- tree[name] = []
7
+ @tree[@name] = [] unless @options[:squash]
8
8
 
9
- Array(get_value).each do |element|
10
- tree[name] << {}
9
+ collection.each do |element|
10
+ @tree[@name] << {}
11
11
 
12
- Supa::Builder.new(context: element, tree: tree[name][-1]).instance_exec(&block)
12
+ Supa::Builder.new(element, tree: @tree[@name][-1], representer: @representer).instance_exec(&@block)
13
13
  end
14
14
  end
15
15
  end
@@ -1,15 +1,11 @@
1
- require 'supa/command'
2
-
3
1
  module Supa
4
2
  module Commands
5
3
  class Namespace < Supa::Command
6
4
  def represent
7
- tree[name] = {}
5
+ @tree[@name] = {}
8
6
 
9
- Supa::Builder.new(context: context, tree: tree[name]).instance_exec(&block)
7
+ Supa::Builder.new(@object, tree: @tree[@name], representer: @representer).instance_exec(&@block)
10
8
  end
11
9
  end
12
10
  end
13
11
  end
14
-
15
-
@@ -1,12 +1,10 @@
1
- require 'supa/command'
2
-
3
1
  module Supa
4
2
  module Commands
5
3
  class Object < Supa::Command
6
4
  def represent
7
- tree[name] = {}
5
+ @tree[@name] = {}
8
6
 
9
- Supa::Builder.new(context: get_value, tree: tree[name]).instance_exec(&block)
7
+ Supa::Builder.new(value, tree: @tree[@name], representer: @representer).instance_exec(&@block)
10
8
  end
11
9
  end
12
10
  end
@@ -0,0 +1,12 @@
1
+ module Supa
2
+ module Commands
3
+ module Collectionable
4
+ private
5
+
6
+ def collection
7
+ collection = value
8
+ collection.is_a?(Array) ? collection : [collection]
9
+ end
10
+ end
11
+ end
12
+ end
@@ -11,7 +11,7 @@ module Supa
11
11
  end
12
12
 
13
13
  def to_hash
14
- Supa::Builder.new(context: object, tree: {}).tap do |builder|
14
+ Supa::Builder.new(@object, tree: {}, representer: self).tap do |builder|
15
15
  builder.instance_exec(&self.class.definition)
16
16
  end.to_hash
17
17
  end
@@ -19,9 +19,6 @@ module Supa
19
19
  def to_json
20
20
  to_hash.to_json
21
21
  end
22
-
23
- private
24
- attr_reader :object
25
22
  end
26
23
 
27
24
  module ClassMethods
data/lib/supa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Supa
2
- VERSION = '0.1.4'
2
+ VERSION = '0.2.0'.freeze
3
3
  end
data/lib/supa.rb CHANGED
@@ -1,6 +1,12 @@
1
1
  require 'supa/version'
2
- require 'supa/builder'
3
2
  require 'supa/representable'
3
+ require 'supa/command'
4
+ require 'supa/commands/traits/collectionable'
5
+ require 'supa/commands/attribute'
6
+ require 'supa/commands/object'
7
+ require 'supa/commands/namespace'
8
+ require 'supa/commands/collection'
9
+ require 'supa/builder'
4
10
 
5
11
  module Supa
6
12
  end
data/supa.gemspec CHANGED
@@ -1,30 +1,37 @@
1
- # coding: utf-8
2
1
  lib = File.expand_path('../lib', __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'supa/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "supa"
6
+ spec.name = 'supa'
8
7
  spec.version = Supa::VERSION
9
8
  spec.platform = Gem::Platform::RUBY
10
- spec.authors = ["Das"]
11
- spec.email = [""]
9
+ spec.authors = ['Andrey Duplichev', 'Jan Rietema', 'Jakub Gorzelak']
10
+ spec.email = %w(
11
+ andrey.duplichev@distribusion.com
12
+ jan.rietema@distribusion.com
13
+ jakub.gorzelak@distribusion.com
14
+ )
12
15
 
13
- spec.summary = "Ruby object → JSON serialization."
14
- spec.description = "Ruby object → JSON serialization."
15
- spec.homepage = "https://github.com/dasnotme/supa"
16
- spec.license = "MIT"
16
+ spec.summary = 'Ruby object → JSON serialization.'
17
+ spec.description = 'Ruby object → JSON serialization.'
18
+ spec.homepage = 'https://github.com/distribusion/supa'
19
+ spec.license = 'MIT'
17
20
 
18
21
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
22
  f.match(%r{^(test|spec|features)/})
20
23
  end
21
- spec.bindir = "exe"
24
+ spec.bindir = 'exe'
22
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
- spec.require_paths = ["lib"]
26
+ spec.require_paths = ['lib']
24
27
 
25
- spec.add_development_dependency "bundler", "~> 1.13"
26
- spec.add_development_dependency "rake", "~> 10.0"
27
- spec.add_development_dependency "minitest", "~> 5.0"
28
- spec.add_development_dependency "simplecov", "~> 0.12"
29
- spec.add_development_dependency "codeclimate-test-reporter", "~> 1.0.0"
28
+ spec.add_development_dependency 'bundler', '~> 1.13'
29
+ spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'rspec', '~> 3.0'
31
+ spec.add_development_dependency 'rspec-benchmark', '~> 0.2.0'
32
+ spec.add_development_dependency 'rubocop', '~> 0.45.0'
33
+ spec.add_development_dependency 'reek', '~> 4.5.0'
34
+ spec.add_development_dependency 'simplecov', '~> 0.12'
35
+ spec.add_development_dependency 'codeclimate-test-reporter', '~> 1.0.0'
36
+ spec.add_development_dependency 'pry-byebug', '~> 3.4.0'
30
37
  end
metadata CHANGED
@@ -1,14 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: supa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - Das
7
+ - Andrey Duplichev
8
+ - Jan Rietema
9
+ - Jakub Gorzelak
8
10
  autorequire:
9
11
  bindir: exe
10
12
  cert_chain: []
11
- date: 2016-12-14 00:00:00.000000000 Z
13
+ date: 2017-01-09 00:00:00.000000000 Z
12
14
  dependencies:
13
15
  - !ruby/object:Gem::Dependency
14
16
  name: bundler
@@ -39,19 +41,61 @@ dependencies:
39
41
  - !ruby/object:Gem::Version
40
42
  version: '10.0'
41
43
  - !ruby/object:Gem::Dependency
42
- name: minitest
44
+ name: rspec
43
45
  requirement: !ruby/object:Gem::Requirement
44
46
  requirements:
45
47
  - - "~>"
46
48
  - !ruby/object:Gem::Version
47
- version: '5.0'
49
+ version: '3.0'
48
50
  type: :development
49
51
  prerelease: false
50
52
  version_requirements: !ruby/object:Gem::Requirement
51
53
  requirements:
52
54
  - - "~>"
53
55
  - !ruby/object:Gem::Version
54
- version: '5.0'
56
+ version: '3.0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: rspec-benchmark
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 0.2.0
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: 0.2.0
71
+ - !ruby/object:Gem::Dependency
72
+ name: rubocop
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: 0.45.0
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: 0.45.0
85
+ - !ruby/object:Gem::Dependency
86
+ name: reek
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: 4.5.0
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: 4.5.0
55
99
  - !ruby/object:Gem::Dependency
56
100
  name: simplecov
57
101
  requirement: !ruby/object:Gem::Requirement
@@ -80,15 +124,33 @@ dependencies:
80
124
  - - "~>"
81
125
  - !ruby/object:Gem::Version
82
126
  version: 1.0.0
127
+ - !ruby/object:Gem::Dependency
128
+ name: pry-byebug
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: 3.4.0
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - "~>"
139
+ - !ruby/object:Gem::Version
140
+ version: 3.4.0
83
141
  description: Ruby object → JSON serialization.
84
142
  email:
85
- - ''
143
+ - andrey.duplichev@distribusion.com
144
+ - jan.rietema@distribusion.com
145
+ - jakub.gorzelak@distribusion.com
86
146
  executables: []
87
147
  extensions: []
88
148
  extra_rdoc_files: []
89
149
  files:
90
150
  - ".codeclimate.yml"
91
151
  - ".gitignore"
152
+ - ".reek"
153
+ - ".rspec"
92
154
  - ".rubocop.yml"
93
155
  - ".travis.yml"
94
156
  - CODE_OF_CONDUCT.md
@@ -105,11 +167,11 @@ files:
105
167
  - lib/supa/commands/collection.rb
106
168
  - lib/supa/commands/namespace.rb
107
169
  - lib/supa/commands/object.rb
108
- - lib/supa/commands/polymorphic.rb
170
+ - lib/supa/commands/traits/collectionable.rb
109
171
  - lib/supa/representable.rb
110
172
  - lib/supa/version.rb
111
173
  - supa.gemspec
112
- homepage: https://github.com/dasnotme/supa
174
+ homepage: https://github.com/distribusion/supa
113
175
  licenses:
114
176
  - MIT
115
177
  metadata: {}
@@ -1,17 +0,0 @@
1
- require 'supa/command'
2
-
3
- module Supa
4
- module Commands
5
- class Polymorphic < Supa::Command
6
- def represent
7
- tree[name] ||= []
8
-
9
- Array(get_value).each do |element|
10
- tree[name] << {}
11
-
12
- Supa::Builder.new(context: element, tree: tree[name][-1]).instance_exec(&block)
13
- end
14
- end
15
- end
16
- end
17
- end