grape-entity 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- Excludes:
2
+ Exclude:
3
3
  - vendor/**
4
4
 
5
5
  LineLength:
@@ -23,15 +23,6 @@ Encoding:
23
23
  # no need to always specify encoding
24
24
  Enabled: false
25
25
 
26
- HashMethods:
27
- # key? instead of has_key?
28
- # value? instead of has_value?
29
- Enabled: false
30
-
31
- StringLiterals:
32
- # use single or double-quoted strings, as you please
33
- Enabled: false
34
-
35
26
  Void:
36
27
  # == operator used in void context in specs
37
28
  Enabled: false
@@ -67,3 +58,6 @@ WordArray:
67
58
  CyclomaticComplexity:
68
59
  Enabled: false
69
60
 
61
+ FileName:
62
+ # allow grape-entity.rb for a require
63
+ Enabled: false
data/.travis.yml CHANGED
@@ -1,7 +1,18 @@
1
1
  language: ruby
2
+
2
3
  cache: bundler
4
+
3
5
  rvm:
6
+ - ruby-head
7
+ - 2.1.1
8
+ - 2.1.0
4
9
  - 2.0.0
5
10
  - 1.9.3
6
11
  - jruby-19mode
7
- - rbx-2.1.1
12
+ - jruby-head
13
+ - rbx-2
14
+
15
+ matrix:
16
+ allow_failures:
17
+ - rvm: ruby-head
18
+ - rvm: jruby-head
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ 0.4.3 (2014-06-12)
2
+ ==================
3
+
4
+ * [#77](https://github.com/intridea/grape-entity/pull/77): Fix compatibility with Rspec 3 - [@justfalter](https://github.com/justfalter).
5
+ * [#76](https://github.com/intridea/grape-entity/pull/76): Improve performance of entity serialization - [@justfalter](https://github.com/justfalter)
6
+
1
7
  0.4.2 (2014-04-03)
2
8
  ==================
3
9
 
data/Gemfile CHANGED
@@ -12,8 +12,7 @@ group :development, :test do
12
12
  gem 'json'
13
13
  gem 'rspec'
14
14
  gem 'rack-test', "~> 0.6.2", :require => "rack/test"
15
- gem 'github-markup'
16
- gem 'rubocop', '~> 0.16.0'
15
+ gem 'rubocop', '0.21.0'
17
16
  end
18
17
 
19
18
  platforms :rbx do
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Grape::Entity
2
2
 
3
- [![Build Status](https://travis-ci.org/agileanimal/grape-entity.png?branch=master)](https://travis-ci.org/agileanimal/grape-entity)
3
+ [![Build Status](https://travis-ci.org/intridea/grape-entity.svg?branch=master)](https://travis-ci.org/intridea/grape-entity)
4
4
 
5
5
  ## Introduction
6
6
 
@@ -86,7 +86,7 @@ expose :ip, if: { type: :full }
86
86
 
87
87
  expose :ip, if: lambda { |instance, options| options[:type] == :full } # exposed if the function evaluates to true
88
88
  expose :ip, if: :type # exposed if :type is available in the options hash
89
- expose :ip, if { type: :full } # exposed if options :type has a value of :full
89
+ expose :ip, if: { type: :full } # exposed if options :type has a value of :full
90
90
 
91
91
  expose :ip, unless: ... # the opposite of :if
92
92
  ```
@@ -110,6 +110,15 @@ expose :contact_info do
110
110
  end
111
111
  ```
112
112
 
113
+ #### Collection Exposure
114
+
115
+ Use `root(plural, singular = nil)` to expose an object or a collection of objects with a root key.
116
+
117
+ ```ruby
118
+ root 'users', 'user'
119
+ expose :id, :name, ...
120
+ ```
121
+
113
122
  #### Runtime Exposure
114
123
 
115
124
  Use a block or a `Proc` to evaluate exposure at runtime. The supplied block or
@@ -172,7 +181,7 @@ expose :text, documentation: { type: "String", desc: "Status update text." }
172
181
 
173
182
  ### Options Hash
174
183
 
175
- The option keys `:version` and `:collection` are always defined. The `:version` key is defined as `api.version`. The `:collection` key is boolean, and defined as `true` if the object presented is an array. The options also contain the runtime environment in `:env`, which includes request parameters in `options[:env][:grape.request.params]`.
184
+ The option keys `:version` and `:collection` are always defined. The `:version` key is defined as `api.version`. The `:collection` key is boolean, and defined as `true` if the object presented is an array. The options also contain the runtime environment in `:env`, which includes request parameters in `options[:env]['grape.request.params']`.
176
185
 
177
186
  Any additional options defined on the entity exposure are included as is. In the following example `user` is set to the value of `current_user`.
178
187
 
@@ -0,0 +1,98 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'grape-entity'
3
+ require 'benchmark'
4
+
5
+ module Models
6
+ class School
7
+ attr_reader :classrooms
8
+ def initialize
9
+ @classrooms = []
10
+ end
11
+ end
12
+
13
+ class ClassRoom
14
+ attr_reader :students
15
+ attr_accessor :teacher
16
+ def initialize(opts = {})
17
+ @teacher = opts[:teacher]
18
+ @students = []
19
+ end
20
+ end
21
+
22
+ class Person
23
+ attr_accessor :name
24
+ def initialize(opts = {})
25
+ @name = opts[:name]
26
+ end
27
+ end
28
+
29
+ class Teacher < Models::Person
30
+ attr_accessor :tenure
31
+ def initialize(opts = {})
32
+ super(opts)
33
+ @tenure = opts[:tenure]
34
+ end
35
+ end
36
+
37
+ class Student < Models::Person
38
+ attr_reader :grade
39
+ def initialize(opts = {})
40
+ super(opts)
41
+ @grade = opts[:grade]
42
+ end
43
+ end
44
+ end
45
+
46
+ module Entities
47
+ class School < Grape::Entity
48
+ expose :classrooms, using: 'Entities::ClassRoom'
49
+ end
50
+
51
+ class ClassRoom < Grape::Entity
52
+ expose :teacher, using: 'Entities::Teacher'
53
+ expose :students, using: 'Entities::Student'
54
+ expose :size do |model, _opts|
55
+ model.students.count
56
+ end
57
+ end
58
+
59
+ class Person < Grape::Entity
60
+ expose :name
61
+ end
62
+
63
+ class Student < Entities::Person
64
+ expose :grade
65
+ expose :failing do |model, _opts|
66
+ model.grade == 'F'
67
+ end
68
+ end
69
+
70
+ class Teacher < Entities::Person
71
+ expose :tenure
72
+ end
73
+ end
74
+
75
+ teacher1 = Models::Teacher.new(name: 'John Smith', tenure: 2)
76
+ classroom1 = Models::ClassRoom.new(teacher: teacher1)
77
+ classroom1.students << Models::Student.new(name: 'Bobby', grade: 'A')
78
+ classroom1.students << Models::Student.new(name: 'Billy', grade: 'B')
79
+
80
+ teacher2 = Models::Teacher.new(name: 'Lisa Barns')
81
+ classroom2 = Models::ClassRoom.new(teacher: teacher2, tenure: 15)
82
+ classroom2.students << Models::Student.new(name: 'Eric', grade: 'A')
83
+ classroom2.students << Models::Student.new(name: 'Eddie', grade: 'C')
84
+ classroom2.students << Models::Student.new(name: 'Arnie', grade: 'C')
85
+ classroom2.students << Models::Student.new(name: 'Alvin', grade: 'F')
86
+ school = Models::School.new
87
+ school.classrooms << classroom1
88
+ school.classrooms << classroom2
89
+
90
+ iters = 5000
91
+
92
+ Benchmark.bm do |bm|
93
+ bm.report('serializing') do
94
+ iters.times do
95
+ Entities::School.represent(school, serializable: true)
96
+ end
97
+ end
98
+ end
data/grape-entity.gemspec CHANGED
@@ -1,22 +1,22 @@
1
- $:.push File.expand_path("../lib", __FILE__)
2
- require "grape_entity/version"
1
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
2
+ require 'grape_entity/version'
3
3
 
4
4
  Gem::Specification.new do |s|
5
- s.name = "grape-entity"
5
+ s.name = 'grape-entity'
6
6
  s.version = GrapeEntity::VERSION
7
7
  s.platform = Gem::Platform::RUBY
8
- s.authors = ["Michael Bleigh"]
9
- s.email = ["michael@intridea.com"]
10
- s.homepage = "https://github.com/intridea/grape-entity"
11
- s.summary = %q{A simple facade for managing the relationship between your model and API.}
12
- s.description = %q{Extracted from Grape, A Ruby framework for rapid API development with great conventions.}
13
- s.license = "MIT"
8
+ s.authors = ['Michael Bleigh']
9
+ s.email = ['michael@intridea.com']
10
+ s.homepage = 'https://github.com/intridea/grape-entity'
11
+ s.summary = %q(A simple facade for managing the relationship between your model and API.)
12
+ s.description = %q(Extracted from Grape, A Ruby framework for rapid API development with great conventions.)
13
+ s.license = 'MIT'
14
14
 
15
- s.rubyforge_project = "grape-entity"
15
+ s.rubyforge_project = 'grape-entity'
16
16
 
17
17
  s.add_runtime_dependency 'activesupport'
18
18
  s.add_runtime_dependency 'multi_json', '>= 1.3.2'
19
-
19
+
20
20
  s.add_development_dependency 'rake'
21
21
  s.add_development_dependency 'maruku'
22
22
  s.add_development_dependency 'yard'
@@ -25,6 +25,6 @@ Gem::Specification.new do |s|
25
25
 
26
26
  s.files = `git ls-files`.split("\n")
27
27
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
28
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
29
- s.require_paths = ["lib"]
28
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
29
+ s.require_paths = ['lib']
30
30
  end
data/lib/grape_entity.rb CHANGED
@@ -1,3 +1,4 @@
1
- require "active_support/core_ext"
2
- require "grape_entity/version"
3
- require "grape_entity/entity"
1
+ require 'active_support'
2
+ require 'active_support/core_ext'
3
+ require 'grape_entity/version'
4
+ require 'grape_entity/entity'
@@ -127,11 +127,11 @@ module Grape
127
127
  options = merge_options(args.last.is_a?(Hash) ? args.pop : {})
128
128
 
129
129
  if args.size > 1
130
- raise ArgumentError, "You may not use the :as option on multi-attribute exposures." if options[:as]
131
- raise ArgumentError, "You may not use block-setting on multi-attribute exposures." if block_given?
130
+ raise ArgumentError, 'You may not use the :as option on multi-attribute exposures.' if options[:as]
131
+ raise ArgumentError, 'You may not use block-setting on multi-attribute exposures.' if block_given?
132
132
  end
133
133
 
134
- raise ArgumentError, "You may not use block-setting when also using format_with" if block_given? && options[:format_with].respond_to?(:call)
134
+ raise ArgumentError, 'You may not use block-setting when also using format_with' if block_given? && options[:format_with].respond_to?(:call)
135
135
 
136
136
  options[:proc] = block if block_given? && block.parameters.any?
137
137
 
@@ -139,7 +139,9 @@ module Grape
139
139
 
140
140
  args.each do |attribute|
141
141
  unless @nested_attributes.empty?
142
+ orig_attribute = attribute.to_sym
142
143
  attribute = "#{@nested_attributes.last}__#{attribute}"
144
+ nested_attribute_names_hash[attribute.to_sym] = orig_attribute
143
145
  options[:nested] = true
144
146
  nested_exposures_hash[@nested_attributes.last.to_sym] ||= {}
145
147
  nested_exposures_hash[@nested_attributes.last.to_sym][attribute.to_sym] = options
@@ -175,7 +177,9 @@ module Grape
175
177
  # are symbolized references to methods on the containing object, the values are
176
178
  # the options that were passed into expose.
177
179
  def self.exposures
178
- @exposures ||= {}
180
+ return @exposures unless @exposures.nil?
181
+
182
+ @exposures = {}
179
183
 
180
184
  if superclass.respond_to? :exposures
181
185
  @exposures = superclass.exposures.merge(@exposures)
@@ -185,20 +189,39 @@ module Grape
185
189
  end
186
190
 
187
191
  class << self
192
+ attr_accessor :_nested_attribute_names_hash
188
193
  attr_accessor :_nested_exposures_hash
189
194
 
195
+ def nested_attribute_names_hash
196
+ self._nested_attribute_names_hash ||= {}
197
+ end
198
+
190
199
  def nested_exposures_hash
191
200
  self._nested_exposures_hash ||= {}
192
201
  end
193
202
 
203
+ def nested_attribute_names
204
+ return @nested_attribute_names unless @nested_attribute_names.nil?
205
+
206
+ @nested_attribute_names = {}.merge(nested_attribute_names_hash)
207
+
208
+ if superclass.respond_to? :nested_attribute_names
209
+ @nested_attribute_names = superclass.nested_attribute_names.deep_merge(@nested_attribute_names)
210
+ end
211
+
212
+ @nested_attribute_names
213
+ end
214
+
194
215
  def nested_exposures
195
- value = nested_exposures_hash
216
+ return @nested_exposures unless @nested_exposures.nil?
217
+
218
+ @nested_exposures = {}.merge(nested_exposures_hash)
196
219
 
197
220
  if superclass.respond_to? :nested_exposures
198
- value = superclass.nested_exposures.deep_merge(value)
221
+ @nested_exposures = superclass.nested_exposures.deep_merge(@nested_exposures)
199
222
  end
200
223
 
201
- value
224
+ @nested_exposures
202
225
  end
203
226
  end
204
227
 
@@ -247,7 +270,7 @@ module Grape
247
270
  # end
248
271
  #
249
272
  def self.format_with(name, &block)
250
- raise ArgumentError, "You must pass a block for formatters" unless block_given?
273
+ raise ArgumentError, 'You must pass a block for formatters' unless block_given?
251
274
  formatters[name.to_sym] = block
252
275
  end
253
276
 
@@ -328,7 +351,7 @@ module Grape
328
351
  inner = inner.serializable_hash if options[:serializable]
329
352
  end
330
353
 
331
- root_element = if options.has_key?(:root)
354
+ root_element = if options.key?(:root)
332
355
  options[:root]
333
356
  else
334
357
  objects.respond_to?(:to_ary) ? @collection_root : @root
@@ -346,7 +369,7 @@ module Grape
346
369
  end
347
370
 
348
371
  def valid_exposures
349
- exposures.reject { |a, options| options[:nested] }.select do |attribute, exposure_options|
372
+ exposures.reject { |_, options| options[:nested] }.select do |attribute, exposure_options|
350
373
  valid_exposure?(attribute, exposure_options)
351
374
  end
352
375
  end
@@ -404,7 +427,8 @@ module Grape
404
427
  protected
405
428
 
406
429
  def self.name_for(attribute)
407
- attribute.to_s.split('__').last.to_sym
430
+ attribute = attribute.to_sym
431
+ nested_attribute_names[attribute] || attribute
408
432
  end
409
433
 
410
434
  def self.key_for(attribute)
@@ -469,13 +493,16 @@ module Grape
469
493
  def valid_exposure?(attribute, exposure_options)
470
494
  nested_exposures = self.class.nested_exposures_for(attribute)
471
495
  (nested_exposures.any? && nested_exposures.all? { |a, o| valid_exposure?(a, o) }) || \
472
- exposure_options.has_key?(:proc) || \
496
+ exposure_options.key?(:proc) || \
473
497
  !exposure_options[:safe] || \
474
498
  object.respond_to?(self.class.name_for(attribute))
475
499
  end
476
500
 
477
501
  def conditions_met?(exposure_options, options)
478
- if_conditions = (exposure_options[:if_extras] || []).dup
502
+ if_conditions = []
503
+ unless exposure_options[:if_extras].nil?
504
+ if_conditions.concat(exposure_options[:if_extras])
505
+ end
479
506
  if_conditions << exposure_options[:if] unless exposure_options[:if].nil?
480
507
 
481
508
  if_conditions.each do |if_condition|
@@ -486,7 +513,10 @@ module Grape
486
513
  end
487
514
  end
488
515
 
489
- unless_conditions = (exposure_options[:unless_extras] || []).dup
516
+ unless_conditions = []
517
+ unless exposure_options[:unless_extras].nil?
518
+ unless_conditions.concat(exposure_options[:unless_extras])
519
+ end
490
520
  unless_conditions << exposure_options[:unless] unless exposure_options[:unless].nil?
491
521
 
492
522
  unless_conditions.each do |unless_condition|
@@ -500,8 +530,6 @@ module Grape
500
530
  true
501
531
  end
502
532
 
503
- private
504
-
505
533
  # All supported options.
506
534
  OPTIONS = [
507
535
  :as, :if, :unless, :using, :with, :proc, :documentation, :format_with, :safe, :if_extras, :unless_extras
@@ -544,7 +572,7 @@ module Grape
544
572
  raise ArgumentError, "#{key.inspect} is not a valid option." unless OPTIONS.include?(key)
545
573
  end
546
574
 
547
- options[:using] = options.delete(:with) if options.has_key?(:with)
575
+ options[:using] = options.delete(:with) if options.key?(:with)
548
576
  options
549
577
  end
550
578
  end
@@ -1,3 +1,3 @@
1
1
  module GrapeEntity
2
- VERSION = '0.4.2'
2
+ VERSION = '0.4.3'
3
3
  end
@@ -46,7 +46,7 @@ describe Grape::Entity do
46
46
  attr_accessor :prop1
47
47
 
48
48
  def initialize
49
- @prop1 = "value1"
49
+ @prop1 = 'value1'
50
50
  end
51
51
  end
52
52
 
@@ -56,7 +56,7 @@ describe Grape::Entity do
56
56
  end
57
57
 
58
58
  subject.expose(:bogus, using: EntitySpec::BogusEntity) do |entity|
59
- entity.prop1 = "MODIFIED 2"
59
+ entity.prop1 = 'MODIFIED 2'
60
60
  entity
61
61
  end
62
62
 
@@ -65,7 +65,7 @@ describe Grape::Entity do
65
65
  value.should be_instance_of EntitySpec::BogusEntity
66
66
 
67
67
  prop1 = value.send(:value_for, :prop1)
68
- prop1.should == "MODIFIED 2"
68
+ prop1.should == 'MODIFIED 2'
69
69
  end
70
70
 
71
71
  context 'with parameters passed to the block' do
@@ -100,37 +100,37 @@ describe Grape::Entity do
100
100
 
101
101
  it 'represents the exposure as a hash of its nested exposures' do
102
102
  subject.expose :awesome do
103
- subject.expose(:nested) { |_| "value" }
104
- subject.expose(:another_nested) { |_| "value" }
103
+ subject.expose(:nested) { |_| 'value' }
104
+ subject.expose(:another_nested) { |_| 'value' }
105
105
  end
106
106
 
107
107
  subject.represent({}).send(:value_for, :awesome).should == {
108
- nested: "value",
109
- another_nested: "value"
108
+ nested: 'value',
109
+ another_nested: 'value'
110
110
  }
111
111
  end
112
112
 
113
113
  it 'does not represent attributes, declared inside nested exposure, outside of it' do
114
114
  subject.expose :awesome do
115
- subject.expose(:nested) { |_| "value" }
116
- subject.expose(:another_nested) { |_| "value" }
115
+ subject.expose(:nested) { |_| 'value' }
116
+ subject.expose(:another_nested) { |_| 'value' }
117
117
  subject.expose :second_level_nested do
118
- subject.expose(:deeply_exposed_attr) { |_| "value" }
118
+ subject.expose(:deeply_exposed_attr) { |_| 'value' }
119
119
  end
120
120
  end
121
121
 
122
122
  subject.represent({}).serializable_hash.should == {
123
123
  awesome: {
124
- nested: "value",
125
- another_nested: "value",
124
+ nested: 'value',
125
+ another_nested: 'value',
126
126
  second_level_nested: {
127
- deeply_exposed_attr: "value"
127
+ deeply_exposed_attr: 'value'
128
128
  }
129
129
  }
130
130
  }
131
131
  end
132
132
 
133
- it "complex nested attributes" do
133
+ it 'complex nested attributes' do
134
134
  class ClassRoom < Grape::Entity
135
135
  expose(:parents, using: 'Parent') { |_| [{}, {}] }
136
136
  end
@@ -157,15 +157,15 @@ describe Grape::Entity do
157
157
  {
158
158
  user: { in_first: 'value' },
159
159
  children: [
160
- { user: { in_first: 'value', user_id: "value", display_id: "value" } },
161
- { user: { in_first: 'value', user_id: "value", display_id: "value" } }
160
+ { user: { in_first: 'value', user_id: 'value', display_id: 'value' } },
161
+ { user: { in_first: 'value', user_id: 'value', display_id: 'value' } }
162
162
  ]
163
163
  },
164
164
  {
165
165
  user: { in_first: 'value' },
166
166
  children: [
167
- { user: { in_first: 'value', user_id: "value", display_id: "value" } },
168
- { user: { in_first: 'value', user_id: "value", display_id: "value" } }
167
+ { user: { in_first: 'value', user_id: 'value', display_id: 'value' } },
168
+ { user: { in_first: 'value', user_id: 'value', display_id: 'value' } }
169
169
  ]
170
170
  }
171
171
  ]
@@ -175,7 +175,7 @@ describe Grape::Entity do
175
175
  it 'is safe if its nested exposures are safe' do
176
176
  subject.with_options safe: true do
177
177
  subject.expose :awesome do
178
- subject.expose(:nested) { |_| "value" }
178
+ subject.expose(:nested) { |_| 'value' }
179
179
  end
180
180
  subject.expose :not_awesome do
181
181
  subject.expose :nested
@@ -252,12 +252,12 @@ describe Grape::Entity do
252
252
  it 'formats an exposure with a :format_with lambda that returns a value from the entity instance' do
253
253
  object = Hash.new
254
254
 
255
- subject.expose(:size, format_with: lambda { |value| self.object.class.to_s })
255
+ subject.expose(:size, format_with: lambda { |_value| self.object.class.to_s })
256
256
  subject.represent(object).send(:value_for, :size).should == object.class.to_s
257
257
  end
258
258
 
259
259
  it 'formats an exposure with a :format_with symbol that returns a value from the entity instance' do
260
- subject.format_with :size_formatter do |date|
260
+ subject.format_with :size_formatter do |_date|
261
261
  self.object.class.to_s
262
262
  end
263
263
 
@@ -312,8 +312,8 @@ describe Grape::Entity do
312
312
  subject.exposures[:awesome_thing].should == { as: :extra_smooth }
313
313
  end
314
314
 
315
- it "merges nested :if option" do
316
- match_proc = lambda { |obj, opts| true }
315
+ it 'merges nested :if option' do
316
+ match_proc = lambda { |_obj, _opts| true }
317
317
 
318
318
  subject.class_eval do
319
319
  # Symbol
@@ -338,7 +338,7 @@ describe Grape::Entity do
338
338
  end
339
339
 
340
340
  it 'merges nested :unless option' do
341
- match_proc = lambda { |obj, opts| true }
341
+ match_proc = lambda { |_, _| true }
342
342
 
343
343
  subject.class_eval do
344
344
  # Symbol
@@ -382,10 +382,10 @@ describe Grape::Entity do
382
382
  end
383
383
 
384
384
  it 'overrides nested :proc option' do
385
- match_proc = lambda { |obj, opts| 'more awesomer' }
385
+ match_proc = lambda { |_obj, _opts| 'more awesomer' }
386
386
 
387
387
  subject.class_eval do
388
- with_options(proc: lambda { |obj, opts| 'awesome' }) do
388
+ with_options(proc: lambda { |_obj, _opts| 'awesome' }) do
389
389
  expose :awesome_thing, proc: match_proc
390
390
  end
391
391
  end
@@ -422,7 +422,7 @@ describe Grape::Entity do
422
422
 
423
423
  it 'adds the collection: true option if called with a collection' do
424
424
  representation = subject.represent(4.times.map { Object.new })
425
- representation.each { |r| r.options[:collection].should be_true }
425
+ representation.each { |r| r.options[:collection].should be true }
426
426
  end
427
427
 
428
428
  it 'returns a serialized hash of a single object if serializable: true' do
@@ -557,8 +557,8 @@ describe Grape::Entity do
557
557
  birthday: Time.gm(2012, 2, 27),
558
558
  fantasies: ['Unicorns', 'Double Rainbows', 'Nessy'],
559
559
  friends: [
560
- double(name: "Friend 1", email: 'friend1@example.com', fantasies: [], birthday: Time.gm(2012, 2, 27), friends: []),
561
- double(name: "Friend 2", email: 'friend2@example.com', fantasies: [], birthday: Time.gm(2012, 2, 27), friends: [])
560
+ double(name: 'Friend 1', email: 'friend1@example.com', fantasies: [], birthday: Time.gm(2012, 2, 27), friends: []),
561
+ double(name: 'Friend 2', email: 'friend2@example.com', fantasies: [], birthday: Time.gm(2012, 2, 27), friends: [])
562
562
  ]
563
563
  }
564
564
  }
@@ -575,7 +575,7 @@ describe Grape::Entity do
575
575
  expect { fresh_class.new(nil).serializable_hash }.not_to raise_error
576
576
  end
577
577
 
578
- context "with safe option" do
578
+ context 'with safe option' do
579
579
  it 'does not throw an exception when an attribute is not found on the object' do
580
580
  fresh_class.expose :name, :nonexistent_attribute, safe: true
581
581
  expect { fresh_class.new(model).serializable_hash }.not_to raise_error
@@ -602,24 +602,24 @@ describe Grape::Entity do
602
602
  end
603
603
  end
604
604
 
605
- context "without safe option" do
605
+ context 'without safe option' do
606
606
  it 'throws an exception when an attribute is not found on the object' do
607
607
  fresh_class.expose :name, :nonexistent_attribute
608
608
  expect { fresh_class.new(model).serializable_hash }.to raise_error
609
609
  end
610
610
 
611
611
  it "exposes attributes that don't exist on the object only when they are generated by a block" do
612
- fresh_class.expose :nonexistent_attribute do |model, _|
613
- "well, I do exist after all"
612
+ fresh_class.expose :nonexistent_attribute do |_model, _opts|
613
+ 'well, I do exist after all'
614
614
  end
615
615
  res = fresh_class.new(model).serializable_hash
616
616
  res.should have_key :nonexistent_attribute
617
617
  end
618
618
 
619
- it "does not expose attributes that are generated by a block but have not passed criteria" do
620
- fresh_class.expose :nonexistent_attribute, proc: lambda { |model, _|
621
- "I exist, but it is not yet my time to shine"
622
- }, if: lambda { |model, _| false }
619
+ it 'does not expose attributes that are generated by a block but have not passed criteria' do
620
+ fresh_class.expose :nonexistent_attribute, proc: lambda { |_model, _opts|
621
+ 'I exist, but it is not yet my time to shine'
622
+ }, if: lambda { |_model, _opts| false }
623
623
  res = fresh_class.new(model).serializable_hash
624
624
  res.should_not have_key :nonexistent_attribute
625
625
  end
@@ -631,17 +631,17 @@ describe Grape::Entity do
631
631
  end
632
632
  end
633
633
 
634
- fresh_class.expose :nonexistent_attribute, using: EntitySpec::TestEntity do |model, _|
635
- "well, I do exist after all"
634
+ fresh_class.expose :nonexistent_attribute, using: EntitySpec::TestEntity do |_model, _opts|
635
+ 'well, I do exist after all'
636
636
  end
637
637
  res = fresh_class.new(model).serializable_hash
638
638
  res.should have_key :nonexistent_attribute
639
639
  end
640
640
 
641
- it "does not expose attributes that are generated by a block but have not passed criteria" do
642
- fresh_class.expose :nonexistent_attribute, proc: lambda { |model, _|
643
- "I exist, but it is not yet my time to shine"
644
- }, if: lambda { |model, _| false }
641
+ it 'does not expose attributes that are generated by a block but have not passed criteria' do
642
+ fresh_class.expose :nonexistent_attribute, proc: lambda { |_, _|
643
+ 'I exist, but it is not yet my time to shine'
644
+ }, if: lambda { |_, _| false }
645
645
  res = fresh_class.new(model).serializable_hash
646
646
  res.should_not have_key :nonexistent_attribute
647
647
  end
@@ -649,14 +649,14 @@ describe Grape::Entity do
649
649
  context '#serializable_hash' do
650
650
  module EntitySpec
651
651
  class EmbeddedExample
652
- def serializable_hash(opts = {})
652
+ def serializable_hash(_opts = {})
653
653
  { abc: 'def' }
654
654
  end
655
655
  end
656
656
 
657
657
  class EmbeddedExampleWithHash
658
658
  def name
659
- "abc"
659
+ 'abc'
660
660
  end
661
661
 
662
662
  def embedded
@@ -666,7 +666,7 @@ describe Grape::Entity do
666
666
 
667
667
  class EmbeddedExampleWithMany
668
668
  def name
669
- "abc"
669
+ 'abc'
670
670
  end
671
671
 
672
672
  def embedded
@@ -676,7 +676,7 @@ describe Grape::Entity do
676
676
 
677
677
  class EmbeddedExampleWithOne
678
678
  def name
679
- "abc"
679
+ 'abc'
680
680
  end
681
681
 
682
682
  def embedded
@@ -688,19 +688,19 @@ describe Grape::Entity do
688
688
  it 'serializes embedded objects which respond to #serializable_hash' do
689
689
  fresh_class.expose :name, :embedded
690
690
  presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithOne.new)
691
- presenter.serializable_hash.should == { name: "abc", embedded: { abc: "def" } }
691
+ presenter.serializable_hash.should == { name: 'abc', embedded: { abc: 'def' } }
692
692
  end
693
693
 
694
694
  it 'serializes embedded arrays of objects which respond to #serializable_hash' do
695
695
  fresh_class.expose :name, :embedded
696
696
  presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithMany.new)
697
- presenter.serializable_hash.should == { name: "abc", embedded: [{ abc: "def" }, { abc: "def" }] }
697
+ presenter.serializable_hash.should == { name: 'abc', embedded: [{ abc: 'def' }, { abc: 'def' }] }
698
698
  end
699
699
 
700
700
  it 'serializes embedded hashes of objects which respond to #serializable_hash' do
701
701
  fresh_class.expose :name, :embedded
702
702
  presenter = fresh_class.new(EntitySpec::EmbeddedExampleWithHash.new)
703
- presenter.serializable_hash.should == { name: "abc", embedded: { a: nil, b: { abc: "def" } } }
703
+ presenter.serializable_hash.should == { name: 'abc', embedded: { a: nil, b: { abc: 'def' } } }
704
704
  end
705
705
  end
706
706
  end
@@ -755,7 +755,7 @@ describe Grape::Entity do
755
755
  rep.last.serializable_hash[:name].should == 'Friend 2'
756
756
  end
757
757
 
758
- it "passes through the proc which returns an array of objects with custom options(:using)" do
758
+ it 'passes through the proc which returns an array of objects with custom options(:using)' do
759
759
  module EntitySpec
760
760
  class FriendEntity < Grape::Entity
761
761
  root 'friends', 'friend'
@@ -764,7 +764,7 @@ describe Grape::Entity do
764
764
  end
765
765
 
766
766
  fresh_class.class_eval do
767
- expose :custom_friends, using: EntitySpec::FriendEntity do |user, options|
767
+ expose :custom_friends, using: EntitySpec::FriendEntity do |user, _opts|
768
768
  user.friends
769
769
  end
770
770
  end
@@ -776,7 +776,7 @@ describe Grape::Entity do
776
776
  rep.last.serializable_hash.should == { name: 'Friend 2', email: 'friend2@example.com' }
777
777
  end
778
778
 
779
- it "passes through the proc which returns single object with custom options(:using)" do
779
+ it 'passes through the proc which returns single object with custom options(:using)' do
780
780
  module EntitySpec
781
781
  class FriendEntity < Grape::Entity
782
782
  root 'friends', 'friend'
@@ -785,7 +785,7 @@ describe Grape::Entity do
785
785
  end
786
786
 
787
787
  fresh_class.class_eval do
788
- expose :first_friend, using: EntitySpec::FriendEntity do |user, options|
788
+ expose :first_friend, using: EntitySpec::FriendEntity do |user, _opts|
789
789
  user.friends.first
790
790
  end
791
791
  end
@@ -795,7 +795,7 @@ describe Grape::Entity do
795
795
  rep.serializable_hash.should == { name: 'Friend 1', email: 'friend1@example.com' }
796
796
  end
797
797
 
798
- it "passes through the proc which returns empty with custom options(:using)" do
798
+ it 'passes through the proc which returns empty with custom options(:using)' do
799
799
  module EntitySpec
800
800
  class FriendEntity < Grape::Entity
801
801
  root 'friends', 'friend'
@@ -804,8 +804,7 @@ describe Grape::Entity do
804
804
  end
805
805
 
806
806
  fresh_class.class_eval do
807
- expose :first_friend, using: EntitySpec::FriendEntity do |user, options|
808
-
807
+ expose :first_friend, using: EntitySpec::FriendEntity do |_user, _opts|
809
808
  end
810
809
  end
811
810
 
@@ -873,7 +872,7 @@ describe Grape::Entity do
873
872
  subject.send(:value_for, :fantasies).should == ['Nessy', 'Double Rainbows', 'Unicorns']
874
873
  end
875
874
 
876
- it "tries instance methods on the entity first" do
875
+ it 'tries instance methods on the entity first' do
877
876
  module EntitySpec
878
877
  class DelegatingEntity < Grape::Entity
879
878
  root 'friends', 'friend'
@@ -883,18 +882,18 @@ describe Grape::Entity do
883
882
  private
884
883
 
885
884
  def name
886
- "cooler name"
885
+ 'cooler name'
887
886
  end
888
887
  end
889
888
  end
890
889
 
891
- friend = double("Friend", name: "joe", email: "joe@example.com")
890
+ friend = double('Friend', name: 'joe', email: 'joe@example.com')
892
891
  rep = EntitySpec::DelegatingEntity.new(friend)
893
- rep.send(:value_for, :name).should == "cooler name"
894
- rep.send(:value_for, :email).should == "joe@example.com"
892
+ rep.send(:value_for, :name).should == 'cooler name'
893
+ rep.send(:value_for, :email).should == 'joe@example.com'
895
894
  end
896
895
 
897
- context "using" do
896
+ context 'using' do
898
897
  before do
899
898
  module EntitySpec
900
899
  class UserEntity < Grape::Entity
@@ -902,15 +901,15 @@ describe Grape::Entity do
902
901
  end
903
902
  end
904
903
  end
905
- it "string" do
904
+ it 'string' do
906
905
  fresh_class.class_eval do
907
- expose :friends, using: "EntitySpec::UserEntity"
906
+ expose :friends, using: 'EntitySpec::UserEntity'
908
907
  end
909
908
 
910
909
  rep = subject.send(:value_for, :friends)
911
910
  rep.should be_kind_of Array
912
911
  rep.size.should == 2
913
- rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }.should be_true
912
+ rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }.should be true
914
913
  end
915
914
 
916
915
  it 'class' do
@@ -921,7 +920,7 @@ describe Grape::Entity do
921
920
  rep = subject.send(:value_for, :friends)
922
921
  rep.should be_kind_of Array
923
922
  rep.size.should == 2
924
- rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }.should be_true
923
+ rep.all? { |r| r.is_a?(EntitySpec::UserEntity) }.should be true
925
924
  end
926
925
  end
927
926
  end
@@ -934,7 +933,7 @@ describe Grape::Entity do
934
933
  end
935
934
 
936
935
  it 'returns each defined documentation hash' do
937
- doc = { type: "foo", desc: "bar" }
936
+ doc = { type: 'foo', desc: 'bar' }
938
937
  fresh_class.expose :name, documentation: doc
939
938
  fresh_class.expose :email, documentation: doc
940
939
  fresh_class.expose :birthday
@@ -943,7 +942,7 @@ describe Grape::Entity do
943
942
  end
944
943
 
945
944
  it 'returns each defined documentation hash with :as param considering' do
946
- doc = { type: "foo", desc: "bar" }
945
+ doc = { type: 'foo', desc: 'bar' }
947
946
  fresh_class.expose :name, documentation: doc, as: :label
948
947
  fresh_class.expose :email, documentation: doc
949
948
  fresh_class.expose :birthday
@@ -973,54 +972,54 @@ describe Grape::Entity do
973
972
  it 'only passes through hash :if exposure if all attributes match' do
974
973
  exposure_options = { if: { condition1: true, condition2: true } }
975
974
 
976
- subject.send(:conditions_met?, exposure_options, {}).should be_false
977
- subject.send(:conditions_met?, exposure_options, condition1: true).should be_false
978
- subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true).should be_true
979
- subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true).should be_false
980
- subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true).should be_true
975
+ subject.send(:conditions_met?, exposure_options, {}).should be false
976
+ subject.send(:conditions_met?, exposure_options, condition1: true).should be false
977
+ subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true).should be true
978
+ subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true).should be false
979
+ subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true).should be true
981
980
  end
982
981
 
983
982
  it 'looks for presence/truthiness if a symbol is passed' do
984
983
  exposure_options = { if: :condition1 }
985
984
 
986
- subject.send(:conditions_met?, exposure_options, {}).should be_false
987
- subject.send(:conditions_met?, exposure_options, condition1: true).should be_true
988
- subject.send(:conditions_met?, exposure_options, condition1: false).should be_false
989
- subject.send(:conditions_met?, exposure_options, condition1: nil).should be_false
985
+ subject.send(:conditions_met?, exposure_options, {}).should be false
986
+ subject.send(:conditions_met?, exposure_options, condition1: true).should be true
987
+ subject.send(:conditions_met?, exposure_options, condition1: false).should be false
988
+ subject.send(:conditions_met?, exposure_options, condition1: nil).should be false
990
989
  end
991
990
 
992
991
  it 'looks for absence/falsiness if a symbol is passed' do
993
992
  exposure_options = { unless: :condition1 }
994
993
 
995
- subject.send(:conditions_met?, exposure_options, {}).should be_true
996
- subject.send(:conditions_met?, exposure_options, condition1: true).should be_false
997
- subject.send(:conditions_met?, exposure_options, condition1: false).should be_true
998
- subject.send(:conditions_met?, exposure_options, condition1: nil).should be_true
994
+ subject.send(:conditions_met?, exposure_options, {}).should be true
995
+ subject.send(:conditions_met?, exposure_options, condition1: true).should be false
996
+ subject.send(:conditions_met?, exposure_options, condition1: false).should be true
997
+ subject.send(:conditions_met?, exposure_options, condition1: nil).should be true
999
998
  end
1000
999
 
1001
1000
  it 'only passes through proc :if exposure if it returns truthy value' do
1002
1001
  exposure_options = { if: lambda { |_, opts| opts[:true] } }
1003
1002
 
1004
- subject.send(:conditions_met?, exposure_options, true: false).should be_false
1005
- subject.send(:conditions_met?, exposure_options, true: true).should be_true
1003
+ subject.send(:conditions_met?, exposure_options, true: false).should be false
1004
+ subject.send(:conditions_met?, exposure_options, true: true).should be true
1006
1005
  end
1007
1006
 
1008
1007
  it 'only passes through hash :unless exposure if any attributes do not match' do
1009
1008
  exposure_options = { unless: { condition1: true, condition2: true } }
1010
1009
 
1011
- subject.send(:conditions_met?, exposure_options, {}).should be_true
1012
- subject.send(:conditions_met?, exposure_options, condition1: true).should be_false
1013
- subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true).should be_false
1014
- subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true).should be_false
1015
- subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true).should be_false
1016
- subject.send(:conditions_met?, exposure_options, condition1: false, condition2: false).should be_true
1010
+ subject.send(:conditions_met?, exposure_options, {}).should be true
1011
+ subject.send(:conditions_met?, exposure_options, condition1: true).should be false
1012
+ subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true).should be false
1013
+ subject.send(:conditions_met?, exposure_options, condition1: false, condition2: true).should be false
1014
+ subject.send(:conditions_met?, exposure_options, condition1: true, condition2: true, other: true).should be false
1015
+ subject.send(:conditions_met?, exposure_options, condition1: false, condition2: false).should be true
1017
1016
  end
1018
1017
 
1019
1018
  it 'only passes through proc :unless exposure if it returns falsy value' do
1020
1019
  exposure_options = { unless: lambda { |_, options| options[:true] == true } }
1021
1020
 
1022
- subject.send(:conditions_met?, exposure_options, true: false).should be_true
1023
- subject.send(:conditions_met?, exposure_options, true: true).should be_false
1021
+ subject.send(:conditions_met?, exposure_options, true: false).should be true
1022
+ subject.send(:conditions_met?, exposure_options, true: true).should be false
1024
1023
  end
1025
1024
  end
1026
1025
 
metadata CHANGED
@@ -1,88 +1,100 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-entity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Michael Bleigh
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-04-03 00:00:00.000000000 Z
12
+ date: 2014-06-12 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: activesupport
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: '0'
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0'
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: multi_json
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
- - - '>='
35
+ - - ! '>='
32
36
  - !ruby/object:Gem::Version
33
37
  version: 1.3.2
34
38
  type: :runtime
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
- - - '>='
43
+ - - ! '>='
39
44
  - !ruby/object:Gem::Version
40
45
  version: 1.3.2
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: rake
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
- - - '>='
51
+ - - ! '>='
46
52
  - !ruby/object:Gem::Version
47
53
  version: '0'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
- - - '>='
59
+ - - ! '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
62
  - !ruby/object:Gem::Dependency
56
63
  name: maruku
57
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
58
66
  requirements:
59
- - - '>='
67
+ - - ! '>='
60
68
  - !ruby/object:Gem::Version
61
69
  version: '0'
62
70
  type: :development
63
71
  prerelease: false
64
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
- - - '>='
75
+ - - ! '>='
67
76
  - !ruby/object:Gem::Version
68
77
  version: '0'
69
78
  - !ruby/object:Gem::Dependency
70
79
  name: yard
71
80
  requirement: !ruby/object:Gem::Requirement
81
+ none: false
72
82
  requirements:
73
- - - '>='
83
+ - - ! '>='
74
84
  - !ruby/object:Gem::Version
75
85
  version: '0'
76
86
  type: :development
77
87
  prerelease: false
78
88
  version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
79
90
  requirements:
80
- - - '>='
91
+ - - ! '>='
81
92
  - !ruby/object:Gem::Version
82
93
  version: '0'
83
94
  - !ruby/object:Gem::Dependency
84
95
  name: rspec
85
96
  requirement: !ruby/object:Gem::Requirement
97
+ none: false
86
98
  requirements:
87
99
  - - ~>
88
100
  - !ruby/object:Gem::Version
@@ -90,6 +102,7 @@ dependencies:
90
102
  type: :development
91
103
  prerelease: false
92
104
  version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
93
106
  requirements:
94
107
  - - ~>
95
108
  - !ruby/object:Gem::Version
@@ -97,15 +110,17 @@ dependencies:
97
110
  - !ruby/object:Gem::Dependency
98
111
  name: bundler
99
112
  requirement: !ruby/object:Gem::Requirement
113
+ none: false
100
114
  requirements:
101
- - - '>='
115
+ - - ! '>='
102
116
  - !ruby/object:Gem::Version
103
117
  version: '0'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
107
122
  requirements:
108
- - - '>='
123
+ - - ! '>='
109
124
  - !ruby/object:Gem::Version
110
125
  version: '0'
111
126
  description: Extracted from Grape, A Ruby framework for rapid API development with
@@ -129,6 +144,7 @@ files:
129
144
  - README.md
130
145
  - RELEASING.md
131
146
  - Rakefile
147
+ - bench/serializing.rb
132
148
  - grape-entity.gemspec
133
149
  - lib/grape-entity.rb
134
150
  - lib/grape_entity.rb
@@ -139,26 +155,33 @@ files:
139
155
  homepage: https://github.com/intridea/grape-entity
140
156
  licenses:
141
157
  - MIT
142
- metadata: {}
143
158
  post_install_message:
144
159
  rdoc_options: []
145
160
  require_paths:
146
161
  - lib
147
162
  required_ruby_version: !ruby/object:Gem::Requirement
163
+ none: false
148
164
  requirements:
149
- - - '>='
165
+ - - ! '>='
150
166
  - !ruby/object:Gem::Version
151
167
  version: '0'
168
+ segments:
169
+ - 0
170
+ hash: -128903481528041369
152
171
  required_rubygems_version: !ruby/object:Gem::Requirement
172
+ none: false
153
173
  requirements:
154
- - - '>='
174
+ - - ! '>='
155
175
  - !ruby/object:Gem::Version
156
176
  version: '0'
177
+ segments:
178
+ - 0
179
+ hash: -128903481528041369
157
180
  requirements: []
158
181
  rubyforge_project: grape-entity
159
- rubygems_version: 2.0.14
182
+ rubygems_version: 1.8.25
160
183
  signing_key:
161
- specification_version: 4
184
+ specification_version: 3
162
185
  summary: A simple facade for managing the relationship between your model and API.
163
186
  test_files:
164
187
  - spec/grape_entity/entity_spec.rb
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 4165ed088e82e10584f53bf9436053d18c647b41
4
- data.tar.gz: be2d0321743d7d27dcc1d3c1f30a9e5483e5c92d
5
- SHA512:
6
- metadata.gz: 2059bf88e77709d197f923e0c4659747d83ed2a859325cecdcbb286cdc828a82a07a8c6571df886be941059b6c2c98110327c04809d453824ff377d387919c5e
7
- data.tar.gz: e2c16d535392af2daf37b834589f63933f66f960c15e199956419dd894a3bd95ab06b76a53b3ca594714f854845c4137b65add706b8be02c8d6e580b7d3e2062