ballast 1.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 +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"
|