apes 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rubocop.yml +82 -0
  4. data/.travis-gemfile +15 -0
  5. data/.travis.yml +15 -0
  6. data/.yardopts +1 -0
  7. data/CHANGELOG.md +3 -0
  8. data/Gemfile +22 -0
  9. data/README.md +177 -0
  10. data/Rakefile +44 -0
  11. data/apes.gemspec +34 -0
  12. data/doc/Apes.html +130 -0
  13. data/doc/Apes/Concerns.html +127 -0
  14. data/doc/Apes/Concerns/Errors.html +1089 -0
  15. data/doc/Apes/Concerns/Pagination.html +636 -0
  16. data/doc/Apes/Concerns/Request.html +766 -0
  17. data/doc/Apes/Concerns/Response.html +940 -0
  18. data/doc/Apes/Controller.html +1100 -0
  19. data/doc/Apes/Errors.html +125 -0
  20. data/doc/Apes/Errors/AuthenticationError.html +133 -0
  21. data/doc/Apes/Errors/BadRequestError.html +157 -0
  22. data/doc/Apes/Errors/BaseError.html +320 -0
  23. data/doc/Apes/Errors/InvalidDataError.html +157 -0
  24. data/doc/Apes/Errors/MissingDataError.html +157 -0
  25. data/doc/Apes/Model.html +378 -0
  26. data/doc/Apes/PaginationCursor.html +2138 -0
  27. data/doc/Apes/RuntimeConfiguration.html +909 -0
  28. data/doc/Apes/Serializers.html +125 -0
  29. data/doc/Apes/Serializers/JSON.html +389 -0
  30. data/doc/Apes/Serializers/JWT.html +452 -0
  31. data/doc/Apes/Serializers/List.html +347 -0
  32. data/doc/Apes/UrlsParser.html +1432 -0
  33. data/doc/Apes/Validators.html +125 -0
  34. data/doc/Apes/Validators/BaseValidator.html +278 -0
  35. data/doc/Apes/Validators/BooleanValidator.html +494 -0
  36. data/doc/Apes/Validators/EmailValidator.html +350 -0
  37. data/doc/Apes/Validators/PhoneValidator.html +375 -0
  38. data/doc/Apes/Validators/ReferenceValidator.html +372 -0
  39. data/doc/Apes/Validators/TimestampValidator.html +640 -0
  40. data/doc/Apes/Validators/UuidValidator.html +372 -0
  41. data/doc/Apes/Validators/ZipCodeValidator.html +372 -0
  42. data/doc/Apes/Version.html +189 -0
  43. data/doc/ApplicationController.html +547 -0
  44. data/doc/Concerns.html +128 -0
  45. data/doc/Concerns/ErrorHandling.html +826 -0
  46. data/doc/Concerns/PaginationHandling.html +463 -0
  47. data/doc/Concerns/RequestHandling.html +512 -0
  48. data/doc/Concerns/ResponseHandling.html +579 -0
  49. data/doc/Errors.html +126 -0
  50. data/doc/Errors/AuthenticationError.html +123 -0
  51. data/doc/Errors/BadRequestError.html +147 -0
  52. data/doc/Errors/BaseError.html +289 -0
  53. data/doc/Errors/InvalidDataError.html +147 -0
  54. data/doc/Errors/MissingDataError.html +147 -0
  55. data/doc/Model.html +315 -0
  56. data/doc/PaginationCursor.html +764 -0
  57. data/doc/Serializers.html +126 -0
  58. data/doc/Serializers/JSON.html +253 -0
  59. data/doc/Serializers/JWT.html +253 -0
  60. data/doc/Serializers/List.html +245 -0
  61. data/doc/Validators.html +126 -0
  62. data/doc/Validators/BaseValidator.html +209 -0
  63. data/doc/Validators/BooleanValidator.html +391 -0
  64. data/doc/Validators/EmailValidator.html +298 -0
  65. data/doc/Validators/PhoneValidator.html +313 -0
  66. data/doc/Validators/ReferenceValidator.html +284 -0
  67. data/doc/Validators/TimestampValidator.html +476 -0
  68. data/doc/Validators/UuidValidator.html +310 -0
  69. data/doc/Validators/ZipCodeValidator.html +310 -0
  70. data/doc/_index.html +435 -0
  71. data/doc/class_list.html +58 -0
  72. data/doc/css/common.css +1 -0
  73. data/doc/css/full_list.css +57 -0
  74. data/doc/css/style.css +339 -0
  75. data/doc/file.README.html +252 -0
  76. data/doc/file_list.html +60 -0
  77. data/doc/frames.html +26 -0
  78. data/doc/index.html +252 -0
  79. data/doc/js/app.js +219 -0
  80. data/doc/js/full_list.js +181 -0
  81. data/doc/js/jquery.js +4 -0
  82. data/doc/method_list.html +615 -0
  83. data/doc/top-level-namespace.html +112 -0
  84. data/lib/apes.rb +40 -0
  85. data/lib/apes/concerns/errors.rb +111 -0
  86. data/lib/apes/concerns/pagination.rb +81 -0
  87. data/lib/apes/concerns/request.rb +237 -0
  88. data/lib/apes/concerns/response.rb +74 -0
  89. data/lib/apes/controller.rb +77 -0
  90. data/lib/apes/errors.rb +38 -0
  91. data/lib/apes/model.rb +94 -0
  92. data/lib/apes/pagination_cursor.rb +152 -0
  93. data/lib/apes/runtime_configuration.rb +80 -0
  94. data/lib/apes/serializers.rb +88 -0
  95. data/lib/apes/urls_parser.rb +233 -0
  96. data/lib/apes/validators.rb +234 -0
  97. data/lib/apes/version.rb +24 -0
  98. data/spec/apes/concerns/errors_spec.rb +141 -0
  99. data/spec/apes/concerns/pagination_spec.rb +114 -0
  100. data/spec/apes/concerns/request_spec.rb +244 -0
  101. data/spec/apes/concerns/response_spec.rb +79 -0
  102. data/spec/apes/controller_spec.rb +54 -0
  103. data/spec/apes/errors_spec.rb +14 -0
  104. data/spec/apes/models_spec.rb +148 -0
  105. data/spec/apes/pagination_cursor_spec.rb +113 -0
  106. data/spec/apes/runtime_configuration_spec.rb +100 -0
  107. data/spec/apes/serializers_spec.rb +70 -0
  108. data/spec/apes/urls_parser_spec.rb +150 -0
  109. data/spec/apes/validators_spec.rb +237 -0
  110. data/spec/spec_helper.rb +30 -0
  111. data/views/_included.json.jbuilder +9 -0
  112. data/views/_pagination.json.jbuilder +9 -0
  113. data/views/collection.json.jbuilder +4 -0
  114. data/views/errors/400.json.jbuilder +9 -0
  115. data/views/errors/403.json.jbuilder +7 -0
  116. data/views/errors/404.json.jbuilder +6 -0
  117. data/views/errors/422.json.jbuilder +19 -0
  118. data/views/errors/500.json.jbuilder +12 -0
  119. data/views/errors/501.json.jbuilder +7 -0
  120. data/views/layouts/general.json.jbuilder +36 -0
  121. data/views/object.json.jbuilder +4 -0
  122. metadata +262 -0
@@ -0,0 +1,74 @@
1
+ #
2
+ # This file is part of the apes gem. Copyright (C) 2016 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 Apes
7
+ module Concerns
8
+ # JSON API response handling module.
9
+ module Response
10
+ attr_accessor :included
11
+
12
+ # Returns the template to use to render a object.
13
+ #
14
+ # @param object [Object] The object to render.
15
+ # @return [String] The template to use.
16
+ def response_template_for(object)
17
+ return @object_template if @object_template
18
+ object = object.first if object.respond_to?(:first)
19
+ object.class.name.underscore.gsub("/", "_")
20
+ end
21
+
22
+ # Returns the metadata for the current response.
23
+ #
24
+ # @param default [Object] Fallback data if nothing is found.
25
+ # @return [HashWithIndifferentAccess|Object|Nil] The metadata for the current response.
26
+ def response_meta(default = nil)
27
+ @meta || default || HashWithIndifferentAccess.new
28
+ end
29
+
30
+ # Returns the data for the current response.
31
+ #
32
+ # @param default [Object] Fallback data if nothing is found.
33
+ # @return [HashWithIndifferentAccess|Object|Nil] The data for the current response.
34
+ def response_data(default = nil)
35
+ @data || default || HashWithIndifferentAccess.new
36
+ end
37
+
38
+ # Returns the linked objects for the current response.
39
+ #
40
+ # @param default [Object] Fallback data if nothing is found.
41
+ # @return [HashWithIndifferentAccess|Object|Nil] The linked objects for the current response.
42
+ def response_links(default = nil)
43
+ @links || default || HashWithIndifferentAccess.new
44
+ end
45
+
46
+ # Returns the included (side-loaded) objects for the current response.
47
+ #
48
+ # @param default [Object] Fallback data if nothing is found.
49
+ # @return [HashWithIndifferentAccess|Object|Nil] The included objects for the current response.
50
+ def response_included(default = nil)
51
+ controller.included || default || HashWithIndifferentAccess.new
52
+ end
53
+
54
+ # Adds an object to the included (side-load) set.
55
+ #
56
+ # @param object [Object] The object to include.
57
+ # @param template [String] The template to use for rendering.
58
+ # @return [HashWithIndifferentAccess] A hash of objects to include. Keys are a template:id formatted strings, values are `[object, template]` pairs.
59
+ def response_include(object, template = nil)
60
+ controller.included ||= HashWithIndifferentAccess.new
61
+ controller.included[sprintf("%s:%s", response_template_for(object), object.to_param)] = [object, template]
62
+ controller.included
63
+ end
64
+
65
+ # Serializes a timestamp.
66
+ #
67
+ # @param timestamp [DateTime] The timestamp to serialize.
68
+ # @return [String] The serialized timestamp.
69
+ def response_timestamp(timestamp)
70
+ timestamp.safe_send(:strftime, "%FT%T.%L%z")
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,77 @@
1
+ #
2
+ # This file is part of the apes gem. Copyright (C) 2016 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 Apes
7
+ # A ready to use controller for JSON API applications.
8
+ #
9
+ # @attribute [r] current_account
10
+ # @return [Object] The current account making the request
11
+ # @attribute [r] cursor
12
+ # @return [Apes::PaginationCursor] The pagination cursor for this request.
13
+ # @attribute [r] request_cursor
14
+ # @return [Apes::PaginationCursor] The original pagination cursor sent from the client.
15
+ class Controller < ActionController::API
16
+ include ActionController::ImplicitRender
17
+ include ActionView::Layouts
18
+ include Apes::Concerns::Request
19
+ include Apes::Concerns::Response
20
+ include Apes::Concerns::Pagination
21
+ include Apes::Concerns::Errors
22
+
23
+ layout "general"
24
+ before_filter :request_handle_cors
25
+ before_filter :request_validate
26
+
27
+ attr_reader :current_account, :cursor, :request_cursor
28
+
29
+ # Exception handling
30
+ rescue_from Exception, with: :error_handle_exception
31
+ # This allows to avoid to declare all the views
32
+ rescue_from ActionView::MissingTemplate, with: :render_default_views
33
+
34
+ # Returns the default URL options for this request.
35
+ # It ensures that the host is always included and that is set properly in development mode.
36
+ #
37
+ # @return [Hash] Default URL options for the request.
38
+ def default_url_options
39
+ rv = {only_path: false}
40
+ rv = {host: request_source_host} if Apes::RuntimeConfiguration.development?
41
+ rv
42
+ end
43
+
44
+ # Tiny handle to handle CORS OPTIONS requests. It just renders nothing as headers are handle in Apes::Concerns::Response module.
45
+ #
46
+ # To enable this route, add the following to the routes.rb:
47
+ #
48
+ # # This is to enable AJAX cross domain
49
+ # match '*path', to: 'application#handle_cors', via: :options
50
+ def handle_cors
51
+ render(nothing: true, status: :no_content)
52
+ end
53
+
54
+ # Default handler to render errors.
55
+ #
56
+ # @param status [Symbol|Fixnum] The HTTP error code to return.
57
+ # @param errors [Array] The list of occurred errors.
58
+ def render_error(status, errors)
59
+ @errors = errors
60
+ status_code = status.is_a?(Fixnum) ? status : Rack::Utils::SYMBOL_TO_STATUS_CODE.fetch(status.to_sym, 500)
61
+ render("errors/#{status_code}", status: status)
62
+ end
63
+
64
+ private
65
+
66
+ # :nodoc:
67
+ def render_default_views(exception)
68
+ if defined?(@objects)
69
+ render "/collection"
70
+ elsif defined?(@object)
71
+ render "/object"
72
+ else
73
+ error_handle_exception(exception)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,38 @@
1
+ #
2
+ # This file is part of the apes gem. Copyright (C) 2016 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 Apes
7
+ # Error used by the framework.
8
+ module Errors
9
+ # The base error.
10
+ class BaseError < RuntimeError
11
+ attr_reader :details
12
+
13
+ # Creates a new error.
14
+ #
15
+ # @param details [Object] The details of this error.
16
+ def initialize(details = nil)
17
+ super("")
18
+ @details = details
19
+ end
20
+ end
21
+
22
+ # Error raised when the request is not compliant with JSON API specification.
23
+ class BadRequestError < BaseError
24
+ end
25
+
26
+ # Error raised when the sent data is not valid.
27
+ class InvalidDataError < BaseError
28
+ end
29
+
30
+ # Error raised when the sent data is not missing.
31
+ class MissingDataError < BaseError
32
+ end
33
+
34
+ # Error raised when provided authentication is invalid.
35
+ class AuthenticationError < RuntimeError
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,94 @@
1
+ #
2
+ # This file is part of the apes gem. Copyright (C) 2016 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 Apes
7
+ # Some utility extensions to ActiveModel.
8
+ module Model
9
+ extend ActiveSupport::Concern
10
+
11
+ class_methods do
12
+ # Find a object by using the UUID, a handle or model specific definitions (defined using SECONDARY_KEY or SECONDARY_QUERY constants).
13
+ # Raise exception when nothing is found.
14
+ #
15
+ # @param id [Object] The value to find.
16
+ # @return [Object] The first found model.
17
+ def find_with_any!(id)
18
+ if id =~ Validators::UuidValidator::VALID_REGEX
19
+ find(id)
20
+ elsif defined?(self::SECONDARY_KEY)
21
+ find_by!(self::SECONDARY_KEY => id)
22
+ elsif defined?(self::SECONDARY_QUERY)
23
+ find_by!(self::SECONDARY_QUERY, {id: id})
24
+ else
25
+ find_by!(handle: id)
26
+ end
27
+ end
28
+
29
+ # Find a object by using the UUID, a handle or model specific definitions (defined using SECONDARY_KEY or SECONDARY_QUERY constants).
30
+ #
31
+ # @param id [Object] The value to find.
32
+ # @return [Object] The first found model.
33
+ def find_with_any(id)
34
+ find_with_any!(id)
35
+ rescue ActiveRecord::RecordNotFound
36
+ nil
37
+ end
38
+
39
+ # Performs a search on the model.
40
+ #
41
+ # @param params [Hash] The list of params for the query.
42
+ # @param query [ActiveRecord::Relation|NilClass] A model query to further scope, if any.
43
+ # @param fields [Array] The model fields where to perform search on.
44
+ # @param start_only [Boolean] Whether only match that starts with the searched value rather than just containing it.
45
+ # @param parameter [Symbol|NilClass] The field in `params` which contains the value to search. Will fallback to `params[:filter][:query]` (using `.dig`).
46
+ # @param placeholder [Symbol] The placeholder to use in prepared statement. Useful to avoid collisions. Default is `query`.
47
+ # @param method [Symbol] The operator to use for searching. Everything different from `or` will fallback to `and`.
48
+ # @param case_sensitive [Boolean] Whether to perform case sensitive search. Default is `false`.
49
+ # @return [ActiveRecord::Relation] A query relation object.
50
+ def search(params: {}, query: nil, fields: ["name"], start_only: false, parameter: nil, placeholder: :query, method: :or, case_sensitive: false)
51
+ query ||= where({})
52
+ value = parameter ? params[parameter] : params.dig(:filter, :query)
53
+ return query if value.blank?
54
+
55
+ value = "#{value}%"
56
+ value = "%#{value}" unless start_only
57
+
58
+ method = method.to_s == "or" ? " OR " : " AND "
59
+ operator = case_sensitive ? "LIKE" : "ILIKE"
60
+
61
+ sql = fields.map { |f| "#{f} #{operator} :#{placeholder}" }.join(method)
62
+ query.where(sql, {placeholder => value})
63
+ end
64
+ end
65
+
66
+ # A list of manually managed errors for the model.
67
+ #
68
+ # @return [ActiveModel::Errors] A list of manually managed errors for the model.
69
+ def additional_errors
70
+ @additional_errors ||= ActiveModel::Errors.new(self)
71
+ end
72
+
73
+ # Perform validations on the model and makes sure manually added errors are included.
74
+ def run_validations!
75
+ errors.messages.merge!(additional_errors.messages)
76
+ super
77
+ end
78
+
79
+ # A list of automatically and manually added errors for the model.
80
+ #
81
+ # @return [ActiveModel::Errors] A list of automatically and manually added errors for the model.
82
+ def all_validation_errors
83
+ additional_errors.each do |field, error|
84
+ errors.add(field, error)
85
+ end
86
+
87
+ errors.each do |field|
88
+ errors[field].uniq!
89
+ end
90
+
91
+ errors
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,152 @@
1
+ #
2
+ # This file is part of the apes gem. Copyright (C) 2016 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 Apes
7
+ # A cursor that can be sent to the client, received unmodified and retrieved later to paginate results.
8
+ #
9
+ # @attribute value
10
+ # @return [String] The value obtain from previous pagination. It can either be the value of the first or last element in previous iteration.
11
+ # @attribute use_offset
12
+ # @return [Boolean] Whether to use offset based pagination rather than collection fields values.
13
+ # @attribute direction
14
+ # @return [IO|String] Which page to get in this iteration.
15
+ # @attribute size
16
+ # @return [IO|String] The size of the pagination page.
17
+ class PaginationCursor
18
+ # The default size of a pagination page.
19
+ DEFAULT_SIZE = 25
20
+
21
+ # Format to serialize timestamp when using them for pagination.
22
+ TIMESTAMP_FORMAT = "%FT%T.%6N%z".freeze
23
+
24
+ attr_accessor :value, :use_offset, :direction, :size
25
+
26
+ # Creates a new cursor.
27
+ #
28
+ # @param params [Hash] The request parameters.
29
+ # @param field [Symbol] The parameters field where to lookup for the serialized cursor.
30
+ # @param count_field [Symbol] The parameters field where to lookup for the overriding cursor size.
31
+ # @return [Apes::PaginationCursor] A new cursor instance.
32
+ def initialize(params = {}, field = :page, count_field = :count)
33
+ begin
34
+ payload = JWT.decode(params[field], jwt_secret, true, {algorithm: "HS256", verify_aud: true, aud: "pagination"}).dig(0, "sub")
35
+
36
+ extract_payload(payload)
37
+ rescue
38
+ default_payload
39
+ end
40
+
41
+ # Sanitization
42
+ sanitize(count_field, params)
43
+ end
44
+
45
+ # Get the operator (`>` or `<`) for the query according to the direction and the provided ordering.
46
+ #
47
+ # @param order [Symbol] The order to use.
48
+ # @return [String] The operator to use for the query.
49
+ def operator(order)
50
+ if direction == "next"
51
+ order == :asc ? ">" : "<" # Descending order means newer results first
52
+ else
53
+ order == :asc ? "<" : ">" # Descending order means newer results first
54
+ end
55
+ end
56
+
57
+ # Verifies whether a specific page might exist for the given collection.
58
+ #
59
+ # @param page [String] The page to check. It can be `first`, `next`, `prev` or `previous`.
60
+ # @param collection [Enumerable] The collection to analyze.
61
+ # @return [Boolean] Returns `true` if the page might exist for the collection, `false` otherwise.
62
+ def might_exist?(page, collection)
63
+ case page.to_s
64
+ when "first" then true
65
+ when "next" then collection.present?
66
+ else value.present? && collection.present? # Previous
67
+ end
68
+ end
69
+
70
+ # Serializes the cursor to send it to the client.
71
+ #
72
+ # @param collection [Enumerable] The collection to analyze.
73
+ # @param page [String] The page to return. It can be `first`, `next`, `prev` or `previous`.
74
+ # @param field [Symbol] When not using offset based pagination, the field to consider for generation.
75
+ # @param size [Fixnum] The number of results to advance when using offset based pagination.
76
+ # @param use_offset [Boolean] Whether to use offset based pagination.
77
+ # @return [String] The serialized cursor.
78
+ def save(collection, page, field: :id, size: nil, use_offset: nil)
79
+ size ||= self.size
80
+ use_offset = self.use_offset if use_offset.nil?
81
+ direction, value = use_offset ? update_with_offset(page, size) : update_with_field(page, collection, field)
82
+
83
+ value = value.strftime(TIMESTAMP_FORMAT) if value.respond_to?(:strftime)
84
+
85
+ JWT.encode({aud: "pagination", sub: {value: value, use_offset: use_offset, direction: direction, size: size}}, jwt_secret, "HS256")
86
+ end
87
+ alias_method :serialize, :save
88
+
89
+ private
90
+
91
+ # :nodoc:
92
+ def default_payload
93
+ @value = nil
94
+ @direction = "next"
95
+ @size = 0
96
+ @use_offset = false
97
+ end
98
+
99
+ # :nodoc:
100
+ def extract_payload(payload)
101
+ @value = payload["value"]
102
+ @direction = payload["direction"]
103
+ @size = payload["size"]
104
+ @use_offset = payload["use_offset"]
105
+ end
106
+
107
+ # :nodoc:
108
+ def sanitize(count_field, params)
109
+ @direction = "next" unless ["prev", "previous"].include?(@direction)
110
+ @size = params[count_field].to_integer if params[count_field].present?
111
+ @size = DEFAULT_SIZE if @size < 1
112
+ end
113
+
114
+ # :nodoc:
115
+ def update_with_field(type, collection, field)
116
+ case type.ensure_string
117
+ when "next"
118
+ direction = "next"
119
+ value = collection.last&.send(field)
120
+ when "prev", "previous"
121
+ direction = "previous"
122
+ value = collection.first&.send(field)
123
+ else # first
124
+ direction = "next"
125
+ value = nil
126
+ end
127
+
128
+ [direction, value]
129
+ end
130
+
131
+ # :nodoc:
132
+ def update_with_offset(type, size)
133
+ case type.ensure_string
134
+ when "next"
135
+ direction = "next"
136
+ value = self.value + size
137
+ when "prev", "previous"
138
+ direction = "previous"
139
+ value = [0, self.value - size].max
140
+ else # first
141
+ direction = "next"
142
+ value = nil
143
+ end
144
+
145
+ [direction, value]
146
+ end
147
+
148
+ def jwt_secret
149
+ Apes::RuntimeConfiguration.jwt_token
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,80 @@
1
+ #
2
+ # This file is part of the apes gem. Copyright (C) 2016 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 Apes
7
+ # Internal class to handle runtime configuration.
8
+ class RuntimeConfiguration
9
+ class << self
10
+ # Returns the root directory of apes.
11
+ # @return [String]
12
+ def root
13
+ Pathname.new(Gem.loaded_specs["apes"].full_gem_path).to_s
14
+ end
15
+
16
+ # Returns the current Rails root directory.
17
+ #
18
+ # @param default [String] The fallback if Rails configuration is invalid.
19
+ # @return [String] The current Rails root directory.
20
+ def rails_root(default = nil)
21
+ fetch_with_fallback(default) { Rails.root.to_s }
22
+ end
23
+
24
+ # Returns the current RubyGems root directory.
25
+ #
26
+ # @param default [String] The fallback if RubyGems configuration is invalid.
27
+ # @return [String] The current RubyGems root directory.
28
+ def gems_root(default = nil)
29
+ fetch_with_fallback(default) { Pathname.new(Gem.loaded_specs["lazier"].full_gem_path).parent.to_s }
30
+ end
31
+
32
+ # Returns the current Rails environment.
33
+ #
34
+ # @param default [String] The fallback environment if Rails configuration is invalid.
35
+ # @return [String] The the current Rails environment.
36
+ def environment(default = "development")
37
+ fetch_with_fallback(default) { Rails.env }
38
+ end
39
+
40
+ # Check if Rails is in development environment.
41
+ #
42
+ # @return [Boolean] `true` if Rails is in `development` environment, `false` otherwise.
43
+ def development?
44
+ environment == "development"
45
+ end
46
+
47
+ # Returns the JWT token used by Apes. This should be defined in the Rails secrets.yml file.
48
+ #
49
+ # @param default [String] The fallback if no valid secret is found in Rails secrets file.
50
+ # @return [String] The JWT token used by Apes.
51
+ def jwt_token(default = "secret")
52
+ fetch_with_fallback(default) { Rails.application.secrets.jwt }
53
+ end
54
+
55
+ # Returns the CORS source used by Apes. This should be defined in the Rails secrets.yml file.
56
+ #
57
+ # @param default [String] The fallback if no valid CORS source is found in Rails secrets file.
58
+ # @return [String] The CORS source used by Apes.
59
+ def cors_source(default = "localhost")
60
+ fetch_with_fallback(default) { Rails.application.secrets.cors_source }
61
+ end
62
+
63
+ # Returns a map where keys are tags and values are strftime compliant formats.
64
+ #
65
+ # @param default [String] The fallback if no valid configuration is found in Rails.
66
+ # @return [Hash] A object describing valid timestamps formats.
67
+ def timestamp_formats(default = {})
68
+ fetch_with_fallback(default) { Rails.application.config.timestamp_formats }
69
+ end
70
+
71
+ private
72
+
73
+ def fetch_with_fallback(default)
74
+ yield
75
+ rescue
76
+ default
77
+ end
78
+ end
79
+ end
80
+ end