parse-ruby-client 0.2.0 → 0.3.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.
Files changed (49) hide show
  1. checksums.yaml +15 -0
  2. data/.travis.yml +2 -1
  3. data/Gemfile +4 -6
  4. data/Gemfile.lock +18 -18
  5. data/README.md +1 -1
  6. data/Rakefile +1 -1
  7. data/VERSION +1 -1
  8. data/example.rb +2 -1
  9. data/fixtures/vcr_cassettes/test_batch_update_nils_delete_keys.yml +57 -0
  10. data/fixtures/vcr_cassettes/test_empty_response.yml +72 -0
  11. data/fixtures/vcr_cassettes/test_get_missing.yml +95 -38
  12. data/fixtures/vcr_cassettes/test_image_file_associate_with_object.yml +2089 -0
  13. data/fixtures/vcr_cassettes/test_image_file_save.yml +1928 -0
  14. data/fixtures/vcr_cassettes/test_object_id.yml +56 -0
  15. data/fixtures/vcr_cassettes/test_reset_password.yml +109 -0
  16. data/fixtures/vcr_cassettes/test_retries.yml +357 -0
  17. data/fixtures/vcr_cassettes/test_retries_404.yml +72 -0
  18. data/fixtures/vcr_cassettes/test_retries_404_correct.yml +72 -0
  19. data/fixtures/vcr_cassettes/test_retries_json_error.yml +357 -0
  20. data/fixtures/vcr_cassettes/test_retries_server_error.yml +357 -0
  21. data/fixtures/vcr_cassettes/test_server_update.yml +248 -191
  22. data/fixtures/vcr_cassettes/test_user_login.yml +276 -0
  23. data/fixtures/vcr_cassettes/test_xget.yml +280 -0
  24. data/lib/faraday/better_retry.rb +94 -0
  25. data/lib/faraday/extended_parse_json.rb +39 -0
  26. data/lib/faraday/get_method_override.rb +32 -0
  27. data/lib/parse-ruby-client.rb +9 -9
  28. data/lib/parse/batch.rb +2 -3
  29. data/lib/parse/client.rb +72 -161
  30. data/lib/parse/cloud.rb +2 -1
  31. data/lib/parse/datatypes.rb +6 -1
  32. data/lib/parse/error.rb +7 -3
  33. data/lib/parse/installation.rb +18 -0
  34. data/lib/parse/model.rb +2 -1
  35. data/lib/parse/object.rb +7 -1
  36. data/lib/parse/protocol.rb +10 -0
  37. data/lib/parse/push.rb +3 -0
  38. data/lib/parse/query.rb +5 -2
  39. data/lib/parse/user.rb +1 -0
  40. data/lib/parse/util.rb +2 -0
  41. data/parse-ruby-client.gemspec +35 -15
  42. data/test/helper.rb +55 -1
  43. data/test/middleware/better_retry_test.rb +57 -0
  44. data/test/middleware/extend_parse_json_test.rb +55 -0
  45. data/test/test_client.rb +78 -28
  46. data/test/test_datatypes.rb +14 -0
  47. data/test/test_object.rb +16 -0
  48. metadata +39 -34
  49. data/lib/parse/http_client.rb +0 -84
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Parse
2
3
  module Cloud
3
4
 
@@ -20,4 +21,4 @@ module Parse
20
21
  end
21
22
 
22
23
  end
23
- end
24
+ end
@@ -1,3 +1,6 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'time'
3
+ require 'date'
1
4
  require 'base64'
2
5
 
3
6
  module Parse
@@ -71,12 +74,14 @@ module Parse
71
74
  attr_accessor :value
72
75
 
73
76
  def initialize(data)
74
- if data.is_a? DateTime
77
+ if data.respond_to?(:iso8601)
75
78
  @value = data
76
79
  elsif data.is_a? Hash
77
80
  @value = DateTime.parse data["iso"]
78
81
  elsif data.is_a? String
79
82
  @value = DateTime.parse data
83
+ else
84
+ raise "data doesn't act like time #{data.inspect}"
80
85
  end
81
86
  end
82
87
 
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Parse
2
3
 
3
4
  # Base exception class for errors thrown by the Parse
@@ -17,8 +18,8 @@ module Parse
17
18
  def initialize(response)
18
19
  @response = response
19
20
  if response
20
- @code = response["code"]
21
- @error = response["error"]
21
+ @code = response["code"]
22
+ @error = response["error"]
22
23
  end
23
24
 
24
25
  super("#{@code}: #{@error}")
@@ -33,4 +34,7 @@ module Parse
33
34
  end
34
35
  end
35
36
 
36
- end
37
+ class ParseProtocolRetry < ParseProtocolError
38
+ end
39
+
40
+ end
@@ -0,0 +1,18 @@
1
+ require 'parse/protocol'
2
+ require 'parse/client'
3
+ require 'parse/error'
4
+ require 'parse/object'
5
+
6
+ module Parse
7
+ class Installation < Parse::Object
8
+
9
+ def initialize(data = nil)
10
+ super(Parse::Protocol::CLASS_INSTALLATION, data)
11
+ end
12
+
13
+ def uri
14
+ Protocol.installation_uri @parse_object_id
15
+ end
16
+
17
+ end
18
+ end
@@ -1,7 +1,8 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Parse
2
3
  class Model < Parse::Object
3
4
  def initialize
4
5
  super(self.class.to_s)
5
6
  end
6
7
  end
7
- end
8
+ end
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'parse/protocol'
2
3
  require 'parse/client'
3
4
  require 'parse/error'
@@ -95,9 +96,14 @@ module Parse
95
96
  self.merge(Parse::Protocol::KEY_CLASS_NAME => class_name)
96
97
  end
97
98
 
99
+ # Handle the addition of Array#to_h in Ruby 2.1
100
+ def should_call_to_h?(value)
101
+ value.respond_to?(:to_h) && !value.kind_of?(Array)
102
+ end
103
+
98
104
  def to_h(*a)
99
105
  Hash[rest_api_hash.map do |key, value|
100
- [key, value.respond_to?(:to_h) ? value.to_h : value]
106
+ [key, should_call_to_h?(value) ? value.to_h : value]
101
107
  end]
102
108
  end
103
109
  alias :as_json :to_h
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Parse
2
3
  # A module which encapsulates the specifics of Parse's REST API.
3
4
  module Protocol
@@ -141,6 +142,15 @@ module Parse
141
142
  end
142
143
  end
143
144
 
145
+ # Construct a uri referencing a given Parse installation
146
+ # class or instance (of object_id is non-nil).
147
+ def Protocol.installation_uri(object_id = nil)
148
+ if object_id
149
+ "/#{VERSION}/installations/#{object_id}"
150
+ else
151
+ "/#{VERSION}/installations"
152
+ end
153
+ end
144
154
 
145
155
  # Construct a uri referencing a given Parse user
146
156
  # instance or the users category.
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'cgi'
2
3
  require 'parse/error'
3
4
 
@@ -9,6 +10,7 @@ module Parse
9
10
  attr_accessor :type
10
11
  attr_accessor :expiration_time_interval
11
12
  attr_accessor :expiration_time
13
+ attr_accessor :push_time
12
14
  attr_accessor :data
13
15
 
14
16
  def initialize(data, channel = "")
@@ -33,6 +35,7 @@ module Parse
33
35
 
34
36
  body.merge!({ :expiration_interval => @expiration_time_interval }) if @expiration_time_interval
35
37
  body.merge!({ :expiration_time => @expiration_time }) if @expiration_time
38
+ body.merge!({ :push_time => @push_time }) if @push_time
36
39
  body.merge!({ :type => @type }) if @type
37
40
 
38
41
  response = Parse.client.request uri, :post, body.to_json, nil
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'cgi'
2
3
 
3
4
  module Parse
@@ -119,11 +120,13 @@ module Parse
119
120
  uri = Protocol.class_uri @class_name
120
121
  if @class_name == Parse::Protocol::CLASS_USER
121
122
  uri = Protocol.user_uri
123
+ elsif @class_name == Parse::Protocol::CLASS_INSTALLATION
124
+ uri = Protocol.installation_uri
122
125
  end
123
- query = { "where" => CGI.escape(where_as_json.to_json) }
126
+ query = { "where" => where_as_json.to_json }
124
127
  set_order(query)
125
128
  [:count, :limit, :skip, :include].each {|a| merge_attribute(a, query)}
126
- Parse.client.logger.info{"Parse query for #{uri} #{CGI.unescape(query.inspect)}"}
129
+ Parse.client.logger.info{"Parse query for #{uri} #{query.inspect}"}
127
130
  response = Parse.client.request uri, :get, nil, query
128
131
 
129
132
  if response.is_a?(Hash) && response.has_key?(Protocol::KEY_RESULTS) && response[Protocol::KEY_RESULTS].is_a?(Array)
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'parse/protocol'
2
3
  require 'parse/client'
3
4
  require 'parse/error'
@@ -1,3 +1,5 @@
1
+ # -*- encoding : utf-8 -*-
2
+
1
3
  module Parse
2
4
 
3
5
  # Parse a JSON representation into a fully instantiated
@@ -2,14 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
+ # stub: parse-ruby-client 0.3.0 ruby lib
5
6
 
6
7
  Gem::Specification.new do |s|
7
8
  s.name = "parse-ruby-client"
8
- s.version = "0.2.0"
9
+ s.version = "0.3.0"
9
10
 
10
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
11
13
  s.authors = ["Alan deLevie", "Adam Alpern"]
12
- s.date = "2013-10-08"
14
+ s.date = "2014-07-31"
13
15
  s.description = "A simple Ruby client for the parse.com REST API"
14
16
  s.email = "adelevie@gmail.com"
15
17
  s.extra_rdoc_files = [
@@ -40,16 +42,26 @@ Gem::Specification.new do |s|
40
42
  "fixtures/vcr_cassettes/test_decrement.yml",
41
43
  "fixtures/vcr_cassettes/test_deep_parse.yml",
42
44
  "fixtures/vcr_cassettes/test_destroy.yml",
45
+ "fixtures/vcr_cassettes/test_empty_response.yml",
43
46
  "fixtures/vcr_cassettes/test_eq_pointerize.yml",
44
47
  "fixtures/vcr_cassettes/test_equality.yml",
45
48
  "fixtures/vcr_cassettes/test_get.yml",
46
49
  "fixtures/vcr_cassettes/test_get_missing.yml",
50
+ "fixtures/vcr_cassettes/test_image_file_associate_with_object.yml",
51
+ "fixtures/vcr_cassettes/test_image_file_save.yml",
47
52
  "fixtures/vcr_cassettes/test_include.yml",
48
53
  "fixtures/vcr_cassettes/test_new_model.yml",
49
54
  "fixtures/vcr_cassettes/test_new_object.yml",
50
55
  "fixtures/vcr_cassettes/test_nils_delete_keys.yml",
56
+ "fixtures/vcr_cassettes/test_object_id.yml",
51
57
  "fixtures/vcr_cassettes/test_parse_delete.yml",
52
58
  "fixtures/vcr_cassettes/test_pointer.yml",
59
+ "fixtures/vcr_cassettes/test_reset_password.yml",
60
+ "fixtures/vcr_cassettes/test_retries.yml",
61
+ "fixtures/vcr_cassettes/test_retries_404.yml",
62
+ "fixtures/vcr_cassettes/test_retries_404_correct.yml",
63
+ "fixtures/vcr_cassettes/test_retries_json_error.yml",
64
+ "fixtures/vcr_cassettes/test_retries_server_error.yml",
53
65
  "fixtures/vcr_cassettes/test_save_with_sub_objects.yml",
54
66
  "fixtures/vcr_cassettes/test_saving_boolean_values.yml",
55
67
  "fixtures/vcr_cassettes/test_saving_nested_objects.yml",
@@ -58,15 +70,19 @@ Gem::Specification.new do |s|
58
70
  "fixtures/vcr_cassettes/test_text_file_save.yml",
59
71
  "fixtures/vcr_cassettes/test_update.yml",
60
72
  "fixtures/vcr_cassettes/test_updated_at.yml",
73
+ "fixtures/vcr_cassettes/test_user_login.yml",
61
74
  "fixtures/vcr_cassettes/test_user_save.yml",
62
75
  "fixtures/vcr_cassettes/test_xget.yml",
76
+ "lib/faraday/better_retry.rb",
77
+ "lib/faraday/extended_parse_json.rb",
78
+ "lib/faraday/get_method_override.rb",
63
79
  "lib/parse-ruby-client.rb",
64
80
  "lib/parse/batch.rb",
65
81
  "lib/parse/client.rb",
66
82
  "lib/parse/cloud.rb",
67
83
  "lib/parse/datatypes.rb",
68
84
  "lib/parse/error.rb",
69
- "lib/parse/http_client.rb",
85
+ "lib/parse/installation.rb",
70
86
  "lib/parse/model.rb",
71
87
  "lib/parse/object.rb",
72
88
  "lib/parse/protocol.rb",
@@ -78,6 +94,8 @@ Gem::Specification.new do |s|
78
94
  "test/cloud_functions/MyCloudCode/cloud/main.js",
79
95
  "test/config/global.json",
80
96
  "test/helper.rb",
97
+ "test/middleware/better_retry_test.rb",
98
+ "test/middleware/extend_parse_json_test.rb",
81
99
  "test/parsers.jpg",
82
100
  "test/test_batch.rb",
83
101
  "test/test_client.rb",
@@ -95,47 +113,49 @@ Gem::Specification.new do |s|
95
113
  ]
96
114
  s.homepage = "http://github.com/adelevie/parse-ruby-client"
97
115
  s.licenses = ["MIT"]
98
- s.require_paths = ["lib"]
99
- s.rubygems_version = "1.8.24"
116
+ s.rubygems_version = "2.2.2"
100
117
  s.summary = "A simple Ruby client for the parse.com REST API"
101
118
 
102
119
  if s.respond_to? :specification_version then
103
- s.specification_version = 3
120
+ s.specification_version = 4
104
121
 
105
122
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
106
- s.add_runtime_dependency(%q<patron>, [">= 0"])
107
- s.add_runtime_dependency(%q<iron_mq>, [">= 0"])
123
+ s.add_runtime_dependency(%q<faraday>, [">= 0"])
124
+ s.add_runtime_dependency(%q<faraday_middleware>, [">= 0"])
108
125
  s.add_development_dependency(%q<bundler>, [">= 0"])
109
126
  s.add_development_dependency(%q<shoulda>, [">= 0"])
110
127
  s.add_development_dependency(%q<test-unit>, ["= 2.5.0"])
111
128
  s.add_development_dependency(%q<mocha>, ["= 0.12.0"])
112
129
  s.add_development_dependency(%q<jeweler>, ["~> 1.8.5"])
113
130
  s.add_development_dependency(%q<simplecov>, [">= 0"])
114
- s.add_development_dependency(%q<webmock>, [">= 0"])
131
+ s.add_development_dependency(%q<webmock>, ["~> 1.9.0"])
115
132
  s.add_development_dependency(%q<vcr>, [">= 0"])
133
+ s.add_development_dependency(%q<pry>, [">= 0"])
116
134
  else
117
- s.add_dependency(%q<patron>, [">= 0"])
118
- s.add_dependency(%q<iron_mq>, [">= 0"])
135
+ s.add_dependency(%q<faraday>, [">= 0"])
136
+ s.add_dependency(%q<faraday_middleware>, [">= 0"])
119
137
  s.add_dependency(%q<bundler>, [">= 0"])
120
138
  s.add_dependency(%q<shoulda>, [">= 0"])
121
139
  s.add_dependency(%q<test-unit>, ["= 2.5.0"])
122
140
  s.add_dependency(%q<mocha>, ["= 0.12.0"])
123
141
  s.add_dependency(%q<jeweler>, ["~> 1.8.5"])
124
142
  s.add_dependency(%q<simplecov>, [">= 0"])
125
- s.add_dependency(%q<webmock>, [">= 0"])
143
+ s.add_dependency(%q<webmock>, ["~> 1.9.0"])
126
144
  s.add_dependency(%q<vcr>, [">= 0"])
145
+ s.add_dependency(%q<pry>, [">= 0"])
127
146
  end
128
147
  else
129
- s.add_dependency(%q<patron>, [">= 0"])
130
- s.add_dependency(%q<iron_mq>, [">= 0"])
148
+ s.add_dependency(%q<faraday>, [">= 0"])
149
+ s.add_dependency(%q<faraday_middleware>, [">= 0"])
131
150
  s.add_dependency(%q<bundler>, [">= 0"])
132
151
  s.add_dependency(%q<shoulda>, [">= 0"])
133
152
  s.add_dependency(%q<test-unit>, ["= 2.5.0"])
134
153
  s.add_dependency(%q<mocha>, ["= 0.12.0"])
135
154
  s.add_dependency(%q<jeweler>, ["~> 1.8.5"])
136
155
  s.add_dependency(%q<simplecov>, [">= 0"])
137
- s.add_dependency(%q<webmock>, [">= 0"])
156
+ s.add_dependency(%q<webmock>, ["~> 1.9.0"])
138
157
  s.add_dependency(%q<vcr>, [">= 0"])
158
+ s.add_dependency(%q<pry>, [">= 0"])
139
159
  end
140
160
  end
141
161
 
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'rubygems'
2
3
  require 'bundler'
3
4
  begin
@@ -52,4 +53,57 @@ class ParseTestCase < Test::Unit::TestCase
52
53
  def setup
53
54
  @client = Parse.init(:logger => Logger.new(STDERR).tap{|l| l.level = Logger::ERROR})
54
55
  end
55
- end
56
+ end
57
+
58
+ module Faraday
59
+ module LiveServerConfig
60
+ def live_server=(value)
61
+ @@live_server = case value
62
+ when /^http/
63
+ URI(value)
64
+ when /./
65
+ URI('http://127.0.0.1:4567')
66
+ end
67
+ end
68
+
69
+ def live_server?
70
+ defined? @@live_server
71
+ end
72
+
73
+ # Returns an object that responds to `host` and `port`.
74
+ def live_server
75
+ live_server? and @@live_server
76
+ end
77
+ end
78
+ class TestCase < Test::Unit::TestCase
79
+ extend LiveServerConfig
80
+ self.live_server = ENV['LIVE']
81
+
82
+ def test_default
83
+ assert true
84
+ end unless defined? ::MiniTest
85
+
86
+ def capture_warnings
87
+ old, $stderr = $stderr, StringIO.new
88
+ begin
89
+ yield
90
+ $stderr.string
91
+ ensure
92
+ $stderr = old
93
+ end
94
+ end
95
+
96
+ def self.jruby?
97
+ defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE
98
+ end
99
+
100
+ def self.rbx?
101
+ defined? RUBY_ENGINE and 'rbx' == RUBY_ENGINE
102
+ end
103
+
104
+ def self.ssl_mode?
105
+ ENV['SSL'] == 'yes'
106
+ end
107
+ end
108
+ end
109
+
@@ -0,0 +1,57 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'helper'
3
+
4
+ module Middleware
5
+ class BeterRetryTest < Faraday::TestCase
6
+ def setup
7
+ @times_called = 0
8
+ end
9
+
10
+ def conn(retry_options = {})
11
+ Faraday.new do |b|
12
+ b.use Faraday::BetterRetry, retry_options
13
+ b.adapter :test do |stub|
14
+ stub.post('/unstable') {
15
+ @times_called += 1
16
+ @explode.call @times_called
17
+ }
18
+ end
19
+ end
20
+ end
21
+
22
+ def test_unhandled_error
23
+ @explode = lambda {|n| raise "boom!" }
24
+ assert_raise(RuntimeError) { conn.post("/unstable") }
25
+ assert_equal 1, @times_called
26
+ end
27
+
28
+ def test_handled_error
29
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
30
+ assert_raise(Errno::ETIMEDOUT) { conn.post("/unstable") }
31
+ assert_equal 3, @times_called
32
+ end
33
+
34
+ def test_new_max_retries
35
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
36
+ assert_raise(Errno::ETIMEDOUT) { conn(:max => 3).post("/unstable") }
37
+ assert_equal 4, @times_called
38
+ end
39
+
40
+ def test_interval
41
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
42
+ started = Time.now
43
+ assert_raise(Errno::ETIMEDOUT) {
44
+ conn(:max => 2, :interval => 0.1).post("/unstable")
45
+ }
46
+ assert_in_delta 0.2, Time.now - started, 0.03
47
+ end
48
+
49
+ def test_custom_exceptions
50
+ @explode = lambda {|n| raise "boom!" }
51
+ assert_raise(RuntimeError) {
52
+ conn(:exceptions => StandardError).post("/unstable")
53
+ }
54
+ assert_equal 3, @times_called
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,55 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'helper'
3
+
4
+ module Middleware
5
+ class ExtedParseJsonTest < Faraday::TestCase
6
+
7
+ def conn(retry_options = {})
8
+ Faraday.new do |b|
9
+ b.use Faraday::ExtendedParseJson
10
+ b.adapter :test do |stub|
11
+ stub.get('/invalid_json') { [200, {}, 'something'] }
12
+ stub.get('/valid_json') { [200, {}, {'var' => 1}.to_json] }
13
+ stub.get('/parse_error_code') { [403, {}, {'code' => Parse::Protocol::ERROR_INTERNAL}.to_json] }
14
+ stub.get('/empty_response') { [403, {}, ''] }
15
+ stub.get('/404') { [404, {}, {}.to_json] }
16
+ stub.get('/500') { [500, {}, {'text' => 'Internal Server Error'}.to_json] }
17
+ end
18
+ end
19
+ end
20
+
21
+ def test_invalid_json
22
+ assert_raise(Faraday::Error::ParsingError) { conn.get("/invalid_json") }
23
+ end
24
+
25
+ def test_valid_json
26
+ resp = conn.get("/valid_json")
27
+ assert_equal 200, resp.status
28
+ assert_equal ({'var' => 1}), resp.body
29
+ end
30
+
31
+ def test_empty_response
32
+ ex = assert_raise(Parse::ParseProtocolError) { conn.get("/empty_response") }
33
+ assert_match /403/, ex.to_s
34
+ assert_equal "HTTP Status 403 Body ", ex.error
35
+ end
36
+
37
+ def test_parse_error_code
38
+ ex = assert_raise(Parse::ParseProtocolError) { conn.get("/parse_error_code") }
39
+ assert_match /403/, ex.to_s
40
+ assert_equal Parse::Protocol::ERROR_INTERNAL, ex.code
41
+ end
42
+
43
+ def test_404
44
+ ex = assert_raise(Parse::ParseProtocolError) { conn.get("/404") }
45
+ assert_match /404/, ex.to_s
46
+ end
47
+
48
+ def test_500
49
+ ex = assert_raise(Parse::ParseProtocolError) { conn.get("/500") }
50
+ assert_match /500/, ex.to_s
51
+ assert_match /Internal Server Error/, ex.to_s
52
+ end
53
+
54
+ end
55
+ end