ballast 1.9.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +35 -0
  3. data/.travis-gemfile +4 -5
  4. data/.travis.yml +3 -2
  5. data/CHANGELOG.md +62 -6
  6. data/Gemfile +7 -8
  7. data/README.md +2 -2
  8. data/ballast.gemspec +9 -12
  9. data/doc/Ballast/AjaxResponse.html +1380 -0
  10. data/doc/Ballast/Concerns/AjaxHandling.html +662 -0
  11. data/doc/Ballast/Concerns/Common.html +81 -361
  12. data/doc/Ballast/Concerns/ErrorsHandling.html +18 -36
  13. data/doc/Ballast/Concerns/View.html +181 -157
  14. data/doc/Ballast/Concerns.html +6 -6
  15. data/doc/Ballast/Configuration.html +204 -31
  16. data/doc/Ballast/Emoji/Character.html +411 -0
  17. data/doc/Ballast/Emoji/Utils.html +794 -0
  18. data/doc/Ballast/Emoji.html +125 -0
  19. data/doc/Ballast/Errors/{BaseError.html → Base.html} +61 -34
  20. data/doc/Ballast/Errors/{PerformError.html → Failure.html} +21 -17
  21. data/doc/Ballast/Errors/InvalidDomain.html +11 -11
  22. data/doc/Ballast/Errors/{ValidationError.html → ValidationFailure.html} +24 -16
  23. data/doc/Ballast/Errors.html +5 -5
  24. data/doc/Ballast/Middlewares/DefaultHost.html +6 -6
  25. data/doc/Ballast/Middlewares.html +4 -4
  26. data/doc/Ballast/RequestDomainMatcher.html +28 -28
  27. data/doc/Ballast/Service/Response.html +1243 -0
  28. data/doc/Ballast/Service.html +1314 -0
  29. data/doc/Ballast/Version.html +7 -7
  30. data/doc/Ballast.html +15 -15
  31. data/doc/_index.html +59 -30
  32. data/doc/class_list.html +6 -2
  33. data/doc/css/style.css +1 -0
  34. data/doc/file.README.html +6 -6
  35. data/doc/file_list.html +5 -1
  36. data/doc/frames.html +1 -1
  37. data/doc/index.html +6 -6
  38. data/doc/js/full_list.js +4 -1
  39. data/doc/method_list.html +167 -79
  40. data/doc/top-level-namespace.html +41 -4
  41. data/lib/ballast/ajax_response.rb +76 -0
  42. data/lib/ballast/concerns/ajax_handling.rb +73 -0
  43. data/lib/ballast/concerns/common.rb +25 -47
  44. data/lib/ballast/concerns/errors_handling.rb +21 -30
  45. data/lib/ballast/concerns/view.rb +24 -22
  46. data/lib/ballast/configuration.rb +30 -10
  47. data/lib/ballast/emoji.rb +114 -0
  48. data/lib/ballast/errors.rb +16 -13
  49. data/lib/ballast/middlewares/default_host.rb +3 -3
  50. data/lib/ballast/request_domain_matcher.rb +7 -7
  51. data/lib/ballast/service.rb +147 -0
  52. data/lib/ballast/version.rb +3 -3
  53. data/lib/ballast.rb +22 -22
  54. data/spec/ballast/ajax_response_spec.rb +61 -0
  55. data/spec/ballast/concerns/ajax_handling_spec.rb +86 -0
  56. data/spec/ballast/concerns/common_spec.rb +17 -52
  57. data/spec/ballast/concerns/errors_handling_spec.rb +35 -29
  58. data/spec/ballast/concerns/view_spec.rb +21 -32
  59. data/spec/ballast/configuration_spec.rb +66 -16
  60. data/spec/ballast/emoji_spec.rb +103 -0
  61. data/spec/ballast/errors_spec.rb +5 -5
  62. data/spec/ballast/middlewares/default_host_spec.rb +3 -5
  63. data/spec/ballast/request_domain_matcher_spec.rb +4 -4
  64. data/spec/ballast/service_spec.rb +137 -0
  65. data/spec/spec_helper.rb +1 -13
  66. metadata +42 -80
  67. data/doc/Ballast/Concerns/Ajax.html +0 -945
  68. data/doc/Ballast/Context.html +0 -417
  69. data/doc/Ballast/Operation.html +0 -1304
  70. data/doc/Ballast/OperationsChain.html +0 -597
  71. data/lib/ballast/concerns/ajax.rb +0 -134
  72. data/lib/ballast/context.rb +0 -38
  73. data/lib/ballast/operation.rb +0 -136
  74. data/lib/ballast/operations_chain.rb +0 -45
  75. data/spec/ballast/concerns/ajax_spec.rb +0 -141
  76. data/spec/ballast/context_spec.rb +0 -23
  77. data/spec/ballast/operation_spec.rb +0 -177
  78. data/spec/ballast/operations_chain_spec.rb +0 -61
@@ -0,0 +1,73 @@
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 AjaxHandling
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 ajax_request?
17
+ request.safe_send(:xhr?).to_boolean || 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] The data of the response.
24
+ # @param error [Object|NilClass] The error of the response.
25
+ def prepare_ajax_response(status: :ok, data: {}, error: nil)
26
+ Ballast::AjaxResponse.new(status: status, data: data, error: error, transport: self)
27
+ end
28
+
29
+ # Prevents HTTP caching.
30
+ def prevent_caching
31
+ response.headers.merge!({
32
+ "Cache-Control" => "no-cache, no-store, max-age=0, must-revalidate",
33
+ "Pragma" => "no-cache",
34
+ "Expires" => "Fri, 01 Jan 1990 00:00:00 GMT"
35
+ })
36
+ end
37
+
38
+ # Allows HTTP Cross-Origin Resource Sharing.
39
+ #
40
+ # @param allow_origin [String] The value for the `Access-Control-Allow-Origin` header.
41
+ # @param allow_methods [Array] A list of methods for the `Access-Control-Allow-Methods` header.
42
+ # @param allow_headers [String] The value for the `Access-Control-Allow-Headers` header.
43
+ # @param max_age [Float|Fixnum] The value for the `Access-Control-Max-Age` header.
44
+ # @param allow_credentials [Boolean] The value for the `Access-Control-Allow-Credentials` header.
45
+ def allow_cors(allow_origin: "*", allow_methods: [:post, :get, :options], allow_headers: "*", max_age: 1.year, allow_credentials: false)
46
+ headers.merge!({
47
+ "Access-Control-Allow-Origin" => allow_origin,
48
+ "Access-Control-Allow-Methods" => allow_methods.map { |m| m.to_s.upcase }.join(", "),
49
+ "Access-Control-Allow-Headers" => allow_headers,
50
+ "Access-Control-Max-Age" => max_age.to_i.to_s
51
+ })
52
+
53
+ headers["Access-Control-Allow-Credentials"] = "true" if allow_credentials
54
+ end
55
+
56
+ # Generates a `robots.txt file.
57
+ #
58
+ # @param configuration [Hash|NilClass] An hash of agent and list of paths to include.
59
+ def generate_robots_txt(configuration = nil)
60
+ configuration ||= {"*" => "/"}
61
+ rv = configuration.reduce([]) { |accu, (agent, paths)|
62
+ paths = paths.ensure_array.map { |e| "Disallow: #{e}" }
63
+
64
+ accu << "User-agent: #{agent}\n#{paths.join("\n")}"
65
+ accu
66
+ }.join("\n\n")
67
+
68
+ render(text: rv, content_type: "text/plain")
69
+ end
70
+ alias_method :disallow_robots, :generate_robots_txt
71
+ end
72
+ end
73
+ end
@@ -9,53 +9,32 @@ module Ballast
9
9
  module Common
10
10
  # Checks if the current request wants JSON or JSONP as response.
11
11
  #
12
- # @return [Boolean] `true` if the request is JSON, `false` otherwise.
13
- def is_json?
14
- ([:json, :jsonp].include?(request.format.to_sym) || params[:json].to_boolean) ? true : false
12
+ # @return [Boolean] `true` if the request is JSON(P), `false` otherwise.
13
+ def json?
14
+ [:json, :jsonp].include?(request.format.to_sym) || params[:json].to_boolean
15
15
  end
16
16
 
17
17
  # Checks if the user is sending any data.
18
18
  #
19
19
  # @return [Boolean] `true` if the user is sending data, `false` otherwise.
20
- def sending_data?
21
- request.post? || request.put?
22
- end
23
-
24
- # Performs an operation, using itself as owner by default.
25
- #
26
- # @param klass [Class] The operation to perform.
27
- # @param owner [Object] The owner to use. By default it uses itself.
28
- # @param kwargs [Hash] The arguments for performing.
29
- # @return [Operation] The performed operation.
30
- def perform_operation(klass, owner = nil, **kwargs)
31
- @operation = klass.perform(owner || self, **kwargs)
32
- end
33
-
34
- # Performs an operations chain, using itself as owner by default.
35
- #
36
- # @param klasses [Array] The operations to perform.
37
- # @param owner [Object] The owner to use. By default it uses itself.
38
- # @param kwargs [Hash] The arguments for performing.
39
- # @return [OperationChain] The performed operation chain.
40
- def perform_operations_chain(klasses, owner = nil, **kwargs)
41
- @operation = Ballast::OperationsChain.perform(owner || self, klasses, **kwargs)
20
+ def request_data?
21
+ request.post? || request.put? || request.patch?
42
22
  end
43
23
 
44
24
  # Formats a relative date using abbreviation or short formats.
45
25
  #
46
26
  # @param date [DateTime] The date to format.
47
- # @param reference [DateTime] The reference date.
27
+ # @param reference [DateTime|NilClass] The reference date.
48
28
  # @param suffix [String] The suffix to add to the formatted date.
49
29
  # @return [String] The formatted date.
50
- def format_short_duration(date, reference = nil, suffix = "")
51
- reference ||= Time.now
52
- amount = (reference.to_i - date.to_i).to_i
30
+ def format_short_duration(date, reference: nil, suffix: "")
31
+ amount = (reference || Time.now).to_i - date.to_i
53
32
 
54
- if amount <= 0 then
33
+ if amount <= 0
55
34
  "now"
56
- elsif amount < 1.day then
35
+ elsif amount < 1.day
57
36
  format_short_amount(amount, suffix)
58
- elsif amount < 1.year then
37
+ elsif amount < 1.year
59
38
  date.strftime("%b %d")
60
39
  else
61
40
  date.strftime("%b %d %Y")
@@ -68,9 +47,9 @@ module Ballast
68
47
  # @param suffix [String] The suffix to add to the formatted amount.
69
48
  # @return [String] The formatted amount.
70
49
  def format_short_amount(amount, suffix = "")
71
- if amount < 1.minute then
50
+ if amount < 1.minute
72
51
  "#{amount.floor}s#{suffix}"
73
- elsif amount < 1.hour then
52
+ elsif amount < 1.hour
74
53
  "#{(amount / 60).floor}m#{suffix}"
75
54
  else
76
55
  "#{(amount / 3600).floor}h#{suffix}"
@@ -83,29 +62,28 @@ module Ballast
83
62
  # @param separator [String] The separator between date and time.
84
63
  # @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
85
64
  # and `%:Z` for the zone name considering also DST.
86
- def format_long_date(date, separator = "•", format = "%I:%M%p %- %b %o, %Y (%:Z)")
65
+ def format_long_date(date, separator: "•", format: "%I:%M%p %- %b %o, %Y (%:Z)")
87
66
  tz = Time.zone
88
- replacements = {"%-" => separator, "%o" => date.day.ordinalize, "%:Z" => tz.send(tz.uses_dst? && date.dst? ? :dst_name : :name)}
89
- date.strftime(format).gsub(/%(-|o|(:Z))/) {|r| replacements.fetch(r, r) }
67
+ replacements = {"%-" => separator, "%o" => date.day.ordinalize, "%:Z" => tz.current_name(tz.uses_dst? && date.dst?)}
68
+ date.strftime(format).gsub(/%(-|o|(:Z))/) { |r| replacements.fetch(r, r) }
90
69
  end
91
70
 
92
71
  # Authenticates a user via HTTP, handling the error if the authentication failed.
93
72
  #
94
- # @param area [String] The name of the area.
95
- # @param title [String] A title for authentication errors.
96
- # @param message [String] A message for authentication errors.
73
+ # @param area [String|NilClass] The name of the area.
74
+ # @param title [String|NilClass] A title for authentication errors.
75
+ # @param message [String|NilClass] A message for authentication errors.
97
76
  # @param authenticator [Proc] A block to verify if authentication is valid.
98
- def authenticate_user(area = nil, title = nil, message = nil, &authenticator)
77
+ def authenticate_user(area: nil, title: nil, message: nil, &authenticator)
78
+ return if authenticate_with_http_basic { |username, password| authenticator.call(username, password) }
79
+
99
80
  area ||= "Private Area"
100
81
  title ||= "Authentication required."
101
82
  message ||= "To view this resource you have to authenticate."
102
- authenticated = authenticate_with_http_basic { |username, password| authenticator.call(username, password) }
103
83
 
104
- if !authenticated then
105
- headers["WWW-Authenticate"] = "Basic realm=\"#{area}\""
106
- handle_error({status: 401, title: title, message: message})
107
- end
84
+ headers["WWW-Authenticate"] = "Basic realm=\"#{area}\""
85
+ handle_error({status: 401, title: title, message: message})
108
86
  end
109
87
  end
110
88
  end
111
- end
89
+ end
@@ -12,44 +12,35 @@ module Ballast
12
12
  # Handles an error in the application.
13
13
  #
14
14
  # @param exception [Hash|Exception] The exception to handle.
15
- # @param layout [String] The layout to use to render the error.
15
+ # @param layout [String] The layout to use to render the error. The `@error` variable will be exposed.
16
16
  # @param title [String] The title to set in case of custom errors.
17
- # @param format [String|Symbol] The format of the response.
18
- def handle_error(exception = nil, layout = "error", title = "Error - Application", format = nil)
19
- @error ||= exception
20
-
21
- if @error.is_a?(Lazier::Exceptions::Debug) then
22
- @error_title = "Debug"
23
- @error_code = 503
24
- elsif @error.is_a?(Hash) then
25
- @error_title = @error[:title] || title
26
- @error_code = @error[:status]
27
- @error_message = @error[:error]
28
- else
29
- @error_title = "Error - #{@error.class.to_s}"
30
- @error_code = 500
31
- end
17
+ # @param format [String|Symbol|NilClass] The format of the response.
18
+ def handle_error(exception, layout: "error", title: "Error - Application", format: nil)
19
+ @error =
20
+ if exception.is_a?(Lazier::Exceptions::Debug)
21
+ {status: 503, title: "Debug", error: exception.message, exception: exception}
22
+ elsif exception.is_a?(::Hash)
23
+ exception.reverse_merge({title: title})
24
+ else
25
+ {status: 500, title: "Error - #{exception.class}", error: exception.message, exception: exception}
26
+ end
32
27
 
33
28
  send_or_render_error(layout, format)
34
29
  end
35
30
 
36
31
  private
37
- # Send an AJAX error o renders it.
38
- #
39
- # @param layout [String] The layout to use to render the error.
40
- # @param format [String|Symbol] The format of the response.
41
- def send_or_render_error(layout, format = nil)
42
- format ||= request.format.to_sym
43
32
 
44
- if is_ajax? || format.to_s =~ /^json/ then
45
- details = {type: @error_title}
46
- details[:backtrace] = @error.backtrace if @error.respond_to?(:backtrace)
47
- data = prepare_ajax(@error_code, details, @error_message || @error.message)
48
- send_ajax(data, format: format)
49
- else
50
- render(html: "", status: @error_code, layout: layout, formats: [:html])
51
- end
33
+ # :nodoc:
34
+ def send_or_render_error(layout, format = nil)
35
+ format ||= request.format.to_sym
36
+
37
+ if ajax_request? || format.match(/^json/)
38
+ details = {description: @error[:title], backtrace: @error[:exception].safe_send(:backtrace)}
39
+ prepare_ajax_response(status: @error[:status], data: details, error: @error[:error]).reply(format: format)
40
+ else
41
+ render(html: "", status: @error[:status], layout: layout, formats: [:html])
52
42
  end
43
+ end
53
44
  end
54
45
  end
55
46
  end
@@ -11,30 +11,30 @@ module Ballast
11
11
  #
12
12
  # @return [String] The scoped string.
13
13
  def scope_css
14
- "%s %s" % [controller_path.gsub("/", "-"), action_name]
14
+ format("%s %s", controller_path.gsub("/", "-"), action_name)
15
15
  end
16
16
 
17
17
  # Returns an instance of the browser.
18
18
  #
19
19
  # @return [Browser] A browser object.
20
20
  def browser
21
- @browser ||= Brauser::Browser.new(request.user_agent)
21
+ @browser ||= Brauser::Browser.new(request.user_agent, request.headers["Accept-Language"])
22
22
  end
23
23
 
24
24
  # Checks if the current browser is supported according to a definition YAML file.
25
25
  #
26
- # @param conf_file [String] The configuration file which holds the definitions.
26
+ # @param file [String] The configuration file which holds the definitions.
27
+ # @param root [String|NilClass] The directory that contains the configuration file.
27
28
  # @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)
29
+ def browser_supported?(file = "config/supported-browsers.yml", root: nil)
30
+ browser.supported?(((Ballast::Configuration.default_root || root) + "/" + file).to_s)
31
31
  end
32
32
 
33
33
  # Returns one or all layout parameters.
34
34
  #
35
35
  # @param key [String|Symbol|NilClass] The parameter to return. If set to `nil`, all the parameters will be returned as an hash.
36
- # @param default_value [Object] The default value if the parameter is not present.
37
- # @return [Object|Hash] The parameter or the entire layout parameters hash.
36
+ # @param default_value [Object|NilClass] The default value if the parameter is not present.
37
+ # @return [Object|Hash|NilClass] The parameter or the entire layout parameters hash.
38
38
  def layout_params(key = nil, default_value = nil)
39
39
  initialize_view_params
40
40
  key ? @layout_params.fetch(key, default_value) : @layout_params
@@ -43,8 +43,8 @@ module Ballast
43
43
 
44
44
  # Adds/Replaces layout parameters.
45
45
  #
46
- # @param args [Hash] The new parameters to add.
47
- def set_layout_params(**args)
46
+ # @param args [Hash] The parameters to add or replace.
47
+ def update_layout_params(**args)
48
48
  initialize_view_params
49
49
  @layout_params.merge!(args)
50
50
  end
@@ -53,18 +53,19 @@ module Ballast
53
53
  #
54
54
  # @param id [String|NilClass|FalseClass] The id for the tag. If `nil` or `false`, the parameters will be returned as an hash.
55
55
  # @param tag [Symbol] The tag to use for HTML.
56
- # @return [String|Hash] Javascript parameters as HTML or the hash.
57
- def javascript_params(id = nil, tag = :details)
56
+ # @param attribute [Symbol] The attribute to use for the HTML element id.
57
+ # @return [String|Hash] Javascript parameters as HTML or as an hash.
58
+ def javascript_params(id = nil, tag: :details, attribute: "data-jid")
58
59
  initialize_view_params
59
- id ? content_tag(tag, @javascript_params.to_json.html_safe, "data-jid" => id): @javascript_params
60
+ id ? content_tag(tag, @javascript_params.to_json.html_safe, attribute => id) : @javascript_params
60
61
  end
61
62
 
62
- # Appends new Javascript parameters.
63
+ # Adds/Replaces Javascript parameters.
63
64
  #
64
65
  # @param key [String|Symbol] The key of the new parameters. If `nil`, the root will be merged/replaced.
65
- # @param data [Hash] The data to add.
66
+ # @param data [Hash] The data to add or replace.
66
67
  # @param replace [Boolean] Whether to replace existing data rather than merge.
67
- def add_javascript_params(key, data, replace = false)
68
+ def update_javascript_params(key, data, replace: false)
68
69
  initialize_view_params
69
70
 
70
71
  if key
@@ -79,11 +80,12 @@ module Ballast
79
80
  end
80
81
 
81
82
  private
82
- # Prepares parameters for views.
83
- def initialize_view_params
84
- @layout_params ||= HashWithIndifferentAccess.new
85
- @javascript_params ||= HashWithIndifferentAccess.new
86
- end
83
+
84
+ # :nodoc:
85
+ def initialize_view_params
86
+ @layout_params ||= HashWithIndifferentAccess.new
87
+ @javascript_params ||= HashWithIndifferentAccess.new
88
+ end
87
89
  end
88
90
  end
89
- end
91
+ end
@@ -7,24 +7,44 @@ module Ballast
7
7
  # A class which loads a list of YAML files in a folder and expose them in a dotted notation.
8
8
  # For each file, only the subsection for the current environment is loaded, so each YAML document should be an hash.
9
9
  class Configuration < HashWithIndifferentAccess
10
+ # Returns the default root directory to lookup a configuration. It will be the Rails root if set or the current folder.
11
+ #
12
+ # @return [String] The default root directory to lookup a configuration.
13
+ def self.default_root
14
+ defined?(Rails) ? Rails.root.to_s : Dir.pwd
15
+ end
16
+
17
+ # Returns the default environment. It will be the first non-nil of the following: Rails environment, the Rack environment or "production".
18
+ #
19
+ # @return [String] The default environment.
20
+ def self.default_environment
21
+ defined?(Rails) ? Rails.env : ENV.fetch("RACK_ENV", "production")
22
+ end
23
+
10
24
  # Creates a new configuration.
11
25
  #
12
26
  # @param sections [Array] A list of sections to load. Each section name should be the basename (without extension) of a file in the root folder.
13
27
  # Subfolders are not supported.
14
- # @param root [String] The root folder where look for file. Default is the Rails root.
15
- # @param environment [String] The environment to load. Default is the Rails environment.
16
- def initialize(sections: [], root: nil, environment: nil)
28
+ # @param root [String|NilClass] The root folder where look for file.
29
+ # @param environment [String|NilClass] The environment to load.
30
+ def initialize(*sections, root: nil, environment: nil)
17
31
  super()
18
- root ||= Rails.root.to_s
19
- environment ||= Rails.env
32
+ root ||= ::Ballast::Configuration.default_root
33
+ environment ||= ::Ballast::Configuration.default_environment
20
34
 
21
35
  sections.each do |section|
22
- content = (YAML.load_file("#{root}/config/#{section}.yml") rescue {}).with_indifferent_access
23
- self[section] = content[environment]
24
- self[section.underscore] = self[section] if section.index("-")
36
+ content = load_section(root, section)
37
+ self[section.underscore] = content.fetch(environment, {})
25
38
  end
26
39
 
27
- self.enable_dotted_access
40
+ enable_dotted_access
41
+ end
42
+
43
+ private
44
+
45
+ # :nodoc:
46
+ def load_section(root, section)
47
+ YAML.load_file("#{root}/config/#{section}.yml") rescue {}
28
48
  end
29
49
  end
30
- end
50
+ end
@@ -0,0 +1,114 @@
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 module to ease emoji handling.
8
+ module Emoji
9
+ # General utility methods.
10
+ #
11
+ # @attribute url_mapper
12
+ # @return [Proc] The current URL mapper or a default one (which will return the relative URL unmodified).
13
+ module Utils
14
+ attr_accessor :url_mapper
15
+
16
+ # Returns the regular expression which matches all the known emojis.
17
+ #
18
+ # @return [Regexp] The regular expression which matches all the known emojis.
19
+ def replace_regex
20
+ @replace_regex ||= /(#{::Emoji.send(:unicodes_index).keys.join("|")})/
21
+ end
22
+
23
+ # Replaces all the emojis in the text using the requested mod.
24
+ #
25
+ # @param text [String] The text to manipulate.
26
+ # @param mode [Symbol] The method to use when replacing icons.
27
+ # @param options [Hash] The options to pass to the replacing method.
28
+ # @return [String] The text with all emojis replaced.
29
+ def replace(text, mode: :html, **options)
30
+ mode = :markup unless mode && ::Emoji::Character.new(nil).respond_to?(mode)
31
+ text.ensure_string.gsub(replace_regex) { invoke(::Emoji.find_by_unicode(Regexp.last_match[1]), mode, options) }
32
+ end
33
+
34
+ # Lists all the emoji known in a hash.
35
+ #
36
+ # @param keys_method [Symbol] The method to use for keys.
37
+ # @param values_method [Symbol] The method to use for values.
38
+ # @param options [Hash] The options to pass to all methods.
39
+ # @return [Hash] A hash of all known emojis.
40
+ def enumerate(keys_method: :markup, values_method: :html, **options)
41
+ tester = ::Emoji::Character.new(nil)
42
+ keys_method = :markup unless keys_method && tester.respond_to?(keys_method)
43
+ values_method = :html unless values_method && tester.respond_to?(values_method)
44
+
45
+ ::Emoji.all.reduce({}) { |accu, icon|
46
+ accu[invoke(icon, keys_method, options)] = invoke(icon, values_method, options)
47
+ accu
48
+ }
49
+ end
50
+
51
+ # Returns the URL mapper for the emojis.
52
+ #
53
+ # @return [Proc] The current URL mapper or a default one (which will return the relative URL unmodified).
54
+ def url_mapper
55
+ @url_mapper || ->(url) { url }
56
+ end
57
+
58
+ # Returns a absolute URL for a emoji image.
59
+ #
60
+ # @param image [String] The relative URL of the emoji filename.
61
+ # @return [String] The absolute URL of the emoji filename.
62
+ def url_for(image)
63
+ url_mapper.call(image)
64
+ end
65
+
66
+ private
67
+
68
+ # :nodoc:
69
+ def invoke(subject, method, options)
70
+ subject.method(method).arity == 1 ? subject.send(method, options) : subject.send(method)
71
+ end
72
+ end
73
+
74
+ # Extensions for a emoji character.
75
+ module Character
76
+ include ActionView::Helpers::TagHelper
77
+ include ActiveSupport::Concern
78
+
79
+ # Returns a markup for the current character.
80
+ #
81
+ # @return [String] The markup for a character.
82
+ def markup
83
+ ":#{name}:"
84
+ end
85
+
86
+ # Returns a image URL for the current character.
87
+ #
88
+ # @return [String] The image URL for the current character.
89
+ def url
90
+ ::Emoji.url_for(image_filename)
91
+ end
92
+
93
+ # Returns a image tag for the current character.
94
+ # @see ActionView::Helpers::TagHelper#tag
95
+ #
96
+ # @return [String] The options for the tag generation.
97
+ def image_tag(options = {})
98
+ options = options.reverse_merge({alt: markup, title: markup, rel: "tooltip"})
99
+ classes = options[:class].ensure_string.tokenize(pattern: /[\s,]+/, no_duplicates: true)
100
+ classes << "emoji" unless classes.include?("emoji")
101
+
102
+ options[:src] = url
103
+ options[:class] = classes.uniq.join(" ")
104
+
105
+ tag(:img, options)
106
+ end
107
+ alias_method :image, :url
108
+ alias_method :html, :image_tag
109
+ end
110
+ end
111
+ end
112
+
113
+ ::Emoji.extend(Ballast::Emoji::Utils)
114
+ ::Emoji::Character.include(Ballast::Emoji::Character)
@@ -8,27 +8,30 @@ module Ballast
8
8
  module Errors
9
9
  # The base error raised from an application.
10
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
11
+ # @attribute [r] details
12
+ # @return [String|Hash|NilClass] The details of the error. If a Hash, it should contain the keys `status` and `error`.
13
+ class Base < RuntimeError
14
+ attr_reader :details
15
15
 
16
- def initialize(msg = nil)
17
- super(msg)
18
- @response = msg
16
+ # Creates a new error.
17
+ #
18
+ # @param details [String|Hash|NilClass] The details of this error.
19
+ def initialize(details = nil)
20
+ super("")
21
+ @details = details
19
22
  end
20
23
  end
21
24
 
22
25
  # This is raised when an invalid domain is requested.
23
- class InvalidDomain < BaseError
26
+ class InvalidDomain < Base
24
27
  end
25
28
 
26
- # This is raised when something went wrong during the processing of a operation.
27
- class PerformError < BaseError
29
+ # This is raised when something went wrong during the processing of a operation or a service.
30
+ class Failure < Base
28
31
  end
29
32
 
30
- # This is raised when some invalid parameters are passed to a operation.
31
- class ValidationError < BaseError
33
+ # This is raised when some invalid parameters are passed to a operation or a service.
34
+ class ValidationFailure < Failure
32
35
  end
33
36
  end
34
- end
37
+ end
@@ -22,9 +22,9 @@ module Ballast
22
22
  # @param env [Hash] A Rack environment.
23
23
  def call(env)
24
24
  old_host = env["SERVER_NAME"].ensure_string
25
- new_host = @hosts[ENV["RACK_ENV"]]
25
+ new_host = @hosts[ENV.fetch("RACK_ENV", "production")]
26
26
 
27
- if old_host =~ /^\d/ && new_host then
27
+ if old_host =~ /^\d/ && new_host
28
28
  env["ORIG_SERVER_NAME"] = old_host
29
29
  env["ORIG_HTTP_HOST"] = env["HTTP_HOST"].dup
30
30
  env["SERVER_NAME"] = new_host
@@ -35,4 +35,4 @@ module Ballast
35
35
  end
36
36
  end
37
37
  end
38
- end
38
+ end
@@ -9,20 +9,20 @@ module Ballast
9
9
  # @attribute domains
10
10
  # @return [Array] The list of domains which mark a positive match.
11
11
  # @attribute replace_pattern
12
- # @return [String|Regexp] A optional pattern to replace in the request host. See `String#gsub`.
12
+ # @return [String|Regexp] A optional pattern to manipulate the request host before trying the match. See `String#gsub`.
13
13
  # @attribute replace_string
14
- # @return [String] A string to use for replacement in the request host. See `String#gsub`.
14
+ # @return [String] A string to manipulate the request host before trying the match. See `String#gsub`.
15
15
  # @attribute replace_block
16
- # @return [Proc] A block to use for replacement in the request host. See `String#gsub`.
16
+ # @return [Proc] A block to use to manipulate the request host before trying the match. See `String#gsub`.
17
17
  class RequestDomainMatcher
18
18
  attr_accessor :domains, :replace_pattern, :replace_string, :replace_block
19
19
 
20
20
  # Creates a new matcher.
21
21
  #
22
22
  # @param domains [String|Array] The list of domains which mark a positive match.
23
- # @param replace_pattern [String|Regexp] A optional pattern to replace in the request host. See `String#gsub`.
24
- # @param replace_string [String] A string to use for replacement in the request host. See `String#gsub`.
25
- # @param replace_block [Proc] A block to use for replacement in the request host. See `String#gsub`.
23
+ # @param replace_pattern [String|Regexp] A optional pattern to manipulate the request host before trying the match. See `String#gsub`.
24
+ # @param replace_string [String] A string to manipulate the request host before trying the match. See `String#gsub`.
25
+ # @param replace_block [Proc] A block to use to manipulate the request host before trying the match. See `String#gsub`.
26
26
  def initialize(domains, replace_pattern = /\.dev$/, replace_string = "", &replace_block)
27
27
  @domains = domains.ensure_array
28
28
  @replace_pattern = replace_pattern
@@ -39,4 +39,4 @@ module Ballast
39
39
  @domains.include?(final_host)
40
40
  end
41
41
  end
42
- end
42
+ end