ballast 1.9.3 → 2.0.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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +35 -0
  3. data/.travis-gemfile +4 -5
  4. data/.travis.yml +3 -2
  5. data/CHANGELOG.md +62 -6
  6. data/Gemfile +7 -8
  7. data/README.md +2 -2
  8. data/ballast.gemspec +9 -12
  9. data/doc/Ballast/AjaxResponse.html +1380 -0
  10. data/doc/Ballast/Concerns/AjaxHandling.html +662 -0
  11. data/doc/Ballast/Concerns/Common.html +81 -361
  12. data/doc/Ballast/Concerns/ErrorsHandling.html +18 -36
  13. data/doc/Ballast/Concerns/View.html +181 -157
  14. data/doc/Ballast/Concerns.html +6 -6
  15. data/doc/Ballast/Configuration.html +204 -31
  16. data/doc/Ballast/Emoji/Character.html +411 -0
  17. data/doc/Ballast/Emoji/Utils.html +794 -0
  18. data/doc/Ballast/Emoji.html +125 -0
  19. data/doc/Ballast/Errors/{BaseError.html → Base.html} +61 -34
  20. data/doc/Ballast/Errors/{PerformError.html → Failure.html} +21 -17
  21. data/doc/Ballast/Errors/InvalidDomain.html +11 -11
  22. data/doc/Ballast/Errors/{ValidationError.html → ValidationFailure.html} +24 -16
  23. data/doc/Ballast/Errors.html +5 -5
  24. data/doc/Ballast/Middlewares/DefaultHost.html +6 -6
  25. data/doc/Ballast/Middlewares.html +4 -4
  26. data/doc/Ballast/RequestDomainMatcher.html +28 -28
  27. data/doc/Ballast/Service/Response.html +1243 -0
  28. data/doc/Ballast/Service.html +1314 -0
  29. data/doc/Ballast/Version.html +7 -7
  30. data/doc/Ballast.html +15 -15
  31. data/doc/_index.html +59 -30
  32. data/doc/class_list.html +6 -2
  33. data/doc/css/style.css +1 -0
  34. data/doc/file.README.html +6 -6
  35. data/doc/file_list.html +5 -1
  36. data/doc/frames.html +1 -1
  37. data/doc/index.html +6 -6
  38. data/doc/js/full_list.js +4 -1
  39. data/doc/method_list.html +167 -79
  40. data/doc/top-level-namespace.html +41 -4
  41. data/lib/ballast/ajax_response.rb +76 -0
  42. data/lib/ballast/concerns/ajax_handling.rb +73 -0
  43. data/lib/ballast/concerns/common.rb +25 -47
  44. data/lib/ballast/concerns/errors_handling.rb +21 -30
  45. data/lib/ballast/concerns/view.rb +24 -22
  46. data/lib/ballast/configuration.rb +30 -10
  47. data/lib/ballast/emoji.rb +114 -0
  48. data/lib/ballast/errors.rb +16 -13
  49. data/lib/ballast/middlewares/default_host.rb +3 -3
  50. data/lib/ballast/request_domain_matcher.rb +7 -7
  51. data/lib/ballast/service.rb +147 -0
  52. data/lib/ballast/version.rb +3 -3
  53. data/lib/ballast.rb +22 -22
  54. data/spec/ballast/ajax_response_spec.rb +61 -0
  55. data/spec/ballast/concerns/ajax_handling_spec.rb +86 -0
  56. data/spec/ballast/concerns/common_spec.rb +17 -52
  57. data/spec/ballast/concerns/errors_handling_spec.rb +35 -29
  58. data/spec/ballast/concerns/view_spec.rb +21 -32
  59. data/spec/ballast/configuration_spec.rb +66 -16
  60. data/spec/ballast/emoji_spec.rb +103 -0
  61. data/spec/ballast/errors_spec.rb +5 -5
  62. data/spec/ballast/middlewares/default_host_spec.rb +3 -5
  63. data/spec/ballast/request_domain_matcher_spec.rb +4 -4
  64. data/spec/ballast/service_spec.rb +137 -0
  65. data/spec/spec_helper.rb +1 -13
  66. metadata +42 -80
  67. data/doc/Ballast/Concerns/Ajax.html +0 -945
  68. data/doc/Ballast/Context.html +0 -417
  69. data/doc/Ballast/Operation.html +0 -1304
  70. data/doc/Ballast/OperationsChain.html +0 -597
  71. data/lib/ballast/concerns/ajax.rb +0 -134
  72. data/lib/ballast/context.rb +0 -38
  73. data/lib/ballast/operation.rb +0 -136
  74. data/lib/ballast/operations_chain.rb +0 -45
  75. data/spec/ballast/concerns/ajax_spec.rb +0 -141
  76. data/spec/ballast/context_spec.rb +0 -23
  77. data/spec/ballast/operation_spec.rb +0 -177
  78. data/spec/ballast/operations_chain_spec.rb +0 -61
@@ -0,0 +1,147 @@
1
+ #
2
+ # This file is part of the ballast gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
3
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
4
+ #
5
+
6
+ module Ballast
7
+ # A class which implements a common abstraction for services.
8
+ #
9
+ # @attribute [r] owner
10
+ # @return [Object|NilClass] The owner of this service.
11
+ class Service
12
+ # A response to a service invocation.
13
+ #
14
+ # @attribute [r] success
15
+ # @return [Boolean] Whether the invocation was successful or not.
16
+ # @attribute [r] data
17
+ # @return [Object] The data returned by the operation.
18
+ # @attribute [r] errors
19
+ # @return [Array] The errors returned by the operation.
20
+ class Response
21
+ attr_reader :success, :data, :errors
22
+
23
+ # Creates a new service response.
24
+ #
25
+ # @param success [Boolean] Whether the invocation was successful or not.
26
+ # @param data [Object|NilClass] The data returned by the operation.
27
+ # @param errors [Array|NilClass] The errors returned by the operation.
28
+ # @param error [Object|NilClass] Alias for errors. *Ignored if `errors` is present.*
29
+ def initialize(success = true, data: nil, errors: nil, error: nil)
30
+ errors ||= error.ensure_array
31
+
32
+ @success = success.to_boolean
33
+ @data = data
34
+ @errors = errors.ensure_array(no_duplicates: true, compact: true)
35
+ end
36
+
37
+ # Returns whether the invocation was successful or not.
38
+ #
39
+ # @return [Boolean] `true` if the service invocation was successful, `false` otherwise.
40
+ def success?
41
+ # TODO@PI: Ignore rubocop on this
42
+ @success
43
+ end
44
+ alias_method :successful?, :success?
45
+ alias_method :succeeded?, :success?
46
+
47
+ # Returns whether the invocation failed or not.
48
+ #
49
+ # @return [Boolean] `true` if the service invocation failed, `false` otherwise.
50
+ def fail?
51
+ !@success
52
+ end
53
+ alias_method :failed?, :fail?
54
+
55
+ # Returns the first error returned by the operation.
56
+ #
57
+ # @return [Object] The first error returned by the service.
58
+ def error
59
+ @errors.first
60
+ end
61
+
62
+ # Converts this response to a AJAX response.
63
+ #
64
+ # @return [AjaxResponse] The AJAX response, which will include only the first error.
65
+ def as_ajax_response
66
+ status, error_message =
67
+ if successful?
68
+ [:ok, nil]
69
+ elsif error.is_a?(Hash)
70
+ [error[:status], error[:error]]
71
+ else
72
+ [:unknown, error]
73
+ end
74
+
75
+ AjaxResponse.new(status: status, data: data, error: error_message)
76
+ end
77
+ end
78
+
79
+ attr_reader :owner
80
+
81
+ # Invokes one of the operations exposed by the service.
82
+ #
83
+ # @param operation [String] The operation to invoke.
84
+ # @param owner [Object|NilClass] The owner of the service.
85
+ # @param raise_errors [Boolean] Whether to raise errors instead of returning a failure.
86
+ # @param params [Hash] The parameters to pass to the service.
87
+ # @param kwargs [Hash] Other modifiers to pass to the service.
88
+ # @param block [Proc] A lambda to pass to the service.
89
+ # @return [Response] The response of the service.
90
+ def self.call(operation = :perform, owner: nil, raise_errors: false, params: {}, **kwargs, &block)
91
+ fail!(status: 501, error: "Unsupported operation #{self}.#{operation}.") unless respond_to?(operation)
92
+ Response.new(true, data: send(operation, owner: owner, params: params, **kwargs, &block))
93
+ rescue Errors::Failure => failure
94
+ handle_failure(failure, raise_errors)
95
+ end
96
+
97
+ # Marks the failure of the operation.
98
+ #
99
+ # @param details [Object] The error(s) occurred.
100
+ # @param on_validation [Boolean] Whether the error(s) was/were validation error(s).
101
+ def self.fail!(details, on_validation: false)
102
+ raise(on_validation ? Errors::ValidationFailure : Errors::Failure, details)
103
+ end
104
+
105
+ # Creates a service object.
106
+ #
107
+ # @param owner [Object|NilClass] The owner of the service.
108
+ def initialize(owner = nil)
109
+ @owner = owner
110
+ end
111
+
112
+ # Invokes one of the operations exposed by the service.
113
+ #
114
+ # @param operation [String] The operation to invoke.
115
+ # @param owner [Object|NilClass] The owner of the service.
116
+ # @param raise_errors [Boolean] Whether to raise errors instead of returning a failure.
117
+ # @param params [Hash] The parameters to pass to the service.
118
+ # @param kwargs [Hash] Other modifiers to pass to the service.
119
+ # @param block [Proc] A lambda to pass to the service.
120
+ # @return [Response] The response of the service.
121
+ def call(operation = :perform, owner: nil, raise_errors: false, params: {}, **kwargs, &block)
122
+ # PI: Ignore Roodi on this method
123
+ @owner = owner if owner
124
+ fail!(status: 501, error: "Unsupported operation #{self.class}##{operation}.") unless respond_to?(operation)
125
+ Response.new(true, data: send(operation, params: params, **kwargs, &block))
126
+ rescue Errors::Failure => failure
127
+ self.class.send(:handle_failure, failure, raise_errors)
128
+ end
129
+
130
+ # Marks the failure of the operation.
131
+ #
132
+ # @param details [Object] The error(s) occurred.
133
+ # @param on_validation [Boolean] Whether the error(s) was/were validation error(s).
134
+ def fail!(details, on_validation: false)
135
+ self.class.fail!(details, on_validation: on_validation)
136
+ end
137
+
138
+ # Handles a failure.
139
+ #
140
+ # @param failure [Failure] The failure to handle.
141
+ # @param raise_errors [Boolean] If `true` it will simply raise the error, otherwise it will return a failure as as Service::Response.
142
+ # @return [Response] A failure response.
143
+ def self.handle_failure(failure, raise_errors)
144
+ raise_errors ? raise(failure) : Response.new(false, error: failure.details)
145
+ end
146
+ end
147
+ end
@@ -10,13 +10,13 @@ module Ballast
10
10
  # @see http://semver.org
11
11
  module Version
12
12
  # The major version.
13
- MAJOR = 1
13
+ MAJOR = 2
14
14
 
15
15
  # The minor version.
16
- MINOR = 9
16
+ MINOR = 0
17
17
 
18
18
  # The patch version.
19
- PATCH = 3
19
+ PATCH = 0
20
20
 
21
21
  # The current version of ballast.
22
22
  STRING = [MAJOR, MINOR, PATCH].compact.join(".")
data/lib/ballast.rb CHANGED
@@ -3,40 +3,41 @@
3
3
  # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
4
4
  #
5
5
 
6
- require "lazier"
6
+ # PI: Ignore flog on this file.
7
+
7
8
  require "brauser"
8
- require "interactor"
9
9
  require "addressable/uri"
10
- require "rack/utils"
11
- require "rack/fiber_pool"
12
10
  require "em-synchrony"
13
- require "oj"
11
+ require "rack/utils"
12
+ require "emoji"
13
+ require "action_view/helpers/capture_helper"
14
+ require "action_view/helpers/tag_helper"
14
15
 
15
- Lazier.load!
16
+ Lazier.load!(:hash, :datetime)
16
17
  Oj.default_options = Oj.default_options.merge(mode: :compat, indent: 2, symbol_keys: true)
17
18
 
18
- require "ballast/version" if !defined?(Ballast::Version)
19
19
  require "ballast/errors"
20
- require "ballast/context"
21
- require "ballast/operation"
22
- require "ballast/operations_chain"
20
+ require "ballast/emoji"
21
+ require "ballast/ajax_response"
22
+ require "ballast/service"
23
23
  require "ballast/request_domain_matcher"
24
24
  require "ballast/configuration"
25
- require "ballast/concerns/ajax"
25
+ require "ballast/concerns/ajax_handling"
26
26
  require "ballast/concerns/common"
27
27
  require "ballast/concerns/view"
28
28
  require "ballast/concerns/errors_handling"
29
29
  require "ballast/middlewares/default_host"
30
30
 
31
+ # A collection of base utilities for web frameworks.
31
32
  module Ballast
32
- # If running under eventmachine, run the block in a thread of its threadpool using EM::Synchrony, otherwise run the block normally.
33
+ # If running under eventmachine, runs the block in a thread of its threadpool using EM::Synchrony, otherwise runs the block directly.
33
34
  #
34
35
  # @param start_reactor [Boolean] If start a EM::Synchrony reactor if none is running.
35
36
  # @param block [Proc] The block to run.
36
37
  def self.in_em_thread(start_reactor = false, &block)
37
- if EM.reactor_running? then
38
+ if EM.reactor_running?
38
39
  run_in_thread(&block)
39
- elsif start_reactor then
40
+ elsif start_reactor
40
41
  EM.synchrony do
41
42
  Ballast.in_em_thread(&block)
42
43
  EM.stop
@@ -47,12 +48,11 @@ module Ballast
47
48
  end
48
49
 
49
50
  private
50
- # Runs a block inside a EM thread.
51
- #
52
- # @param block [Proc] The block to run.
53
- def self.run_in_thread(&block)
54
- EM::Synchrony.defer do
55
- Fiber.new { block.call }.resume
56
- end
51
+
52
+ # :nodoc:
53
+ def self.run_in_thread(&block)
54
+ EM::Synchrony.defer do
55
+ Fiber.new { block.call }.resume
57
56
  end
58
- end
57
+ end
58
+ end
@@ -0,0 +1,61 @@
1
+ #
2
+ # This file is part of the ballast gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
3
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
4
+ #
5
+
6
+ require "spec_helper"
7
+
8
+ describe Ballast::AjaxResponse do
9
+ describe "#initialize" do
10
+ it "should save arguments" do
11
+ subject = Ballast::AjaxResponse.new(status: "STATUS", data: "DATA", error: "ERROR", transport: "TRANSPORT")
12
+
13
+ expect(subject.status).to eq("STATUS")
14
+ expect(subject.data).to eq("DATA")
15
+ expect(subject.error).to eq("ERROR")
16
+ expect(subject.transport).to eq("TRANSPORT")
17
+ end
18
+ end
19
+
20
+ describe "#numeric_status" do
21
+ it "should return the status as an integer" do
22
+ expect(Ballast::AjaxResponse.new.numeric_status).to eq(200)
23
+ expect(Ballast::AjaxResponse.new(status: :forbidden).numeric_status).to eq(403)
24
+ expect(Ballast::AjaxResponse.new(status: :whatever).numeric_status).to eq(500)
25
+ end
26
+ end
27
+
28
+ describe "#as_json" do
29
+ it "should serialize correctly" do
30
+ subject = Ballast::AjaxResponse.new(data: "DATA", error: "ERROR", transport: "TRANSPORT")
31
+ expect(subject.as_json).to eq({status: 200, data: "DATA", error: "ERROR"})
32
+ expect(subject.as_json(original_status: true)).to eq({status: :ok, data: "DATA", error: "ERROR"})
33
+ end
34
+ end
35
+
36
+ describe "#reply" do
37
+ before(:example) do
38
+ @transport = OpenStruct.new(request: OpenStruct.new(format: :json), params: {}, performed?: false)
39
+ end
40
+
41
+ subject { Ballast::AjaxResponse.new(status: 200, data: "DATA", error: "ERROR", transport: @transport) }
42
+
43
+ it "should setup the right content type for text" do
44
+ expect(@transport).to receive(:render).with(text: "{\"status\":200,\"data\":\"DATA\",\"error\":\"ERROR\"}", status: 200, callback: nil, content_type: "text/plain")
45
+ subject.reply(format: :text)
46
+ end
47
+
48
+ it "should set the right callback for JSONP" do
49
+ @transport.params[:callback] = "callback"
50
+ expect(@transport).to receive(:render).with(jsonp: "{\"status\":200,\"data\":\"DATA\",\"error\":\"ERROR\"}", status: 200, callback: "callback", content_type: nil)
51
+ subject.reply(format: :jsonp)
52
+ end
53
+
54
+ it "should fallback to transport request format" do
55
+ subject.status = 403
56
+
57
+ expect(@transport).to receive(:render).with(json: "{\"status\":403,\"data\":\"DATA\",\"error\":\"ERROR\"}", status: 403, callback: nil, content_type: nil)
58
+ subject.reply(format: nil)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,86 @@
1
+ #
2
+ # This file is part of the ballast gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
3
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
4
+ #
5
+
6
+ require "spec_helper"
7
+
8
+ describe Ballast::Concerns::AjaxHandling do
9
+ class AjaxMockClass < OpenStruct
10
+ include Ballast::Concerns::AjaxHandling
11
+ end
12
+
13
+ subject{ AjaxMockClass.new(response: OpenStruct.new(headers: {}), headers: {}, params: {}, performed?: false) }
14
+
15
+ describe "#ajax_request?" do
16
+ it "should return false by default" do
17
+ expect(AjaxMockClass.new(request: {}, params: {}).ajax_request?).to be_falsey
18
+ end
19
+
20
+ it "should return true when the request is XHR" do
21
+ expect(AjaxMockClass.new(request: OpenStruct.new(xhr?: true)).ajax_request?).to be_truthy
22
+ end
23
+
24
+ it "should return true when the parameter is overriden" do
25
+ expect(AjaxMockClass.new(params: {xhr: true}).ajax_request?).to be_truthy
26
+ end
27
+ end
28
+
29
+ describe "#prepare_ajax_response" do
30
+ it "should return a AJAX response" do
31
+ expect(subject.prepare_ajax_response).to be_a(Ballast::AjaxResponse)
32
+
33
+ expect(Ballast::AjaxResponse).to receive(:new).with({status: "STATUS", data: "DATA", error: "ERROR", transport: subject})
34
+ subject.prepare_ajax_response(status: "STATUS", data: "DATA", error: "ERROR")
35
+ end
36
+ end
37
+
38
+ describe "#prevent_caching" do
39
+ it "should append correct headers" do
40
+ subject.prevent_caching
41
+
42
+ expect(subject.response.headers).to eq({
43
+ "Cache-Control" => "no-cache, no-store, max-age=0, must-revalidate",
44
+ "Pragma" => "no-cache",
45
+ "Expires" => "Fri, 01 Jan 1990 00:00:00 GMT"
46
+ })
47
+ end
48
+ end
49
+
50
+ describe "#allow_cors" do
51
+ it "should append correct headers" do
52
+ subject.allow_cors
53
+
54
+ expect(subject.headers).to eq({
55
+ "Access-Control-Allow-Origin" => "*",
56
+ "Access-Control-Allow-Methods" => "POST, GET, OPTIONS",
57
+ "Access-Control-Allow-Headers" => "*",
58
+ "Access-Control-Max-Age" => "31557600"
59
+ })
60
+ end
61
+
62
+ it "should append custom headers values" do
63
+ subject.allow_cors(allow_origin: "ORIGIN", allow_methods: [:first, :second], allow_headers: "_", max_age: 1.day, allow_credentials: true)
64
+
65
+ expect(subject.headers).to eq({
66
+ "Access-Control-Allow-Origin" => "ORIGIN",
67
+ "Access-Control-Allow-Methods" => "FIRST, SECOND",
68
+ "Access-Control-Allow-Headers" => "_",
69
+ "Access-Control-Max-Age" => "86400",
70
+ "Access-Control-Allow-Credentials" => "true"
71
+ })
72
+ end
73
+ end
74
+
75
+ describe "#generate_robots_txt" do
76
+ it "should generate a robots.txt file which prevents everything by default" do
77
+ expect(subject).to receive(:render).with(text: "User-agent: *\nDisallow: /", content_type: "text/plain")
78
+ subject.disallow_robots
79
+ end
80
+
81
+ it "should generate a robots.txt file which prevents everything by default" do
82
+ expect(subject).to receive(:render).with(text: "User-agent: A\nDisallow: B\nDisallow: C\nDisallow: D\n\nUser-agent: E\nDisallow: F\nDisallow: \nDisallow: G", content_type: "text/plain")
83
+ subject.generate_robots_txt({"A"=> ["B", "C", "D"], "E" => ["F", "", "G"]})
84
+ end
85
+ end
86
+ end
@@ -10,86 +10,51 @@ describe Ballast::Concerns::Common do
10
10
  include Ballast::Concerns::Common
11
11
  end
12
12
 
13
- class OperationMockClass
14
-
15
- end
16
-
17
- module Actions
18
- module CommonMockClass
19
- class Sub
20
- end
21
- end
22
- end
23
-
24
13
  subject{ CommonMockClass.new(request: OpenStruct.new(headers: {}), headers: {}, params: {}, performed?: false) }
25
14
 
26
- describe "#is_json?" do
15
+ describe "#json?" do
27
16
  it "should return false by default" do
28
- expect(CommonMockClass.new(request: OpenStruct.new({format: ""}), params: {}).is_json?).to be(false)
17
+ expect(CommonMockClass.new(request: OpenStruct.new({format: ""}), params: {}).json?).to be_falsey
29
18
  end
30
19
 
31
20
  it "should return true when the request is JSON" do
32
- expect(CommonMockClass.new(request: OpenStruct.new(format: "json")).is_json?).to be(true)
21
+ expect(CommonMockClass.new(request: OpenStruct.new(format: "json")).json?).to be_truthy
33
22
  end
34
23
 
35
24
  it "should return true when the parameter is overriden" do
36
- expect(CommonMockClass.new(request: OpenStruct.new({format: ""}), params: {json: true}).is_json?).to be(true)
25
+ expect(CommonMockClass.new(request: OpenStruct.new({format: ""}), params: {json: true}).json?).to be_truthy
37
26
  end
38
27
  end
39
28
 
40
- describe "#sending_data?" do
29
+ describe "#request_data?" do
41
30
  it "should return the current status" do
42
- expect(CommonMockClass.new(request: OpenStruct.new(post?: false, put?: false)).sending_data?).to be(false)
43
- expect(CommonMockClass.new(request: OpenStruct.new(post?: true, put?: false)).sending_data?).to be(true)
44
- expect(CommonMockClass.new(request: OpenStruct.new(post?: false, put?: true)).sending_data?).to be(true)
45
- end
46
- end
47
-
48
- describe "#perform_operation" do
49
- it "should perform the requested operation and memoize it" do
50
- expect(OperationMockClass).to receive(:perform).with("OWNER", a: 1, b: 2).and_return("OPERATION 1")
51
- expect(OperationMockClass).to receive(:perform).with(subject, c: 3, d: 4).and_return("OPERATION 2")
52
-
53
- subject.perform_operation(OperationMockClass, "OWNER", a: 1, b: 2)
54
- expect(subject.instance_variable_get(:@operation)).to eq("OPERATION 1")
55
- subject.perform_operation(OperationMockClass, c: 3, d: 4)
56
- expect(subject.instance_variable_get(:@operation)).to eq("OPERATION 2")
57
- end
58
- end
59
-
60
- describe "#perform_operations_chain" do
61
- it "should perform the requested operation chain and memoize it" do
62
- expect(Ballast::OperationsChain).to receive(:perform).with("OWNER", [:a, :b], a: 1, b: 2).and_return("OPERATION 1")
63
- expect(Ballast::OperationsChain).to receive(:perform).with(subject, [:c, :d], c: 3, d: 4).and_return("OPERATION 2")
64
-
65
- subject.perform_operations_chain([:a, :b], "OWNER", a: 1, b: 2)
66
- expect(subject.instance_variable_get(:@operation)).to eq("OPERATION 1")
67
- subject.perform_operations_chain([:c, :d], c: 3, d: 4)
68
- expect(subject.instance_variable_get(:@operation)).to eq("OPERATION 2")
31
+ expect(CommonMockClass.new(request: OpenStruct.new(post?: false, put?: false)).request_data?).to be_falsey
32
+ expect(CommonMockClass.new(request: OpenStruct.new(post?: true, put?: false)).request_data?).to be_truthy
33
+ expect(CommonMockClass.new(request: OpenStruct.new(post?: false, put?: true)).request_data?).to be_truthy
69
34
  end
70
35
  end
71
36
 
72
37
  describe "#format_short_duration" do
73
38
  it "should format a date" do
74
39
  now = DateTime.civil(2013, 12, 9, 15, 6, 00)
75
- expect(subject.format_short_duration(DateTime.civil(2013, 12, 9, 16, 6, 0), now, "ago")).to eq("now")
76
- expect(subject.format_short_duration(DateTime.civil(2013, 12, 9, 15, 5, 58), now, "")).to eq("2s")
77
- expect(subject.format_short_duration(DateTime.civil(2013, 12, 9, 15, 3, 0), now, " in the past")).to eq("3m in the past")
78
- expect(subject.format_short_duration(DateTime.civil(2013, 12, 9, 8, 6, 0), now, "")).to eq("7h")
79
- expect(subject.format_short_duration(DateTime.civil(2013, 5, 3, 15, 6, 0), now, "")).to eq("May 03")
80
- expect(subject.format_short_duration(DateTime.civil(2011, 6, 4, 15, 6, 0), now, "")).to eq("Jun 04 2011")
40
+ expect(subject.format_short_duration(DateTime.civil(2013, 12, 9, 16, 6, 0), reference: now, suffix: "ago")).to eq("now")
41
+ expect(subject.format_short_duration(DateTime.civil(2013, 12, 9, 15, 5, 58), reference: now, suffix: "")).to eq("2s")
42
+ expect(subject.format_short_duration(DateTime.civil(2013, 12, 9, 15, 3, 0), reference: now, suffix: " in the past")).to eq("3m in the past")
43
+ expect(subject.format_short_duration(DateTime.civil(2013, 12, 9, 8, 6, 0), reference: now, suffix: "")).to eq("7h")
44
+ expect(subject.format_short_duration(DateTime.civil(2013, 5, 3, 15, 6, 0), reference: now, suffix: "")).to eq("May 03")
45
+ expect(subject.format_short_duration(DateTime.civil(2011, 6, 4, 15, 6, 0), reference: now, suffix: "")).to eq("Jun 04 2011")
81
46
  end
82
47
  end
83
48
 
84
49
  describe "#format_long_date" do
85
- before(:each) do
50
+ before(:example) do
86
51
  expect_any_instance_of(DateTime).to receive(:dst?).and_return(true)
87
52
  expect(Time).to receive(:zone).at_least(1).and_return(ActiveSupport::TimeZone["UTC"], ActiveSupport::TimeZone["Pacific Time (US & Canada)"])
88
53
  end
89
54
 
90
55
  it "should format a date" do
91
56
  expect(subject.format_long_date(DateTime.civil(2013, 7, 11, 10, 9, 8))).to eq("10:09AM • Jul 11th, 2013 (UTC)")
92
- expect(subject.format_long_date(DateTime.civil(2013, 7, 11, 10, 9, 8), "SEP", "%F %T %o %- %:Z")).to eq("2013-07-11 10:09:08 11th SEP Pacific Time (US & Canada) (DST)")
57
+ expect(subject.format_long_date(DateTime.civil(2013, 7, 11, 10, 9, 8), separator: "SEP", format: "%F %T %o %- %:Z")).to eq("2013-07-11 10:09:08 11th SEP Pacific Time (US & Canada) (DST)")
93
58
  end
94
59
  end
95
60
 
@@ -114,7 +79,7 @@ describe Ballast::Concerns::Common do
114
79
  expect(subject).to receive(:authenticate_with_http_basic).and_return(false)
115
80
  expect(subject).to receive(:handle_error).with({status: 401, title: "TITLE", message: "MESSAGE"})
116
81
 
117
- subject.authenticate_user("AREA", "TITLE", "MESSAGE")
82
+ subject.authenticate_user(area: "AREA", title: "TITLE", message: "MESSAGE")
118
83
  expect(subject.headers["WWW-Authenticate"]).to eq("Basic realm=\"AREA\"")
119
84
  end
120
85
  end
@@ -6,65 +6,71 @@
6
6
  require "spec_helper"
7
7
 
8
8
  describe Ballast::Concerns::ErrorsHandling do
9
- class ErrorsHandlingMockClass < OpenStruct
10
- include Ballast::Concerns::Ajax
9
+ class ErrorsHandlingMockClass
10
+ include Ballast::Concerns::AjaxHandling
11
11
  include Ballast::Concerns::ErrorsHandling
12
12
 
13
- def initialize(attrs)
14
- @operation = OpenStruct.new(attrs.delete(:operation))
15
- super(attrs)
13
+ def request
14
+ OpenStruct.new(format: "json")
15
+ end
16
+
17
+ def performed?
18
+
19
+ end
20
+
21
+ def render(*args)
22
+
16
23
  end
17
24
  end
18
25
 
19
- subject{ ErrorsHandlingMockClass.new(response: OpenStruct.new(headers: {}), headers: {}, params: {}, performed?: false) }
26
+ subject{ ErrorsHandlingMockClass.new }
20
27
 
21
28
  describe "#handle_error" do
22
29
  it "should handle a custom error" do
23
- error = {status: "STATUS", error: "ERROR"}
30
+ error = {status: "STATUS", error: "ERROR", title: "TITLE"}
24
31
  expect(subject).to receive(:send_or_render_error).with("LAYOUT", nil)
25
- subject.handle_error(error, "LAYOUT", "TITLE")
26
-
32
+ subject.handle_error(error, layout: "LAYOUT", title: "OTHER TITLE")
27
33
  expect(subject.instance_variable_get(:@error)).to eq(error)
28
- expect(subject.instance_variable_get(:@error_code)).to eq("STATUS")
29
- expect(subject.instance_variable_get(:@error_title)).to eq("TITLE")
30
- expect(subject.instance_variable_get(:@error_message)).to eq("ERROR")
34
+
35
+ error = {status: "STATUS", error: "ERROR"}
36
+ expect(subject).to receive(:send_or_render_error).with("LAYOUT", nil)
37
+ subject.handle_error(error, layout: "LAYOUT", title: "OTHER TITLE")
38
+ expect(subject.instance_variable_get(:@error)).to eq(error.merge({title: "OTHER TITLE"}))
31
39
  end
32
40
 
33
41
  it "should handle a debug error" do
42
+ error = Lazier::Exceptions::Debug.new("MESSAGE")
34
43
  expect(subject).to receive(:send_or_render_error)
35
- subject.handle_error(Lazier::Exceptions::Debug.new("MESSAGE"))
36
44
 
37
- expect(subject.instance_variable_get(:@error_code)).to eq(503)
38
- expect(subject.instance_variable_get(:@error_title)).to eq("Debug")
39
- expect(subject.instance_variable_get(:@error_message)).to be_nil
45
+ subject.handle_error(error)
46
+ expect(subject.instance_variable_get(:@error)).to eq({status: 503, title: "Debug", error: "MESSAGE", exception: error})
40
47
  end
41
48
 
42
49
  it "should handle every other error" do
50
+ error = RuntimeError.new("ERROR")
43
51
  expect(subject).to receive(:send_or_render_error)
44
- subject.handle_error(RuntimeError.new("ERROR"))
45
52
 
46
- expect(subject.instance_variable_get(:@error_code)).to eq(500)
47
- expect(subject.instance_variable_get(:@error_title)).to eq("Error - RuntimeError")
48
- expect(subject.instance_variable_get(:@error_message)).to be_nil
53
+ subject.handle_error(error)
54
+ expect(subject.instance_variable_get(:@error)).to eq({status: 500, title: "Error - RuntimeError", error: "ERROR", exception: error})
49
55
  end
50
56
 
51
57
  it "should render an AJAX error" do
52
58
  expect_any_instance_of(RuntimeError).to receive(:backtrace).and_return(["A", "B"])
53
- allow(subject).to receive(:request).and_return(OpenStruct.new(format: :json))
54
- expect(subject).to receive(:is_ajax?).exactly(2).and_return(true, false)
55
-
56
- expect(subject).to receive(:send_ajax).with({status: 500, error: "ERROR", data: {type: "Error - RuntimeError", backtrace: ["A", "B"]}}.with_indifferent_access, {format: :json})
57
- expect(subject).to receive(:send_ajax).with({status: :forbidden, error: "ERROR", data: {type: "TITLE"}}.with_indifferent_access, {format: :json})
59
+ expect(subject).to receive(:ajax_request?).exactly(2).and_return(true, false)
60
+
61
+ expect(Ballast::AjaxResponse).to receive(:new).with({status: 500, error: "ERROR", data: {description: "Error - RuntimeError", backtrace: ["A", "B"]}, transport: subject}).and_call_original
62
+ expect(Ballast::AjaxResponse).to receive(:new).with({status: :forbidden, error: "ERROR", data: {description: "TITLE", backtrace: nil}, transport: subject}).and_call_original
58
63
 
59
- subject.handle_error(RuntimeError.new("ERROR"), "LAYOUT")
64
+ subject.handle_error(RuntimeError.new("ERROR"))
60
65
  subject.instance_variable_set(:@error, nil)
61
- subject.handle_error({title: "TITLE", status: :forbidden, error: "ERROR"}, :json)
66
+ subject.handle_error({title: "TITLE", status: :forbidden, error: "ERROR"}, format: :json)
62
67
  end
63
68
 
64
69
  it "should render a HTML error" do
65
- allow(subject).to receive(:request).and_return(OpenStruct.new(format: :html))
70
+ expect(subject).to receive(:ajax_request?).and_return(false)
71
+ expect(subject).to receive(:request).and_return(OpenStruct.new(format: "HTML"))
66
72
  expect(subject).to receive(:render).with(html: "", status: 500, layout: "LAYOUT", formats: [:html])
67
- subject.handle_error(RuntimeError.new("ERROR"), "LAYOUT")
73
+ subject.handle_error(RuntimeError.new("ERROR"), layout: "LAYOUT")
68
74
  end
69
75
  end
70
76
  end