grape-rabl 0.5.0 → 0.6.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/lib/grape/rabl.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape-rabl'
2
4
 
3
5
  module Grape
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Rabl
3
5
  class Configuration
4
- attr_accessor :cache_template_loading
6
+ attr_accessor :cache_template_loading, :tilt_root
5
7
 
6
8
  def initialize
7
9
  @cache_template_loading = false
10
+ @tilt_root = nil
8
11
  end
9
12
  end
10
13
  end
@@ -1,11 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  module Grape
4
6
  module Rabl
5
7
  class Formatter
8
+ @tilt_cache = {}
9
+ @tilt_cache_mutex = Mutex.new
10
+
6
11
  class << self
7
- def tilt_cache
8
- @tilt_cache ||= ::Tilt::Cache.new
12
+ attr_reader :tilt_cache
13
+
14
+ def fetch_cached_template(key)
15
+ @tilt_cache_mutex.synchronize do
16
+ tilt_cache.fetch(key) { tilt_cache[key] = yield }
17
+ end
9
18
  end
10
19
  end
11
20
 
@@ -38,14 +47,14 @@ module Grape
38
47
  # Find a formatter to fallback to. `env[Grape::Env::API_FORMAT]` will always be a
39
48
  # valid formatter, otherwise a HTTP 406 error would have already have been thrown
40
49
  def fallback_formatter
41
- Grape::Formatter.formatter_for(env[Grape::Env::API_FORMAT])
50
+ Grape::Formatter.formatter_for(env[Grape::Env::API_FORMAT], formatters: {})
42
51
  end
43
52
 
44
53
  def view_path(template)
45
54
  if template.split('.')[-1] == 'rabl'
46
55
  File.join(env['api.tilt.root'], template)
47
56
  else
48
- File.join(env['api.tilt.root'], (template + '.rabl'))
57
+ File.join(env['api.tilt.root'], "#{template}.rabl")
49
58
  end
50
59
  end
51
60
 
@@ -55,6 +64,7 @@ module Grape
55
64
 
56
65
  def rabl
57
66
  raise 'missing rabl template' unless rabl_template
67
+
58
68
  set_view_root unless env['api.tilt.root']
59
69
  yield rabl_template
60
70
  end
@@ -68,12 +78,18 @@ module Grape
68
78
  end
69
79
 
70
80
  def set_view_root
71
- raise "Use Rack::Config to set 'api.tilt.root' in config.ru"
81
+ root = Grape::Rabl.configuration.tilt_root
82
+ env['api.tilt.root'] = root.is_a?(Proc) ? endpoint.instance_exec(&root) : root
83
+ return if env['api.tilt.root']
84
+
85
+ raise "Use Rack::Config to set 'api.tilt.root' in config.ru or configure 'tilt_root'"
72
86
  end
73
87
 
74
88
  def tilt_template(template)
75
89
  if Grape::Rabl.configuration.cache_template_loading
76
- Grape::Rabl::Formatter.tilt_cache.fetch(tilt_cache_key(template)) { ::Tilt.new(view_path(template), tilt_options) }
90
+ Grape::Rabl::Formatter.fetch_cached_template(tilt_cache_key(template)) do
91
+ ::Tilt.new(view_path(template), tilt_options)
92
+ end
77
93
  else
78
94
  ::Tilt.new(view_path(template), tilt_options)
79
95
  end
@@ -86,7 +102,9 @@ module Grape
86
102
  def layout_template
87
103
  layout_path = view_path(env['api.tilt.layout'] || 'layouts/application')
88
104
  if Grape::Rabl.configuration.cache_template_loading
89
- Grape::Rabl::Formatter.tilt_cache.fetch(tilt_cache_key(layout_path)) { ::Tilt.new(layout_path, tilt_options) if File.exist?(layout_path) }
105
+ Grape::Rabl::Formatter.fetch_cached_template(tilt_cache_key(layout_path)) do
106
+ ::Tilt.new(layout_path, tilt_options) if File.exist?(layout_path)
107
+ end
90
108
  else
91
109
  ::Tilt.new(layout_path, tilt_options) if File.exist?(layout_path)
92
110
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Rabl
3
5
  module Render
@@ -9,4 +11,4 @@ module Grape
9
11
  end
10
12
  end
11
13
 
12
- Grape::Endpoint.send(:include, Grape::Rabl::Render)
14
+ Grape::Endpoint.include Grape::Rabl::Render
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'tilt'
2
4
 
3
5
  Rabl.register!
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Rabl
3
- VERSION = '0.5.0'.freeze
5
+ VERSION = '0.6.0'
4
6
  end
5
7
  end
data/lib/grape-rabl.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rabl'
2
4
  require 'grape'
3
5
  require 'grape/rabl'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe 'Grape::Rabl configuration' do
@@ -1,5 +1,139 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
5
+ describe Grape::Rabl::Formatter do
6
+ before do
7
+ hide_const('Tilt::Cache')
8
+ described_class.tilt_cache.clear
9
+ end
10
+
11
+ after do
12
+ described_class.tilt_cache.clear
13
+ end
14
+
15
+ it 'reuses a cached template without Tilt::Cache' do
16
+ template = described_class.fetch_cached_template(:template) { Object.new }
17
+
18
+ expect(described_class.fetch_cached_template(:template) { raise 'recompiled' })
19
+ .to equal(template)
20
+ end
21
+
22
+ it 'memoizes missing layouts' do
23
+ expect(described_class.fetch_cached_template(:missing) { nil }).to be_nil
24
+ expect(described_class.fetch_cached_template(:missing) { raise 'looked up again' })
25
+ .to be_nil
26
+ end
27
+
28
+ it 'creates one cached template for concurrent requests' do
29
+ threads = 4.times.map do
30
+ Thread.new do
31
+ described_class.fetch_cached_template(:concurrent) do
32
+ sleep 0.01
33
+ Object.new
34
+ end
35
+ end
36
+ end
37
+
38
+ expect(threads.map(&:value).map(&:object_id).uniq.size).to eq(1)
39
+ end
40
+ end
41
+
42
+ describe 'configured template roots' do
43
+ let(:app) { Class.new(Grape::API) }
44
+ let(:request_root) { nil }
45
+ let(:root) do
46
+ lambda {
47
+ File.join(__dir__, 'views', env['api.version'] == 'v2' ? 'layout_test' : '')
48
+ }
49
+ end
50
+
51
+ before do
52
+ Grape::Rabl.configuration.tilt_root = root
53
+ Grape::Rabl::Formatter.tilt_cache.clear
54
+ app.version %w[v1 v2], using: :path
55
+ app.format :json
56
+ app.formatter :json, Grape::Formatter::Rabl
57
+ override = request_root
58
+ app.before { env['api.tilt.root'] = override } if override
59
+ app.get('/user', rabl: 'user', rabl_locals: { details: 'base template' }) do
60
+ @user = Struct.new(:name).new('Jane')
61
+ @project = Struct.new(:name).new('First')
62
+ @status = 200
63
+ end
64
+ end
65
+
66
+ after do
67
+ Grape::Rabl.reset_configuration!
68
+ Grape::Rabl::Formatter.tilt_cache.clear
69
+ end
70
+
71
+ [false, true].each do |cache|
72
+ context "with template caching #{cache ? 'enabled' : 'disabled'}" do
73
+ before do
74
+ Grape::Rabl.configuration.cache_template_loading = cache
75
+ end
76
+
77
+ it 'resolves templates and layouts per request without leaking roots' do
78
+ user = { 'user' => { 'name' => 'Jane', 'project' => { 'name' => 'First' } } }
79
+ base_user = { 'user' => user.fetch('user').merge('details' => 'base template') }
80
+
81
+ get '/v1/user'
82
+ expect(JSON.parse(last_response.body)).to eq(base_user)
83
+
84
+ get '/v2/user'
85
+ expect(JSON.parse(last_response.body)).to eq('status' => 200, 'result' => user)
86
+
87
+ get '/v1/user'
88
+ expect(JSON.parse(last_response.body)).to eq(base_user)
89
+ expect(Grape::Rabl.configuration.tilt_root).to equal(root)
90
+ end
91
+ end
92
+ end
93
+
94
+ context 'a static configured root' do
95
+ let(:root) { File.join(__dir__, 'views') }
96
+
97
+ it 'does not automatically append the API version' do
98
+ get '/v2/user'
99
+ expect(JSON.parse(last_response.body)).to have_key('user')
100
+ expect(last_request.env['api.tilt.root']).to eq(root)
101
+ end
102
+ end
103
+
104
+ context 'an explicit request root' do
105
+ let(:root) { -> { raise 'configured root should not run' } }
106
+ let(:request_root) { File.join(__dir__, 'views') }
107
+
108
+ it 'takes precedence over the configured root' do
109
+ get '/v1/user'
110
+ expect(JSON.parse(last_response.body)).to have_key('user')
111
+ end
112
+ end
113
+
114
+ context 'a missing resolved root' do
115
+ let(:root) { -> {} }
116
+
117
+ it 'raises the configuration error' do
118
+ expect { get '/v1/user' }.to raise_error(RuntimeError, /configure 'tilt_root'/)
119
+ end
120
+ end
121
+
122
+ context 'a failing root resolver' do
123
+ let(:root) { -> { raise 'root lookup failed' } }
124
+
125
+ it 'propagates the error' do
126
+ expect { get '/v1/user' }.to raise_error(RuntimeError, 'root lookup failed')
127
+ end
128
+
129
+ it 'does not run when falling back to the default formatter' do
130
+ app.get('/health') { { healthy: true } }
131
+ get '/v1/health'
132
+ expect(JSON.parse(last_response.body)).to eq('healthy' => true)
133
+ end
134
+ end
135
+ end
136
+
3
137
  describe 'Grape::Rabl formatter' do
4
138
  subject do
5
139
  Class.new(Grape::API)
@@ -38,7 +172,7 @@ describe 'Grape::Rabl formatter' do
38
172
 
39
173
  it 'falls back to :txt given no other format information' do
40
174
  get '/oops'
41
- expect(last_response.body).to eq('{:errors=>["bad", "things", "happened"]}')
175
+ expect(last_response.body).to eq({ errors: %w[bad things happened] }.to_s)
42
176
  expect(last_response.headers['Content-Type']).to eq('text/plain')
43
177
  end
44
178
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe 'Grape::Rabl layout' do
@@ -22,8 +24,8 @@ describe 'Grape::Rabl layout' do
22
24
  context 'default' do
23
25
  it 'proper render with default layout' do
24
26
  subject.get('/about', rabl: 'user') do
25
- @user = OpenStruct.new(name: 'LTe')
26
- @project = OpenStruct.new(name: 'First')
27
+ @user = Struct.new(:name).new('LTe')
28
+ @project = Struct.new(:name).new('First')
27
29
  @status = 200
28
30
  end
29
31
 
@@ -41,8 +43,8 @@ describe 'Grape::Rabl layout' do
41
43
 
42
44
  it 'proper render with specified layout' do
43
45
  subject.get('/about', rabl: 'user') do
44
- @user = OpenStruct.new(name: 'LTe')
45
- @project = OpenStruct.new(name: 'First')
46
+ @user = Struct.new(:name).new('LTe')
47
+ @project = Struct.new(:name).new('First')
46
48
  @status = 200
47
49
  end
48
50
 
@@ -61,8 +63,8 @@ describe 'Grape::Rabl layout' do
61
63
  FileUtils.cp_r("#{File.dirname(__FILE__)}/views/layout_test/.", @views_dir)
62
64
  subject.before { env['api.tilt.root'] = "#{File.expand_path('..', File.dirname(__FILE__))}/tmp" }
63
65
  subject.get('/home', rabl: 'user') do
64
- @user = OpenStruct.new(name: 'LTe', email: 'email@example.com')
65
- @project = OpenStruct.new(name: 'First')
66
+ @user = Struct.new(:name, :email).new('LTe', 'email@example.com')
67
+ @project = Struct.new(:name).new('First')
66
68
  @status = 200
67
69
  end
68
70
  end
@@ -79,7 +81,7 @@ describe 'Grape::Rabl layout' do
79
81
  get '/home'
80
82
  expect(last_response.status).to eq(200)
81
83
  old_response = last_response.body
82
- open(@layout, 'a') { |f| f << 'node(:test) { "test" }' }
84
+ File.open(@layout, 'a') { |f| f << 'node(:test) { "test" }' }
83
85
  get '/home'
84
86
  expect(last_response.status).to eq(200)
85
87
  new_response = last_response.body
@@ -90,7 +92,7 @@ describe 'Grape::Rabl layout' do
90
92
  get '/home'
91
93
  expect(last_response.status).to eq(200)
92
94
  old_response = last_response.body
93
- open(@layout, 'a') { |f| f << 'node(:test) { "test" }' }
95
+ File.open(@layout, 'a') { |f| f << 'node(:test) { "test" }' }
94
96
  get '/home'
95
97
  expect(last_response.status).to eq(200)
96
98
  new_response = last_response.body
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe 'Grape::Rabl partials' do
@@ -19,9 +21,9 @@ describe 'Grape::Rabl partials' do
19
21
 
20
22
  it 'proper render partials' do
21
23
  subject.get('/home', rabl: 'project') do
22
- @author = OpenStruct.new(author: 'LTe')
23
- @type = OpenStruct.new(type: 'paper')
24
- @project = OpenStruct.new(name: 'First', type: @type, author: @author)
24
+ @author = Struct.new(:author).new('LTe')
25
+ @type = Struct.new(:type).new('paper')
26
+ @project = Struct.new(:name, :type, :author).new('First', @type, @author)
25
27
  end
26
28
 
27
29
  get('/home')
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Rabl do
@@ -23,12 +25,8 @@ describe Grape::Rabl do
23
25
  end
24
26
 
25
27
  it 'should raise error about root directory' do
26
- begin
27
- subject.get('/home', rabl: true) {}
28
- get '/home'
29
- rescue Exception => e
30
- expect(e.message).to include "Use Rack::Config to set 'api.tilt.root' in config.ru"
31
- end
28
+ subject.get('/home', rabl: true) { nil }
29
+ expect { get '/home' }.to raise_error(RuntimeError, /Use Rack::Config to set 'api.tilt.root' in config.ru/)
32
30
  end
33
31
 
34
32
  context 'titl root is setup' do
@@ -40,7 +38,7 @@ describe Grape::Rabl do
40
38
 
41
39
  describe 'helpers' do
42
40
  it 'should execute helper' do
43
- subject.get('/home', rabl: 'helper') { @user = OpenStruct.new }
41
+ subject.get('/home', rabl: 'helper') { @user = Object.new }
44
42
  get '/home'
45
43
  expect(parsed_response).to eq(JSON.parse('{"user":{"helper":"my_helper"}}'))
46
44
  end
@@ -49,27 +47,27 @@ describe Grape::Rabl do
49
47
  describe '#render' do
50
48
  before do
51
49
  subject.get('/home', rabl: 'user') do
52
- @user = OpenStruct.new(name: 'LTe')
50
+ @user = Struct.new(:name).new('LTe')
53
51
  render rabl: 'admin'
54
52
  end
55
53
 
56
54
  subject.get('/admin/:id', rabl: 'user') do
57
- @user = OpenStruct.new(name: 'LTe')
55
+ @user = Struct.new(:name).new('LTe')
58
56
 
59
57
  render rabl: 'admin' if params[:id] == '1'
60
58
  end
61
59
 
62
60
  subject.get('/home-detail', rabl: 'user') do
63
- @user = OpenStruct.new(name: 'LTe')
61
+ @user = Struct.new(:name).new('LTe')
64
62
  render rabl: 'admin', locals: { details: 'amazing detail' }
65
63
  end
66
64
 
67
65
  subject.get('/about', rabl: 'user') do
68
- @user = OpenStruct.new(name: 'LTe')
66
+ @user = Struct.new(:name).new('LTe')
69
67
  end
70
68
 
71
69
  subject.get('/about-detail', rabl: 'user') do
72
- @user = OpenStruct.new(name: 'LTe')
70
+ @user = Struct.new(:name).new('LTe')
73
71
  render locals: { details: 'just a user' }
74
72
  end
75
73
  end
@@ -112,13 +110,13 @@ describe Grape::Rabl do
112
110
  end
113
111
 
114
112
  it 'should respond with proper content-type' do
115
- subject.get('/home', rabl: 'user') {}
113
+ subject.get('/home', rabl: 'user') { nil }
116
114
  get('/home')
117
115
  expect(last_response.headers['Content-Type']).to eq('application/json')
118
116
  end
119
117
 
120
118
  it 'should not raise error about root directory' do
121
- subject.get('/home', rabl: 'user') {}
119
+ subject.get('/home', rabl: 'user') { nil }
122
120
  get '/home'
123
121
  expect(last_response.status).to eq 200
124
122
  expect(last_response.body).not_to include "Use Rack::Config to set 'api.tilt.root' in config.ru"
@@ -127,8 +125,8 @@ describe Grape::Rabl do
127
125
  ['user', 'user.rabl'].each do |rabl_option|
128
126
  it "should render rabl template (#{rabl_option})" do
129
127
  subject.get('/home', rabl: rabl_option) do
130
- @user = OpenStruct.new(name: 'LTe', email: 'email@example.com')
131
- @project = OpenStruct.new(name: 'First')
128
+ @user = Struct.new(:name, :email).new('LTe', 'email@example.com')
129
+ @project = Struct.new(:name).new('First')
132
130
  end
133
131
 
134
132
  get '/home'
@@ -143,8 +141,8 @@ describe Grape::Rabl do
143
141
  FileUtils.cp("#{File.dirname(__FILE__)}/views/user.rabl", @template)
144
142
  subject.before { env['api.tilt.root'] = "#{File.expand_path('..', File.dirname(__FILE__))}/tmp" }
145
143
  subject.get('/home', rabl: 'user') do
146
- @user = OpenStruct.new(name: 'LTe', email: 'email@example.com')
147
- @project = OpenStruct.new(name: 'First')
144
+ @user = Struct.new(:name, :email).new('LTe', 'email@example.com')
145
+ @project = Struct.new(:name).new('First')
148
146
  end
149
147
  end
150
148
 
@@ -160,7 +158,7 @@ describe Grape::Rabl do
160
158
  get '/home'
161
159
  expect(last_response.status).to eq(200)
162
160
  old_response = last_response.body
163
- open(@template, 'a') { |f| f << 'node(:test) { "test" }' }
161
+ File.open(@template, 'a') { |f| f << 'node(:test) { "test" }' }
164
162
  get '/home'
165
163
  expect(last_response.status).to eq(200)
166
164
  new_response = last_response.body
@@ -178,7 +176,7 @@ describe Grape::Rabl do
178
176
  expect(last_response.status).to eq(200)
179
177
  xml_response = last_response.body
180
178
  expect(json_response).not_to eq(xml_response)
181
- open(@template, 'a') { |f| f << 'node(:test) { "test" }' }
179
+ File.open(@template, 'a') { |f| f << 'node(:test) { "test" }' }
182
180
  get '/home.xml'
183
181
  expect(last_response.status).to eq(200)
184
182
  expect(last_response.body).to eq(xml_response)
@@ -191,7 +189,7 @@ describe Grape::Rabl do
191
189
  get '/home'
192
190
  expect(last_response.status).to eq(200)
193
191
  old_response = last_response.body
194
- open(@template, 'a') { |f| f << 'node(:test) { "test" }' }
192
+ File.open(@template, 'a') { |f| f << 'node(:test) { "test" }' }
195
193
  get '/home'
196
194
  expect(last_response.status).to eq(200)
197
195
  new_response = last_response.body
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Rabl do
@@ -23,7 +25,7 @@ describe Grape::Rabl do
23
25
  end
24
26
 
25
27
  it 'should respond with proper content-type' do
26
- subject.get('/home', rabl: 'user') {}
28
+ subject.get('/home', rabl: 'user') { nil }
27
29
  get('/home')
28
30
  expect(last_response.headers['Content-Type']).to eq('application/xml')
29
31
  end
@@ -31,8 +33,8 @@ describe Grape::Rabl do
31
33
  ['user', 'user.rabl'].each do |rabl_option|
32
34
  it "should render rabl template (#{rabl_option})" do
33
35
  subject.get('/home', rabl: rabl_option) do
34
- @user = OpenStruct.new(name: 'LTe', email: 'email@example.com')
35
- @project = OpenStruct.new(name: 'First')
36
+ @user = Struct.new(:name, :email).new('LTe', 'email@example.com')
37
+ @project = Struct.new(:name).new('First')
36
38
  end
37
39
 
38
40
  get '/home'
data/spec/spec_helper.rb CHANGED
@@ -1,21 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
4
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
5
 
4
6
  require 'bundler'
5
7
  Bundler.setup :default, :test
6
8
 
7
- require 'coveralls'
8
- Coveralls.wear!
9
+ require 'simplecov'
10
+ require 'simplecov-lcov'
11
+ SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
12
+ SimpleCov::Formatter::LcovFormatter.config.lcov_file_name = 'grape-rabl.lcov'
13
+ SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
14
+ SimpleCov.start do
15
+ add_filter '/spec/'
16
+ end
9
17
 
10
18
  require 'active_support/core_ext/hash/conversions'
11
19
  require 'grape/rabl'
12
20
  require 'rspec'
13
21
  require 'rack/test'
14
- require 'ostruct'
15
22
 
16
23
  RSpec.configure do |config|
17
24
  config.include Rack::Test::Methods
18
25
  config.raise_errors_for_deprecations!
19
26
  end
20
27
 
21
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
28
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MyHelper
2
4
  def my_helper
3
5
  'my_helper'
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  attributes :type
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  object @user => :admin
2
4
  attributes :name
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  object @user => :user
2
4
 
3
5
  node(:helper) { my_helper }
data/spec/views/info.rabl CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  attributes :author
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  node(:result) do
2
4
  JSON.parse(yield)
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  node(:status) { @status }
2
4
 
3
5
  node(:result) do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  object @user => :user
2
4
  attributes :name, :email
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  object @project => :project
2
4
  attributes :name
3
5
 
data/spec/views/user.rabl CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  object @user => :user
2
4
  attributes :name, :email
3
5