ballast 1.9.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +35 -0
- data/.travis-gemfile +4 -5
- data/.travis.yml +3 -2
- data/CHANGELOG.md +62 -6
- data/Gemfile +7 -8
- data/README.md +2 -2
- data/ballast.gemspec +9 -12
- data/doc/Ballast/AjaxResponse.html +1380 -0
- data/doc/Ballast/Concerns/AjaxHandling.html +662 -0
- data/doc/Ballast/Concerns/Common.html +81 -361
- data/doc/Ballast/Concerns/ErrorsHandling.html +18 -36
- data/doc/Ballast/Concerns/View.html +181 -157
- data/doc/Ballast/Concerns.html +6 -6
- data/doc/Ballast/Configuration.html +204 -31
- data/doc/Ballast/Emoji/Character.html +411 -0
- data/doc/Ballast/Emoji/Utils.html +794 -0
- data/doc/Ballast/Emoji.html +125 -0
- data/doc/Ballast/Errors/{BaseError.html → Base.html} +61 -34
- data/doc/Ballast/Errors/{PerformError.html → Failure.html} +21 -17
- data/doc/Ballast/Errors/InvalidDomain.html +11 -11
- data/doc/Ballast/Errors/{ValidationError.html → ValidationFailure.html} +24 -16
- data/doc/Ballast/Errors.html +5 -5
- data/doc/Ballast/Middlewares/DefaultHost.html +6 -6
- data/doc/Ballast/Middlewares.html +4 -4
- data/doc/Ballast/RequestDomainMatcher.html +28 -28
- data/doc/Ballast/Service/Response.html +1243 -0
- data/doc/Ballast/Service.html +1314 -0
- data/doc/Ballast/Version.html +7 -7
- data/doc/Ballast.html +15 -15
- data/doc/_index.html +59 -30
- data/doc/class_list.html +6 -2
- data/doc/css/style.css +1 -0
- data/doc/file.README.html +6 -6
- data/doc/file_list.html +5 -1
- data/doc/frames.html +1 -1
- data/doc/index.html +6 -6
- data/doc/js/full_list.js +4 -1
- data/doc/method_list.html +167 -79
- data/doc/top-level-namespace.html +41 -4
- data/lib/ballast/ajax_response.rb +76 -0
- data/lib/ballast/concerns/ajax_handling.rb +73 -0
- data/lib/ballast/concerns/common.rb +25 -47
- data/lib/ballast/concerns/errors_handling.rb +21 -30
- data/lib/ballast/concerns/view.rb +24 -22
- data/lib/ballast/configuration.rb +30 -10
- data/lib/ballast/emoji.rb +114 -0
- data/lib/ballast/errors.rb +16 -13
- data/lib/ballast/middlewares/default_host.rb +3 -3
- data/lib/ballast/request_domain_matcher.rb +7 -7
- data/lib/ballast/service.rb +147 -0
- data/lib/ballast/version.rb +3 -3
- data/lib/ballast.rb +22 -22
- data/spec/ballast/ajax_response_spec.rb +61 -0
- data/spec/ballast/concerns/ajax_handling_spec.rb +86 -0
- data/spec/ballast/concerns/common_spec.rb +17 -52
- data/spec/ballast/concerns/errors_handling_spec.rb +35 -29
- data/spec/ballast/concerns/view_spec.rb +21 -32
- data/spec/ballast/configuration_spec.rb +66 -16
- data/spec/ballast/emoji_spec.rb +103 -0
- data/spec/ballast/errors_spec.rb +5 -5
- data/spec/ballast/middlewares/default_host_spec.rb +3 -5
- data/spec/ballast/request_domain_matcher_spec.rb +4 -4
- data/spec/ballast/service_spec.rb +137 -0
- data/spec/spec_helper.rb +1 -13
- metadata +42 -80
- data/doc/Ballast/Concerns/Ajax.html +0 -945
- data/doc/Ballast/Context.html +0 -417
- data/doc/Ballast/Operation.html +0 -1304
- data/doc/Ballast/OperationsChain.html +0 -597
- data/lib/ballast/concerns/ajax.rb +0 -134
- data/lib/ballast/context.rb +0 -38
- data/lib/ballast/operation.rb +0 -136
- data/lib/ballast/operations_chain.rb +0 -45
- data/spec/ballast/concerns/ajax_spec.rb +0 -141
- data/spec/ballast/context_spec.rb +0 -23
- data/spec/ballast/operation_spec.rb +0 -177
- 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
|
data/lib/ballast/version.rb
CHANGED
@@ -10,13 +10,13 @@ module Ballast
|
|
10
10
|
# @see http://semver.org
|
11
11
|
module Version
|
12
12
|
# The major version.
|
13
|
-
MAJOR =
|
13
|
+
MAJOR = 2
|
14
14
|
|
15
15
|
# The minor version.
|
16
|
-
MINOR =
|
16
|
+
MINOR = 0
|
17
17
|
|
18
18
|
# The patch version.
|
19
|
-
PATCH =
|
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
|
-
|
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 "
|
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/
|
21
|
-
require "ballast/
|
22
|
-
require "ballast/
|
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/
|
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,
|
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?
|
38
|
+
if EM.reactor_running?
|
38
39
|
run_in_thread(&block)
|
39
|
-
elsif start_reactor
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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 "#
|
15
|
+
describe "#json?" do
|
27
16
|
it "should return false by default" do
|
28
|
-
expect(CommonMockClass.new(request: OpenStruct.new({format: ""}), params: {}).
|
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")).
|
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}).
|
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 "#
|
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)).
|
43
|
-
expect(CommonMockClass.new(request: OpenStruct.new(post?: true, put?: false)).
|
44
|
-
expect(CommonMockClass.new(request: OpenStruct.new(post?: false, put?: 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(:
|
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
|
10
|
-
include Ballast::Concerns::
|
9
|
+
class ErrorsHandlingMockClass
|
10
|
+
include Ballast::Concerns::AjaxHandling
|
11
11
|
include Ballast::Concerns::ErrorsHandling
|
12
12
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
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
|
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
|
-
|
29
|
-
|
30
|
-
expect(subject
|
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
|
-
|
38
|
-
expect(subject.instance_variable_get(:@
|
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
|
-
|
47
|
-
expect(subject.instance_variable_get(:@
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
expect(
|
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")
|
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
|
-
|
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
|