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.
- 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
|