supa 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -2,19 +2,11 @@ sudo: false
2
2
  language: ruby
3
3
  rvm:
4
4
  - 2.3.1
5
-
6
- notifications:
7
- email: false
8
-
5
+ before_install: gem install bundler -v 1.13.1
9
6
  addons:
10
7
  code_climate:
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
8
+ repo_token: eb758a7a8b04369f929aa1b962bc9764b6517539971b785600065cf5ad93e282
19
9
  after_success:
20
10
  - bundle exec codeclimate-test-reporter
11
+ notifications:
12
+ email: false
data/Gemfile CHANGED
@@ -1,3 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ # Specify your gem's dependencies in supa.gemspec
3
4
  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/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)
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)
9
9
 
10
10
  ## Introduction
11
11
 
@@ -49,60 +49,75 @@ end
49
49
  ```
50
50
 
51
51
  ```ruby
52
- class ArticleRepresenter
53
- include Supa::Representable
52
+ module Supa
53
+ class ArticleRepresenter
54
+ include Supa::Representable
54
55
 
55
- define do
56
- namespace :jsonapi do
57
- attribute :version, getter: proc { 1.1 }
58
- end
59
-
60
- namespace :data do
61
- attribute :id
62
- attribute :type, getter: proc { 'articles' }
56
+ define do
57
+ namespace :jsonapi do
58
+ virtual :version, 1.1, modifier: :to_s
59
+ end
63
60
 
64
- namespace :attributes do
65
- attribute :title
66
- attribute :text
61
+ namespace :meta do
62
+ attribute :locale, :language, exec_context: :representer
67
63
  end
68
64
 
69
- namespace :relationships do
70
- object :author do
71
- namespace :data do
72
- attribute :id
73
- attribute :type, getter: proc { 'authors' }
74
- end
65
+ namespace :data do
66
+ attribute :id
67
+ virtual :type, 'articles'
68
+
69
+ namespace :attributes do
70
+ attribute :title
71
+ attribute :text
75
72
  end
76
73
 
77
- namespace :comments do
78
- collection :data, getter: proc { self.comments } do
79
- attribute :id
80
- attribute :type, getter: proc { 'comments' }
74
+ namespace :relationships do
75
+ object :author do
76
+ namespace :data do
77
+ attribute :id
78
+ virtual :type, 'authors'
79
+ end
80
+ end
81
+
82
+ namespace :comments do
83
+ collection :data, :comments do
84
+ attribute :id
85
+ virtual :type, 'comments'
86
+ end
81
87
  end
82
88
  end
83
89
  end
84
- end
85
90
 
86
- collection :included, getter: proc { [self.author] } do
87
- attribute :id
88
- attribute :type, getter: proc { 'authors' }
91
+ collection :included, :author do
92
+ attribute :id
93
+ virtual :type, 'authors'
89
94
 
90
- namespace :attributes do
91
- attribute :first_name
92
- attribute :last_name
95
+ namespace :attributes do
96
+ attribute :first_name
97
+ attribute :last_name
98
+ end
93
99
  end
94
- end
95
100
 
96
- collection :included, getter: proc { self.comments }, squash: true do
97
- attribute :id
98
- attribute :type, getter: proc { 'comments' }
101
+ append :included, :comments do
102
+ attribute :id
103
+ virtual :type, 'comments'
99
104
 
100
- namespace :attributes do
101
- attribute :text
105
+ namespace :attributes do
106
+ attribute :text
107
+ end
102
108
  end
103
109
  end
110
+
111
+ def to_s(value)
112
+ value.to_s
113
+ end
114
+
115
+ def language
116
+ 'en'
117
+ end
104
118
  end
105
119
  end
120
+
106
121
  ```
107
122
 
108
123
  ```ruby
@@ -112,7 +127,10 @@ ArticleRepresenter.new(Article.new).to_json
112
127
  ```json
113
128
  {
114
129
  "jsonapi": {
115
- "version": 1.1
130
+ "version": "1.1"
131
+ },
132
+ "meta": {
133
+ "locale": "en"
116
134
  },
117
135
  "data": {
118
136
  "id": "7aa15512-1f9d-4a86-98ad-4bb0aae487a2",
@@ -171,58 +189,15 @@ ArticleRepresenter.new(Article.new).to_json
171
189
 
172
190
  ### `attribute`
173
191
 
192
+ ### `virtual`
193
+
174
194
  ### `namespace`
175
195
 
176
196
  ### `object`
177
197
 
178
198
  ### `collection`
179
199
 
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
- ```
200
+ ### `append`
226
201
 
227
202
  ## Development
228
203
 
@@ -248,9 +223,10 @@ bin/console
248
223
 
249
224
  ## Contributing
250
225
 
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.
226
+ 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.
227
+
253
228
 
254
229
  ## License
255
230
 
256
231
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
232
+
data/Rakefile CHANGED
@@ -1,6 +1,14 @@
1
1
  require 'bundler/gem_tasks'
2
- require 'rspec/core/rake_task'
2
+ require 'rake/testtask'
3
3
 
4
- RSpec::Core::RakeTask.new(:spec)
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << %w(spec lib)
6
+ t.test_files = FileList['spec/**/*_spec.rb']
7
+ end
5
8
 
6
- task default: :spec
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
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 'pry-byebug'
14
- Pry.start
13
+ require "irb"
14
+ IRB.start
data/lib/supa.rb CHANGED
@@ -1,12 +1,6 @@
1
1
  require 'supa/version'
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
2
  require 'supa/builder'
3
+ require 'supa/representable'
10
4
 
11
5
  module Supa
12
6
  end
data/lib/supa/builder.rb CHANGED
@@ -1,34 +1,44 @@
1
+ require 'supa/commands/attribute'
2
+ require 'supa/commands/virtual'
3
+ require 'supa/commands/object'
4
+ require 'supa/commands/namespace'
5
+ require 'supa/commands/collection'
6
+ require 'supa/commands/append'
7
+
1
8
  module Supa
2
9
  class Builder
3
- COMMANDS = %w(attribute object namespace collection).freeze
10
+ COMMANDS = %w(attribute virtual object namespace collection append).freeze
4
11
 
5
12
  COMMANDS.each do |command|
6
13
  klass = Supa::Commands.const_get(command.capitalize)
7
14
 
8
- define_method command do |name, options = {}, &block|
9
- klass.new(
10
- @object,
11
- tree: @tree,
12
- representer: @representer,
13
- name: name,
14
- options: options,
15
- &block
16
- ).represent
15
+ define_method command do |name, getter = nil, options = {}, &block|
16
+ klass.new(representer: representer,
17
+ context: context,
18
+ tree: tree,
19
+ name: name,
20
+ getter: getter,
21
+ options: options,
22
+ &block).represent
17
23
  end
18
24
  end
19
25
 
20
- def initialize(object, tree:, representer:)
21
- @object = object
22
- @tree = tree
26
+ def initialize(representer:, context:, tree:)
23
27
  @representer = representer
28
+ @context = context
29
+ @tree = tree
24
30
  end
25
31
 
26
32
  def to_hash
27
- @tree.to_hash
33
+ tree.to_hash
28
34
  end
29
35
 
30
36
  def to_json
31
37
  to_hash.to_json
32
38
  end
39
+
40
+ private
41
+
42
+ attr_reader :representer, :context, :tree
33
43
  end
34
44
  end
data/lib/supa/command.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  module Supa
2
2
  class Command
3
- def initialize(object, tree:, representer:, name:, options: {}, &block)
4
- @object = object
5
- @tree = tree
3
+ def initialize(representer:, context:, tree:, name:, getter:, options: {}, &block)
6
4
  @representer = representer
5
+ @context = context
6
+ @tree = tree
7
7
  @name = name
8
+ @getter = getter
8
9
  @options = options
9
10
  @block = block
10
11
  end
@@ -14,43 +15,50 @@ module Supa
14
15
  end
15
16
 
16
17
  private
18
+ attr_reader :representer, :context, :tree, :name, :options, :block
17
19
 
18
- def value
19
- return instance_exec(&value_accessor) if value_accessor.respond_to?(:call)
20
+ def apply_modifier(value)
21
+ with_modifier? ? representer.send(modifier, value) : value
22
+ end
20
23
 
21
- extracted_value = derived_value_from_object(@object) || derived_value_from_object(@representer)
22
- extracted_value ||= literal_value
24
+ def modifier
25
+ options[:modifier]
26
+ end
23
27
 
24
- raise_no_method_error(value_accessor) if extracted_value.nil?
25
- extracted_value
28
+ def with_modifier?
29
+ !!options[:modifier]
26
30
  end
27
31
 
28
- def value_accessor
29
- @value_accessor ||= @options.fetch(:getter, @name)
32
+ def static_value
33
+ value = getter
34
+
35
+ apply_modifier(value)
30
36
  end
31
37
 
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)
38
+ def dynamic_value
39
+ value = if exec_on_object?
40
+ value_from_object
41
+ else
42
+ value_from_representer
37
43
  end
44
+
45
+ apply_modifier(value)
38
46
  end
39
47
 
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)
48
+ def exec_on_object?
49
+ options[:exec_context] != :representer
43
50
  end
44
51
 
45
- def raise_no_method_error(method_sym)
46
- raise NoMethodError, "undefined method `#{method_sym}' for #{@object} or #{@representer}"
52
+ def value_from_object
53
+ context.is_a?(Hash) ? context[getter] : context.send(getter)
47
54
  end
48
55
 
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)
56
+ def value_from_representer
57
+ representer.send(getter)
58
+ end
52
59
 
53
- raise_no_method_error(method_sym)
60
+ def getter
61
+ @getter || @name
54
62
  end
55
63
  end
56
64
  end
@@ -0,0 +1,17 @@
1
+ require 'supa/command'
2
+
3
+ module Supa
4
+ module Commands
5
+ class Append < Supa::Command
6
+ def represent
7
+ tree[name] ||= []
8
+
9
+ Array(dynamic_value).each do |element|
10
+ tree[name] << {}
11
+
12
+ Supa::Builder.new(representer: representer, context: element, tree: tree[name][-1]).instance_exec(&block)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end