supa 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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