ballast 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis-gemfile +15 -0
- data/.travis.yml +7 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +21 -0
- data/README.md +40 -0
- data/Rakefile +28 -0
- data/ballast.gemspec +35 -0
- data/doc/Ballast/Concerns/Ajax.html +806 -0
- data/doc/Ballast/Concerns/Common.html +900 -0
- data/doc/Ballast/Concerns/ErrorsHandling.html +283 -0
- data/doc/Ballast/Concerns/View.html +664 -0
- data/doc/Ballast/Concerns.html +127 -0
- data/doc/Ballast/Context.html +417 -0
- data/doc/Ballast/Errors/BaseError.html +326 -0
- data/doc/Ballast/Errors/InvalidDomain.html +157 -0
- data/doc/Ballast/Errors/PerformError.html +157 -0
- data/doc/Ballast/Errors/ValidationError.html +157 -0
- data/doc/Ballast/Errors.html +125 -0
- data/doc/Ballast/Operation.html +1304 -0
- data/doc/Ballast/OperationsChain.html +585 -0
- data/doc/Ballast/Version.html +189 -0
- data/doc/Ballast.html +130 -0
- data/doc/_index.html +267 -0
- data/doc/class_list.html +54 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +338 -0
- data/doc/file.README.html +115 -0
- data/doc/file_list.html +56 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +115 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +178 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +269 -0
- data/doc/top-level-namespace.html +112 -0
- data/lib/ballast/concerns/ajax.rb +116 -0
- data/lib/ballast/concerns/common.rb +97 -0
- data/lib/ballast/concerns/errors_handling.rb +49 -0
- data/lib/ballast/concerns/view.rb +63 -0
- data/lib/ballast/context.rb +38 -0
- data/lib/ballast/errors.rb +34 -0
- data/lib/ballast/operation.rb +136 -0
- data/lib/ballast/operations_chain.rb +38 -0
- data/lib/ballast/version.rb +24 -0
- data/lib/ballast.rb +24 -0
- data/spec/ballast/concerns/ajax_spec.rb +124 -0
- data/spec/ballast/concerns/common_spec.rb +100 -0
- data/spec/ballast/concerns/errors_handling_spec.rb +63 -0
- data/spec/ballast/concerns/view_spec.rb +107 -0
- data/spec/ballast/context_spec.rb +23 -0
- data/spec/ballast/errors_spec.rb +16 -0
- data/spec/ballast/operation_spec.rb +175 -0
- data/spec/ballast/operations_chain_spec.rb +33 -0
- data/spec/coverage_helper.rb +19 -0
- data/spec/spec_helper.rb +19 -0
- metadata +225 -0
@@ -0,0 +1,116 @@
|
|
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 set of concerns to address common issues.
|
8
|
+
module Concerns
|
9
|
+
# A concern to handle AJAX and HTTP requests.
|
10
|
+
module Ajax
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
# Checks if the current request is AJAX.
|
14
|
+
#
|
15
|
+
# @return [Boolean] `true` if the request is AJAX, `false` otherwise.
|
16
|
+
def is_ajax?
|
17
|
+
(request.respond_to?(:xhr?) && request.xhr?) || params[:xhr].to_boolean
|
18
|
+
end
|
19
|
+
|
20
|
+
# Prepares an AJAX response.
|
21
|
+
#
|
22
|
+
# @param status [Symbol|Fixnum] The HTTP status of the response.
|
23
|
+
# @param data [Object] Additional data to append to the response.
|
24
|
+
# @param error [Object] A error to append to the response.
|
25
|
+
def prepare_ajax(status = :ok, data = nil, error = nil)
|
26
|
+
rv = {status: status}.ensure_access(:indifferent)
|
27
|
+
rv[:error] = error if error.present?
|
28
|
+
rv[:data] = data if data.present?
|
29
|
+
rv
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sends an AJAX response to the client.
|
33
|
+
#
|
34
|
+
# @param data [Hash] The response to send.
|
35
|
+
# @param status [Symbol|Fixnum] The HTTP status of the response, *ignored if already set in data*.
|
36
|
+
# @param format [Symbol] The content type of the response.
|
37
|
+
def send_ajax(data, status: :ok, format: :json)
|
38
|
+
if !performed? then
|
39
|
+
# Prepare data
|
40
|
+
data = prepare_ajax_send(data, status)
|
41
|
+
|
42
|
+
# Setup callback and format
|
43
|
+
format, callback, content_type = format_ajax_send(format)
|
44
|
+
status = data[:status]
|
45
|
+
|
46
|
+
# Prepare data for formatting
|
47
|
+
data = ActiveSupport::JSON.encode(data) if [:json, :jsonp, :pretty_json, :pretty_jsonp, :text].include?(format)
|
48
|
+
|
49
|
+
# Render
|
50
|
+
render(format => data, status: status, callback: callback, content_type: content_type)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Updates an AJAX response from a operation, taking either the response data or the first error.
|
55
|
+
#
|
56
|
+
# @param data [Hash] The current data.
|
57
|
+
# @param operation [Operation] The operation to gather data from.
|
58
|
+
# @return [Hash] The updated data.
|
59
|
+
def update_ajax(data, operation = nil)
|
60
|
+
operation ||= @operation
|
61
|
+
data.merge!(operation.success? ? {data: operation.response[:data]} : {error: operation.errors.first})
|
62
|
+
data
|
63
|
+
end
|
64
|
+
|
65
|
+
# Prevents HTTP caching.
|
66
|
+
def prevent_caching
|
67
|
+
response.headers.merge!({
|
68
|
+
"Cache-Control" => "no-cache, no-store, max-age=0, must-revalidate",
|
69
|
+
"Pragma" => "no-cache",
|
70
|
+
"Expires" => "Fri, 01 Jan 1990 00:00:00 GMT"
|
71
|
+
})
|
72
|
+
end
|
73
|
+
|
74
|
+
# Allows HTTP Cross-Origin Resource Sharing.
|
75
|
+
def allow_cors
|
76
|
+
headers.merge!({
|
77
|
+
"Access-Control-Allow-Origin" => "*",
|
78
|
+
"Access-Control-Allow-Methods" => "POST, GET, OPTIONS",
|
79
|
+
"Access-Control-Allow-Headers" => "*",
|
80
|
+
"Access-Control-Max-Age" => 1.year.to_i.to_s
|
81
|
+
})
|
82
|
+
end
|
83
|
+
|
84
|
+
# Disallows web robots.
|
85
|
+
def disallow_robots
|
86
|
+
render(text: "User-agent: *\nDisallow: /", content_type: "text/plain")
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
# Prepares data for sending back to the client.
|
91
|
+
#
|
92
|
+
# @param data [Object] The data to send back. Can be a full response or partial data.
|
93
|
+
# @param status [Symbol|Fixnum] The HTTP status to set if a new response must be created.
|
94
|
+
# @return [Hash] An HTTP response.
|
95
|
+
def prepare_ajax_send(data, status)
|
96
|
+
data = prepare_ajax(status, data) if !data.is_a?(Hash)
|
97
|
+
data[:status] ||= status
|
98
|
+
data[:status] = Rack::Utils.status_code(data[:status].to_s.to_sym) if !data[:status].is_a?(Fixnum)
|
99
|
+
data
|
100
|
+
end
|
101
|
+
|
102
|
+
# Sets up parameters to send a response.
|
103
|
+
#
|
104
|
+
# @param format [Symbol] The format of the data.
|
105
|
+
# @return [Array] An array of format, callback and content_type.
|
106
|
+
def format_ajax_send(format)
|
107
|
+
format ||= params[:format] || request.format || "json"
|
108
|
+
format = format.to_sym
|
109
|
+
callback = format == :jsonp ? (params[:callback] || "jsonp#{Time.now.to_i}") : nil
|
110
|
+
content_type = (format == :text) ? "text/plain" : nil
|
111
|
+
|
112
|
+
[format, callback, content_type]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,97 @@
|
|
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
|
+
module Concerns
|
8
|
+
# A concern to handle common tasks in an application.
|
9
|
+
module Common
|
10
|
+
# Checks if the user is sending any data.
|
11
|
+
#
|
12
|
+
# @return [Boolean] `true` if the user is sending data, `false` otherwise.
|
13
|
+
def sending_data?
|
14
|
+
request.post? || request.put?
|
15
|
+
end
|
16
|
+
|
17
|
+
# Performs an operation, using itself as owner by default.
|
18
|
+
#
|
19
|
+
# @param klass [Class] The operation to perform.
|
20
|
+
# @param owner [Object] The owner to use. By default it uses itself.
|
21
|
+
# @param kwargs [Hash] The arguments for performing.
|
22
|
+
# @return [Operation] The performed operation
|
23
|
+
def perform_operation(klass, owner = nil, **kwargs)
|
24
|
+
@operation = klass.perform(owner || self, **kwargs)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Formats a relative date using abbreviation or short formats.
|
28
|
+
#
|
29
|
+
# @param date [DateTime] The date to format.
|
30
|
+
# @param reference [DateTime] The reference date.
|
31
|
+
# @param suffix [String] The suffix to add to the formatted date.
|
32
|
+
# @return [String] The formatted date.
|
33
|
+
def format_short_duration(date, reference = nil, suffix = "")
|
34
|
+
reference ||= Time.now
|
35
|
+
amount = (reference.to_i - date.to_i).to_i
|
36
|
+
|
37
|
+
if amount <= 0 then
|
38
|
+
"now"
|
39
|
+
elsif amount < 1.day then
|
40
|
+
format_short_amount(amount, suffix)
|
41
|
+
elsif amount < 1.year then
|
42
|
+
date.strftime("%b %d")
|
43
|
+
else
|
44
|
+
date.strftime("%b %d %Y")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Formats a short amount of time (less than one hour).
|
49
|
+
#
|
50
|
+
# @param amount [Fixnum] The amount to format.
|
51
|
+
# @param suffix [String] The suffix to add to the formatted amount.
|
52
|
+
# @return [String] The formatted amount.
|
53
|
+
def format_short_amount(amount, suffix)
|
54
|
+
if amount < 1.minute then
|
55
|
+
"#{amount.floor}s#{suffix}"
|
56
|
+
elsif amount < 1.hour then
|
57
|
+
"#{(amount / 60).floor}m#{suffix}"
|
58
|
+
else
|
59
|
+
"#{(amount / 3600).floor}h#{suffix}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Formats a long date.
|
64
|
+
#
|
65
|
+
# @param date [DateTime] The date to format.
|
66
|
+
# @param separator [String] The separator between date and time.
|
67
|
+
# @param format [String] The format of the date, like in strftime. Use `%-` for the separator, `%o` for the ordinalized version of the day of the month
|
68
|
+
# and `%:Z` for the zone name considering also DST.
|
69
|
+
def format_long_date(date, separator = "•", format = "%I:%M%p %- %b %o, %Y (%:Z)")
|
70
|
+
tz = Time.zone
|
71
|
+
replacements = {"%-" => separator, "%o" => date.day.ordinalize, "%:Z" => tz.send(tz.uses_dst? && date.dst? ? :dst_name : :name)}
|
72
|
+
date.strftime(format).gsub(/%(-|o|(:Z))/) {|r| replacements.fetch(r, r) }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Authenticates a user via HTTP, handling the error if the authentication failed.
|
76
|
+
#
|
77
|
+
# @param area [String] The name of the area.
|
78
|
+
# @param title [String] A title for authentication errors.
|
79
|
+
# @param message [String] A message for authentication errors.
|
80
|
+
# @param authenticator [Proc] A block to verify if authentication is valid.
|
81
|
+
def authenticate_user(area = nil, title = nil, message = nil, &authenticator)
|
82
|
+
area ||= "Private Area"
|
83
|
+
title ||= "Authentication required."
|
84
|
+
message ||= "To view this resource you have to authenticate."
|
85
|
+
authenticated = authenticate_with_http_basic { |username, password| authenticator.call(username, password) }
|
86
|
+
|
87
|
+
if !authenticated then
|
88
|
+
headers["WWW-Authenticate"] = "Basic realm=\"#{area}\""
|
89
|
+
@error_title = title
|
90
|
+
@error_code = 401
|
91
|
+
@error_message = message
|
92
|
+
handle_error
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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
|
+
module Concerns
|
8
|
+
# A concern to handle errors.
|
9
|
+
module ErrorsHandling
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
# Handles an error in the application.
|
13
|
+
#
|
14
|
+
# @param exception [Hash|Exception] The exception to handle.
|
15
|
+
# @param layout [String] The layout to use to render the error.
|
16
|
+
# @param title [String] The title to set in case of custom errors.
|
17
|
+
def handle_error(exception = nil, layout = "error", title = "Error - Application")
|
18
|
+
@error ||= exception
|
19
|
+
|
20
|
+
if @error.is_a?(Lazier::Exceptions::Debug) then
|
21
|
+
@error_title = "Debug"
|
22
|
+
@error_code = 503
|
23
|
+
elsif @error.is_a?(Hash) then
|
24
|
+
@error_title = title
|
25
|
+
@error_code = @error[:status]
|
26
|
+
@error_message = @error[:error]
|
27
|
+
else
|
28
|
+
@error_title = "Error - #{@error.class.to_s}"
|
29
|
+
@error_code = 500
|
30
|
+
end
|
31
|
+
|
32
|
+
send_or_render_error(layout)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
# Send an AJAX error o renders it.
|
37
|
+
#
|
38
|
+
# @param layout [String] The layout to use to render the error.
|
39
|
+
def send_or_render_error(layout)
|
40
|
+
if is_ajax? then
|
41
|
+
data = prepare_ajax(@error_code, {type: @error_title, backtrace: @error.backtrace.join("\n")}, @error_message || @error.message)
|
42
|
+
send_ajax(data)
|
43
|
+
else
|
44
|
+
render(nothing: true, status: @error_code, layout: layout, formats: [:html])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,63 @@
|
|
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
|
+
module Concerns
|
8
|
+
# A concern to help view handling.
|
9
|
+
module View
|
10
|
+
# Scopes the CSS of the current page using the controller and action name.
|
11
|
+
#
|
12
|
+
# @return [String] The scoped string.
|
13
|
+
def scope_css
|
14
|
+
"%s %s" % [controller_path.gsub("/", "-"), action_name]
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns an instance of the browser.
|
18
|
+
#
|
19
|
+
# @return [Browser] A browser object.
|
20
|
+
def browser
|
21
|
+
@browser ||= Brauser::Browser.new(request.user_agent)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Checks if the current browser is supported according to a definition YAML file.
|
25
|
+
#
|
26
|
+
# @param conf_file [String] The configuration file which holds the definitions.
|
27
|
+
# @return [Boolean] `true` if the browser is supported, `false` otherwise.
|
28
|
+
def browser_supported?(conf_file = nil)
|
29
|
+
conf_file ||= (Rails.root + "config/supported-browsers.yml").to_s if defined?(Rails)
|
30
|
+
browser.supported?(conf_file)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Outputs the Javascript parameters.
|
34
|
+
#
|
35
|
+
# @param as_html [Boolean] Whether to return the parameters as HTML rather than hash.
|
36
|
+
# @param tag [Symbol] The tag to use for HTML.
|
37
|
+
# @param id [String] The id for the tag.
|
38
|
+
# @return [String|Hash] Javascript parameters as HTML or the hash.
|
39
|
+
def javascript_params(as_html = true, tag = :details, id = nil)
|
40
|
+
as_html ? content_tag(tag, @javascript_params.to_json.html_safe, "data-jid" => id): @javascript_params
|
41
|
+
end
|
42
|
+
|
43
|
+
# Appends new Javascript parameters.
|
44
|
+
#
|
45
|
+
# @param key [String|Symbol] The key of the new parameters. If `nil`, the root will be merged/replaced.
|
46
|
+
# @param data [Hash] The data to add.
|
47
|
+
# @param replace [Boolean] Whether to replace existing data rather than merge.
|
48
|
+
def add_javascript_params(key, data, replace = false)
|
49
|
+
@javascript_params ||= HashWithIndifferentAccess.new
|
50
|
+
|
51
|
+
if key
|
52
|
+
@javascript_params[key] = nil if replace
|
53
|
+
@javascript_params[key] ||= {}
|
54
|
+
@javascript_params[key].merge!(data)
|
55
|
+
elsif replace
|
56
|
+
@javascript_params = data.with_indifferent_access
|
57
|
+
else
|
58
|
+
@javascript_params.merge!(data)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,38 @@
|
|
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 context for an operation. It is basically a Hash with few enhancements, like owner, errors and output support.
|
8
|
+
class Context < Interactor::Context
|
9
|
+
# Builds a new context.
|
10
|
+
#
|
11
|
+
# @param owner [Object] The owner of this context.
|
12
|
+
# @param additional [Hash] Additional parameters to include into the context.
|
13
|
+
def self.build(owner, additional = {})
|
14
|
+
super({
|
15
|
+
owner: owner,
|
16
|
+
errors: [],
|
17
|
+
output: nil,
|
18
|
+
response: HashWithIndifferentAccess.new
|
19
|
+
}.merge(additional).ensure_access(:indifferent))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Lookups missing methods in the delegatee hash.
|
23
|
+
#
|
24
|
+
# @param method [Symbol] The method to lookup.
|
25
|
+
# @param args [Array] The arguments passed to the method. *This is ignored.*
|
26
|
+
# @param block [Proc] The block passed to the method. *This is ignored.*
|
27
|
+
# @return [Object] The value for the method, if present.
|
28
|
+
def method_missing(method, *args, &block)
|
29
|
+
object = __getobj__
|
30
|
+
|
31
|
+
if object[method] then
|
32
|
+
object[method]
|
33
|
+
else
|
34
|
+
super(method, *args, &block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,34 @@
|
|
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
|
+
# Common errors raised by a Rails application.
|
8
|
+
module Errors
|
9
|
+
# The base error raised from an application.
|
10
|
+
#
|
11
|
+
# @attribute [r] response
|
12
|
+
# @return [String|Hash] The response which contains either a message or an hash with status code and a error message.
|
13
|
+
class BaseError < RuntimeError
|
14
|
+
attr_reader :response
|
15
|
+
|
16
|
+
def initialize(msg = nil)
|
17
|
+
super(msg)
|
18
|
+
@response = msg
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# This is raised when an invalid domain is requested.
|
23
|
+
class InvalidDomain < BaseError
|
24
|
+
end
|
25
|
+
|
26
|
+
# This is raised when something went wrong during the processing of a operation.
|
27
|
+
class PerformError < BaseError
|
28
|
+
end
|
29
|
+
|
30
|
+
# This is raised when some invalid parameters are passed to a operation.
|
31
|
+
class ValidationError < BaseError
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,136 @@
|
|
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 operation represents a single responsibility class. Subclasses should only expose and override the #perform method.
|
8
|
+
class Operation
|
9
|
+
extend ::Forwardable
|
10
|
+
include Interactor
|
11
|
+
def_delegators :context, :owner, :errors, :response, :output
|
12
|
+
|
13
|
+
# Performs the operation.
|
14
|
+
#
|
15
|
+
# @param owner_or_context [Object|Context] If is a context, then it will be the context of the operation, otherwise a blank a context with the object
|
16
|
+
# as owner will be created.
|
17
|
+
# @param context [NilClass] The context for the operation. *Ignored if `owner_or_context` is a context.*
|
18
|
+
# @param params [Hash] The additional parameters for the new context. *Ignored if `owner_or_context` is a context.*
|
19
|
+
# @return [Operation] The performed operation.
|
20
|
+
def self.perform(owner_or_context, context: nil, params: {})
|
21
|
+
arg = owner_or_context
|
22
|
+
arg = (context || ::Ballast::Context.build(owner_or_context, params)) if !arg.is_a?(::Ballast::Context)
|
23
|
+
super(arg)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Creates a new operation.
|
27
|
+
#
|
28
|
+
# @param context [Context] The context for the operation.
|
29
|
+
def initialize(context)
|
30
|
+
@context = context
|
31
|
+
setup
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sets up the response hash from instance variables.
|
35
|
+
#
|
36
|
+
# @param force [Boolean] Whether to setup the response even if the operation failed.
|
37
|
+
# @return [Hash] The response hash.
|
38
|
+
def setup_response(force = false)
|
39
|
+
if success? || force then
|
40
|
+
vars = instance_variables
|
41
|
+
vars.delete(:@context)
|
42
|
+
|
43
|
+
context.response.merge!(vars.reduce({}){ |rv, var|
|
44
|
+
rv[var.to_s.gsub(/[:@]/, "")] = instance_variable_get(var)
|
45
|
+
rv
|
46
|
+
})
|
47
|
+
end
|
48
|
+
|
49
|
+
context.response
|
50
|
+
end
|
51
|
+
|
52
|
+
# Imports the response hash into the target instance variables.
|
53
|
+
#
|
54
|
+
# @param target [Object] The target of the import.
|
55
|
+
# @param fields [Array] The keys to import.
|
56
|
+
# @param overwrite [Boolean] Whether to overwrite existing variables into the target. If set to `false`, any overwrite will raise an `ArgumentError`.
|
57
|
+
def import_response(target, *fields, overwrite: true)
|
58
|
+
fields.each do |field|
|
59
|
+
raise ArgumentError.new(field) if target.instance_variable_get("@#{field}") && !overwrite
|
60
|
+
target.instance_variable_set("@#{field}", response[field])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Performs the operation handling base errors.
|
65
|
+
#
|
66
|
+
# @param setup_response_after [Boolean] Whether to setup the response after processing.
|
67
|
+
def perform_with_handling(setup_response_after = true)
|
68
|
+
begin
|
69
|
+
yield
|
70
|
+
rescue Lazier::Exceptions::Debug => de
|
71
|
+
raise de
|
72
|
+
rescue => e
|
73
|
+
e.is_a?(::Ballast::Errors::BaseError) ? fail!(e.response) : raise(e)
|
74
|
+
end
|
75
|
+
|
76
|
+
setup_response if setup_response_after
|
77
|
+
end
|
78
|
+
|
79
|
+
# Marks failure of the operation appending the error to the context.
|
80
|
+
#
|
81
|
+
# @param error [Object|NilClass] The error to store.
|
82
|
+
def fail!(error = nil)
|
83
|
+
errors << error if error
|
84
|
+
super()
|
85
|
+
end
|
86
|
+
|
87
|
+
# Imports the current operation errors into the target's `@error` instance variable or in the flash hash.
|
88
|
+
#
|
89
|
+
# @param target [Object] The target of the import.
|
90
|
+
# @param to_flash [Boolean] If to import the error in the target's flash object rather than the instance variable.
|
91
|
+
# @param first_only [Boolean] If to only import the first error.
|
92
|
+
def import_error(target, to_flash = true, first_only = true)
|
93
|
+
values = errors
|
94
|
+
values = values.map {|v| v[:error] } if to_flash
|
95
|
+
values = values.first if first_only
|
96
|
+
|
97
|
+
if to_flash then
|
98
|
+
target.flash[:error] = values
|
99
|
+
else
|
100
|
+
target.instance_variable_set(:@error, values)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Resolves a numeric error to a human readable message.
|
105
|
+
#
|
106
|
+
# @param error [BaseError|Fixnum] The error to resolve.
|
107
|
+
# @param supported_messages [Hash] The list of supported error codes.
|
108
|
+
# @param only_message [Boolean] If to only return the message string rather than a full error hash.
|
109
|
+
# @return [String|Hash] The error with a human readable message or the message alone.
|
110
|
+
def resolve_error(error, supported_messages = {}, only_message = false)
|
111
|
+
code = (error.respond_to?(:response) ? error.response : 500).to_integer(500)
|
112
|
+
rv = {status: code, error: supported_messages.fetch(code, "Oops! We're having some issue. Please try again later.")}
|
113
|
+
only_message ? rv[:error] : rv
|
114
|
+
end
|
115
|
+
|
116
|
+
# If running under eventmachine, run the block in a thread of its threadpool using EM::Synchrony, otherwise run the block normally.
|
117
|
+
#
|
118
|
+
# @param block [Proc] The block to run.
|
119
|
+
def in_em_thread(&block)
|
120
|
+
EM.reactor_running? ? EM::Synchrony.defer(&block) : block.call
|
121
|
+
end
|
122
|
+
|
123
|
+
# Forwards any missing method to the owner.
|
124
|
+
#
|
125
|
+
# @param method [Symbol] The method to forward.
|
126
|
+
# @param args [Array] The arguments to pass to the method.
|
127
|
+
# @param block [Proc] The block to pass to the method.
|
128
|
+
def method_missing(method, *args, &block)
|
129
|
+
if owner.respond_to?(method)
|
130
|
+
owner.send(method, *args, &block)
|
131
|
+
else
|
132
|
+
super(method, *args, &block)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,38 @@
|
|
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 operation made of several operation run sequentially passing the common context. The chain will stop on the first failure.
|
8
|
+
#
|
9
|
+
# @attribute [r] operations
|
10
|
+
# @return [Array] The list of operations performed.
|
11
|
+
class OperationsChain < Operation
|
12
|
+
include ::Interactor::Organizer
|
13
|
+
attr_reader :operations
|
14
|
+
|
15
|
+
# Performs the chain.
|
16
|
+
#
|
17
|
+
# @param argument [Object|Context] If is a context, then it will be the context of the operation, unless a blank a context with the object
|
18
|
+
# as owner will be created.
|
19
|
+
# @param operations [Array] The list of operations to perform.
|
20
|
+
# @param context [NilClass] The context for the operation. *Ignored if `owner_or_context` is a context.*
|
21
|
+
# @param params [Hash] The additional parameters for the new context. *Ignored if `owner_or_context` is a context.*
|
22
|
+
# @return [Operation] The performed chain.
|
23
|
+
def self.perform(argument, operations, context: nil, params: {})
|
24
|
+
argument = (context || ::Ballast::Context.build(argument, params)) if !argument.is_a?(::Ballast::Context)
|
25
|
+
new(operations, argument).tap(&:perform)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a new chain.
|
29
|
+
#
|
30
|
+
# @param operations [Array] The list of operations to perform.
|
31
|
+
# @param context [Context] The context for the chain.
|
32
|
+
def initialize(operations, context)
|
33
|
+
@context = context
|
34
|
+
@operations = operations.ensure_array
|
35
|
+
setup
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
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
|
+
# A collection of base utilities for Ruby on Rails.
|
7
|
+
module Ballast
|
8
|
+
# The current version of ballast, according to semantic versioning.
|
9
|
+
#
|
10
|
+
# @see http://semver.org
|
11
|
+
module Version
|
12
|
+
# The major version.
|
13
|
+
MAJOR = 1
|
14
|
+
|
15
|
+
# The minor version.
|
16
|
+
MINOR = 0
|
17
|
+
|
18
|
+
# The patch version.
|
19
|
+
PATCH = 0
|
20
|
+
|
21
|
+
# The current version of ballast.
|
22
|
+
STRING = [MAJOR, MINOR, PATCH].compact.join(".")
|
23
|
+
end
|
24
|
+
end
|
data/lib/ballast.rb
ADDED
@@ -0,0 +1,24 @@
|
|
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 "lazier"
|
7
|
+
require "brauser"
|
8
|
+
require "interactor"
|
9
|
+
require "addressable/uri"
|
10
|
+
require "rack/utils"
|
11
|
+
require "rack/fiber_pool"
|
12
|
+
require "em-synchrony"
|
13
|
+
|
14
|
+
Lazier.load!
|
15
|
+
|
16
|
+
require "ballast/version" if !defined?(Ballast::Version)
|
17
|
+
require "ballast/errors"
|
18
|
+
require "ballast/context"
|
19
|
+
require "ballast/operation"
|
20
|
+
require "ballast/operations_chain"
|
21
|
+
require "ballast/concerns/ajax"
|
22
|
+
require "ballast/concerns/common"
|
23
|
+
require "ballast/concerns/view"
|
24
|
+
require "ballast/concerns/errors_handling"
|