asana_exception_notifier 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bfdc4b272be9f6414781dafe42e0162f1ffee60e
4
- data.tar.gz: f4f13163c9e1f825764b1d5c08c5934f97e75ded
3
+ metadata.gz: 4347775ca62029a21993e4af21d0ce3ab69fba95
4
+ data.tar.gz: dc0fbe28a472e7b9ed3c32d30f2539b60f3693b2
5
5
  SHA512:
6
- metadata.gz: 0f6f2005e5eebd44234b8f8307e171ef50aebf5f287a7d1055e1b6a7743c9612fd66f59659f6908dc4d6aab0dd86980345c7ac61765787167ebbecec9d9dfabf
7
- data.tar.gz: 2d4d567f75b6aa041c22e6fb7d7e3f9943218bf7b1c8afb11bb1c62e5aedfab82e0dd7871d6817e46eaccd0950d4fbb6bc137d03e07f8389517047e44e2a0b19
6
+ metadata.gz: 8ec4281a81123c92a7065b0d93e3418d3bf7d5f86cfc358db5f062a890212d777a1a42d1297697874aee2c037430bff1151c0226eeca1e65888b8d33b99d78bb
7
+ data.tar.gz: 7e8cb03ef5d3decfee940816af3747c483818c4e2329136c87811beabaccd2a7bff77e0aaf8ab22ef4d456fc5e79257aee71548e05c68e3776d5ebf500f1b31f
data/README.md CHANGED
@@ -179,7 +179,7 @@ Array of projects this task is associated with and the section it is in. At task
179
179
 
180
180
  *Array, optional*
181
181
 
182
- Array of tags associated with this task. This property may be specified on creation using just an array of existing tag IDs. (Default: false).
182
+ Array of tags associated with this task. This property may be specified on creation using just an array of existing tag IDs. (Default: []).
183
183
 
184
184
  ##### name
185
185
 
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
34
34
  s.add_runtime_dependency 'rubyzip', '~> 1.0', '>= 1.0.0' # will load new rubyzip version
35
35
  s.add_runtime_dependency 'zip-zip', '~> 0.3', '>= 0.3' # will load compatibility for old rubyzip API
36
36
  s.add_runtime_dependency 'sys-uname', '~> 1.0', '>= 1.0.2'
37
-
37
+
38
38
  s.add_development_dependency 'appraisal', '~> 2.1', '>= 2.1'
39
39
  s.add_development_dependency 'rspec', '~> 3.4', '>= 3.4'
40
40
  s.add_development_dependency 'simplecov', '~> 0.11', '>= 0.10'
@@ -1,22 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative '../helpers/application_helper'
3
- # class used for connecting to github api and retrieves information about repository
3
+ # class used for connecting to connecting to Asana and creation of task and upload of archives
4
4
  #
5
- # @!attribute callback
6
- # @return [Proc] The callback that is executed after the info is fetched from Github API
5
+ # @!attribute [r] initial_options
6
+ # @return [Hash] THe initial options that the notifier received ( blank values are filtered )
7
+ # @!attribute [r] default_options
8
+ # @return [Hash] The permitted_options that are merged with initial options ( blank values are filtered )
7
9
  module ExceptionNotifier
8
10
  # module that is used for formatting numbers using metrics
9
11
  class AsanaNotifier < ExceptionNotifier::BaseNotifier
10
12
  include AsanaExceptionNotifier::ApplicationHelper
11
- # the base url to which the API will connect for fetching information about gems
12
- attr_reader :initial_options, :default_options
13
13
 
14
+ # The initial options that the middleware was configured with
15
+ # @return [Hash] THe initial options that the notifier received ( blank values are filtered )
16
+ attr_reader :initial_options
17
+
18
+ # The resulting options after merging with permitted_options and with initial_options
19
+ # @return [Hash] The permitted_options that are merged with initial options ( blank values are filtered )
20
+ attr_reader :default_options
21
+
22
+ # Initializes the instance with the options from the configuration and
23
+ # parses the options
24
+ # @see #parse_options
25
+ #
26
+ # @param [options] options The options that can be set in the configuration block
27
+ # @option params [String] :asana_api_key Your Personal Access Token from Asana. You can get it from https://app.asana.com/-/account_api.
28
+ # Please make sure you keep the token secret, and don't commit it in your repository.
29
+ # I suggest to put it into an environment variable and use it from that variable. ( This is REQUIRED )
30
+ # @option params [Integer] :workspace The workspace ID where the task will be created. ( This is REQUIRED )
31
+ # @option params [String, nil] :assignee Who will be assigned by default to the task that is going to be created. (Default: 'me').
32
+ # Can be disabled by setting it to NIL value
33
+ # @option params [String, nil] :assignee_status Scheduling status of this task for the user it is assigned to.
34
+ # This field can only be set if the assignee is non-null. (Default: 'today'). Can be disabled by setting it to NIL value.
35
+ # @option params [Time, nil] :due_at Date and time on which this task is due, or null if the task has no due time.
36
+ # This takes a UTC timestamp and should not be used together with due_on. Default ( Time.now.iso8601)
37
+ # @option params [Time, nil] :due_on Date on which this task is due, or null if the task has no due date.
38
+ # This takes a date with YYYY-MM-DD format and should not be used together with due_at
39
+ # @option params [Boolean, nil] :hearted True if the task is hearted by the authorized user, false if not (Default: false).
40
+ # @option params [Array<String>] :hearts Array of users who will heart the task after creation. (Default: empty Array)
41
+ # @option params [Array<String>] :projects Array of projects this task is associated with.
42
+ # At task creation time, this array can be used to add the task to many projects at once.(Default: empty array).
43
+ # @option params [Array<String>] :followers Array of users following this task. (Default: empty array).
44
+ # @option params [Array<String>] :memberships Array of projects this task is associated with and the section it is in.
45
+ # At task creation time, this array can be used to add the task to specific sections.
46
+ # Note that over time, more types of memberships may be added to this property.(Default: []).
47
+ # @option params [Array<String>] :tags Array of tags associated with this task.
48
+ # This property may be specified on creation using just an array of existing tag IDs. (Default: false).
49
+ # @option params [String] :notes More detailed, free-form textual information associated with the task. (Default: '')
50
+ # @option params [String] :name Name of the task. This is generally a short sentence fragment that fits on a line in the UI for maximum readability.
51
+ # However, it can be longer. (Default: "[AsanaExceptionNotifier] %Exception Class Name%").
52
+ # @option params [String] :template_path This can be used to override the default template when rendering the exception details with customized template.
53
+ # @option params [Array<String>] :unsafe_options This can be used to specify options as strings that will be filtered from session and from request parameters
54
+ # ( The options will not be displayed in the HTML template)
55
+ # @return [void]
14
56
  def initialize(options)
15
57
  super
16
58
  @initial_options = options.symbolize_keys.reject { |_key, value| value.blank? }
17
59
  parse_options(@initial_options)
18
60
  end
19
61
 
62
+ # Returns the asana client that will be used to connect to Asana API and sets the configuration for the client
63
+ # @see #faraday_configuration
64
+ #
65
+ # @return [Asana::Client] Returns the client used for connecting to Asana API's
20
66
  def asana_client
21
67
  @asana_client = Asana::Client.new do |config|
22
68
  config.authentication :access_token, asana_api_key
@@ -26,54 +72,107 @@ module ExceptionNotifier
26
72
  end
27
73
  end
28
74
 
75
+ # Returns the asana client that will be used to connect to Asana API
76
+ # @param [Asana::Configuration] config The configuration object that will be used to set the faraday adapter options for connecting to API's
77
+ #
78
+ # @return [void]
29
79
  def faraday_configuration(config)
30
80
  config.configure_faraday do |conn|
31
- conn.request :url_encoded
81
+ conn.request :url_encoded
32
82
  conn.response :logger
33
83
  end
34
84
  end
35
85
 
86
+ # When a exception is caught , this method will be called to publish to Asana the exception details
87
+ # In order not to block the main thread, while we are parsing the exception, and constructing the template date,
88
+ # and connecting to asana, this will spawn a new thread to ensure that the processing of the exception is deferred
89
+ # from the main thread.
90
+ # This method will also create the asana task after the processing of the exception and all the other data is gathered
91
+ # by the AsanaExceptionNotifier::ErrorPage class
92
+ #
93
+ #
94
+ # @see #ensure_thread_running
95
+ # @see #execute_with_rescue
96
+ # @see AsanaExceptionNotifier::ErrorPage#new
97
+ # @see #create_asana_task
98
+ #
99
+ # @param [Exception] exception The exception that was caught by the middleware
100
+ # @param [Hash] options Additional options that the middleware can send ( Default : {})
101
+ #
102
+ # @return [void]
36
103
  def call(exception, options = {})
37
104
  ensure_thread_running do
38
105
  execute_with_rescue do
39
- EM::HttpRequest.use AsanaExceptionNotifier::Request::Middleware if ENV['DEBUG_ASANA_EXCEPTION_NOTIFIER']
40
106
  error_page = AsanaExceptionNotifier::ErrorPage.new(template_path, exception, options)
41
107
  create_asana_task(error_page) if active?
42
108
  end
43
109
  end
44
110
  end
45
111
 
112
+ # Method that is used to fetch the Asana api key from the default_options
113
+ #
114
+ # @return [String, nil] returns the asana api key if was provided in configuration, or nil otherwise
46
115
  def asana_api_key
47
116
  @default_options.fetch(:asana_api_key, nil)
48
117
  end
49
118
 
119
+ # Method that is used to fetch the workspace ID from the default_options
120
+ #
121
+ # @return [String, nil] returns the workspace ID if was provided in configuration, or nil otherwise
50
122
  def workspace
51
123
  @default_options.fetch(:workspace, nil)
52
124
  end
53
125
 
126
+ # Method that is used to fetch the notes from the default_options
127
+ #
128
+ # @return [String, nil] returns the notes if they were provided in configuration, or nil otherwise
54
129
  def notes
55
130
  @default_options.fetch(:notes, nil)
56
131
  end
57
132
 
133
+ # Method that is used to fetch the task name from the default_options
134
+ #
135
+ # @return [String, nil] returns the task name if was were provided in configuration, or nil otherwise
58
136
  def task_name
59
137
  @default_options.fetch(:name, nil)
60
138
  end
61
139
 
140
+ # Method that is used by the ExceptionNotifier gem to check if this notifier can be activated.
141
+ # The method checks if the asana api key and workspace ID were provided
142
+ #
143
+ # @return [Boolean] returns true if the asana api key and the workspace ID were provided in the configuration, otherwise false
62
144
  def active?
63
145
  asana_api_key.present? && workspace.present?
64
146
  end
65
147
 
148
+ # Method that retrieves the template_path for rendering the exception details
149
+ #
150
+ # @return [String, nil] returns the template_path if was were provided in configuration, or nil otherwise
66
151
  def template_path
67
152
  @default_options.fetch(:template_path, nil)
68
153
  end
69
154
 
70
155
  private
71
156
 
157
+ # Method that parses the options, and rejects keys that are not permitted , and values that are blank
158
+ # @see #permitted_options
159
+ #
160
+ # @param [Hash] options Additional options that are merged in the default options
161
+ #
162
+ # @return [void]
72
163
  def parse_options(options)
73
164
  options = options.reject { |key, _value| !permitted_options.key?(key) }
74
165
  @default_options = permitted_options.merge(options).reject { |_key, value| value.blank? }
75
166
  end
76
167
 
168
+ # Method that tries to render a custom notes template or the default notes template
169
+ # @see #path_is_a_template
170
+ # @see #expanded_path
171
+ # @see AsanaExceptionNotifier::ErrorPage#render_template
172
+ #
173
+ # @param [AsanaExceptionNotifier::ErrorPage] error_page the Erorr page class that is responsible for rendering the exception templates
174
+ #
175
+ # @return [String] The content of the notes templates after being rendered
77
176
  def note_content(error_page)
78
177
  if path_is_a_template?(notes)
79
178
  error_page.render_template(expanded_path(notes))
@@ -82,10 +181,22 @@ module ExceptionNotifier
82
181
  end
83
182
  end
84
183
 
184
+ # Returns the customized task name ( if any provided ) or the default one
185
+ #
186
+ # @param [AsanaExceptionNotifier::ErrorPage] error_page the Erorr page class that is responsible handling exceptions
187
+ #
188
+ # @return [String] The task name that will be used when creating the asana task
85
189
  def task_name_content(error_page)
86
190
  task_name.present? ? task_name : "[AsanaExceptionNotifier] #{error_page.exception_data[:message]}"
87
191
  end
88
192
 
193
+ # Builds all the options needed for creating a asana task
194
+ # @see #task_name_content
195
+ # @see #note_content
196
+ #
197
+ # @param [AsanaExceptionNotifier::ErrorPage] error_page the Erorr page class that is responsible handling exceptions
198
+ #
199
+ # @return [void]
89
200
  def build_request_options(error_page)
90
201
  @default_options.except(:asana_api_key, :template_path).merge(
91
202
  name: task_name_content(error_page),
@@ -94,7 +205,12 @@ module ExceptionNotifier
94
205
  ).symbolize_keys!
95
206
  end
96
207
 
208
+ # Method that is used to create the asana task and upload the log files to the task
209
+ # @see Asana::Resources::Task#create
210
+ # @see #build_request_options
211
+ # @see #upload_log_file_to_task
97
212
  #
213
+ # @param [AsanaExceptionNotifier::ErrorPage] error_page the Erorr page class that is responsible handling exceptions
98
214
  #
99
215
  # @return [void]
100
216
  def create_asana_task(error_page)
@@ -104,6 +220,15 @@ module ExceptionNotifier
104
220
  end
105
221
  end
106
222
 
223
+ # Method that is used to fetch all the needed archives that will be uploaded to the task
224
+ # and upload each of them
225
+ # @see AsanaExceptionNotifier::ErrorPage#fetch_all_archives
226
+ # @see #upload_archive
227
+ #
228
+ # @param [AsanaExceptionNotifier::ErrorPage] error_page the Erorr page class that is responsible handling exceptions
229
+ # @param [Asana::Resources::Task] task the task that was created, and needed to upload archives to the task
230
+ #
231
+ # @return [void]
107
232
  def upload_log_file_to_task(error_page, task)
108
233
  archives = error_page.fetch_all_archives
109
234
  archives.each do |zip|
@@ -111,6 +236,13 @@ module ExceptionNotifier
111
236
  end
112
237
  end
113
238
 
239
+ # Method that is used to upload an archive to a task, The file will be deleted after the upload finishes
240
+ # @see Asana::Resources::Task#attach
241
+ #
242
+ # @param [String] zip the file path to the archive that will be uploaded
243
+ # @param [Asana::Resources::Task] task the task that was created, and needed to upload archives to the task
244
+ #
245
+ # @return [void]
114
246
  def upload_archive(zip, task)
115
247
  return if task.blank?
116
248
  task.attach(
@@ -3,11 +3,39 @@ require_relative '../helpers/application_helper'
3
3
  require_relative './unsafe_filter'
4
4
  module AsanaExceptionNotifier
5
5
  # class used for rendering the template for exception
6
+ #
7
+ # @!attribute [r] template_path
8
+ # @return [Hash] The template_path that will be used to render the exception details
9
+ # @!attribute [r] exception
10
+ # @return [Hash] The exception that will be parsed
11
+ # @!attribute [r] options
12
+ # @return [Hash] Additional options sent by the middleware that will be used to provide additional informatio
13
+ # @!attribute [r] template_details
14
+ # @return [Hash] The name and the extension of the template
15
+ # @!attribute [r] env
16
+ # @return [Hash] The environment that was sent by the middleware or the ENV variable
17
+ # @!attribute [r] request
18
+ # @return [Hash] The request that is built based on the environment, in order to provide more information
19
+ # @!attribute [r] tempfile
20
+ # @return [Hash] The archive that will be created and then splitted into multiple archives (if needed )
21
+ # @!attribute [r] template_params
22
+ # @return [Hash] The template params that will be sent to the template
6
23
  class ErrorPage
7
24
  include AsanaExceptionNotifier::ApplicationHelper
8
25
 
9
- attr_reader :template_path, :exception, :options, :boundary, :template_details, :env, :request, :tempfile, :template_params, :content
26
+ attr_reader :template_path, :exception, :options, :template_details, :env, :request, :tempfile, :template_params
10
27
 
28
+ # Initializes the instance with the template path that will be used to render the template,
29
+ # the exception caught by the middleware and additional options sent by the middleware
30
+ # @see #html_template
31
+ # @see #setup_template_details
32
+ # @see #parse_exception_options
33
+ #
34
+ # @param [String] template_path The template_path that will be used to render the exception details
35
+ # @param [Exception] exception The exception that was caught by the middleware
36
+ # @param [Hash] options Additional options that the middleware can send ( Default : {})
37
+ #
38
+ # @return [void]
11
39
  def initialize(template_path, exception, options)
12
40
  @exception = exception
13
41
  @options = options.symbolize_keys
@@ -19,6 +47,15 @@ module AsanaExceptionNotifier
19
47
  parse_exception_options
20
48
  end
21
49
 
50
+ # Initializes the instance with the template path that will be used to render the template,
51
+ # the exception caught by the middleware and additional options sent by the middleware
52
+ # @see #path_is_a_template
53
+ # @see #expanded_path
54
+ # @see #template_dir
55
+ #
56
+ # @param [String] path The template_path that will be used to render the exception details
57
+ #
58
+ # @return [void]
22
59
  def html_template(path)
23
60
  @template_path = if path_is_a_template?(path)
24
61
  expanded_path(path)
@@ -27,10 +64,18 @@ module AsanaExceptionNotifier
27
64
  end
28
65
  end
29
66
 
67
+ # Returns true or false if ActionDispatch is available
68
+ #
69
+ # @return [Boolean] returns true if ActionDispatch::Request is defined, false otherwise
30
70
  def action_dispatch?
31
71
  defined?(ActionDispatch::Request)
32
72
  end
33
73
 
74
+ # Gets the name and the extension of the template path, if was provided custom
75
+ # ( this is needed in case someone wants something else than ERB template , since Tilt can support multiple formats)
76
+ # @see #get_extension_and_name_from_file
77
+ #
78
+ # @return [Hash] Returns a hash containing the name and the extension of the template
34
79
  def setup_template_details
35
80
  template_extension = @template_path.scan(/\.(\w+)\.?(.*)?/)[0][0]
36
81
  get_extension_and_name_from_file(@template_path).merge(
@@ -39,6 +84,19 @@ module AsanaExceptionNotifier
39
84
  end
40
85
 
41
86
  # :reek:TooManyStatements: { max_statements: 10 }
87
+ #
88
+ # Fetches information about request, exception, environment and other additional information needed for the template
89
+ # @see #fetch_basic_info
90
+ # @see #exception_data
91
+ # @see #setup_env_params
92
+ # @see #filter_params
93
+ # @see #session
94
+ # @see #request_params
95
+ # @see Rack::Request#cookies
96
+ # @see ActionDispatch::Request#filtered_env
97
+ # @see ActionDispatch::Request#filtered_parameters
98
+ #
99
+ # @return [Hash] Returns a hash containing all the information gathered about the exception, including env, cookies, session, and other additional information
42
100
  def parse_exception_options
43
101
  @template_params ||= {
44
102
  basic_info: fetch_basic_info,
@@ -54,10 +112,19 @@ module AsanaExceptionNotifier
54
112
  }.merge(@options).reject { |_key, value| value.blank? }
55
113
  end
56
114
 
115
+ # returns the session from the request, (either from ActionDispatch or from Rack)
116
+ #
117
+ # @return [Hash] Returns the session of the request
57
118
  def session
58
119
  @request.session
59
120
  end
60
121
 
122
+ # returns basic information about the system, like hostname, rails root directory, the process Id, the uname , the timestamp, and the Program name
123
+ # @see Socket#gethostname
124
+ # @see Rails::root
125
+ # @see Sys::Uname#uname
126
+ #
127
+ # @return [Hash] Returns basic information about the system, like hostname, and other additionl information
61
128
  def fetch_basic_info
62
129
  {
63
130
  server: Socket.gethostname,
@@ -69,6 +136,9 @@ module AsanaExceptionNotifier
69
136
  }
70
137
  end
71
138
 
139
+ # returns information about the exception, like the class name, the message, the backtrace, the cause ( if gem 'cause' is used)
140
+ #
141
+ # @return [Hash] Returns information about the exception, like the class name, the message, the backtrace, the cause ( if gem 'cause' is used)
72
142
  def exception_data
73
143
  exception_service.merge(
74
144
  error_class: @exception.class.to_s,
@@ -78,6 +148,9 @@ module AsanaExceptionNotifier
78
148
  )
79
149
  end
80
150
 
151
+ # returns the instance variables defined by the exception, useful when using custom exceptions
152
+ #
153
+ # @return [Hash] Returns information about the instance variables defined by the exception, useful when using custom exceptions
81
154
  def exception_service
82
155
  hash = {}
83
156
  @exception.instance_variables.select do |ivar|
@@ -87,6 +160,9 @@ module AsanaExceptionNotifier
87
160
  hash
88
161
  end
89
162
 
163
+ # returns information about URL, referer, http_method used, ip address and user agent
164
+ #
165
+ # @return [Hash] Returns information about URL, referer, http_method used, ip address and user agent
90
166
  def setup_env_params
91
167
  {
92
168
  url: @request.respond_to?(:original_url) ? @request.original_url : @request.path_info,
@@ -97,25 +173,46 @@ module AsanaExceptionNotifier
97
173
  }
98
174
  end
99
175
 
176
+ # Filters sensitive information from parameters so that they won't get leaked into the template
177
+ # @see AsanaExceptionNotifier::UnsafeFilter#new
178
+ #
179
+ # @return [Hash] Returns the information filtered , by using custom filters or the default one
100
180
  def filter_params(params)
101
181
  AsanaExceptionNotifier::UnsafeFilter.new(params, @options.fetch(:unsafe_options, [])).arguments
102
182
  end
103
183
 
184
+ # returns the params sent with the initial request
185
+ #
186
+ # @return [Hash] Returns the params sent with the initial request
104
187
  def request_params
105
188
  @request.params
106
189
  rescue
107
190
  {}
108
191
  end
109
192
 
193
+ # returns the names that will be used on the table header in the template
194
+ # @see #fieldsets
195
+ # @see #link_helper
196
+ #
197
+ # @return [String] returns the names that will be used on the table header in the template
110
198
  def fieldsets_links
111
199
  fieldsets.map { |key, _value| link_helper(key.to_s) }.join(' | ')
112
200
  end
113
201
 
202
+ # returns fieldsets that will be showned in the template on separate table
203
+ # @see #mount_tables_for_fieldsets
204
+ #
205
+ # @return [Array<Hash>] returns fieldsets that will be showned in the template on separate table
114
206
  def fieldsets
115
207
  @fieldsets ||= mount_tables_for_fieldsets
116
208
  @fieldsets
117
209
  end
118
210
 
211
+ # returns fieldsets that will be showned in the template on separate table
212
+ # @see #fetch_fieldsets
213
+ # @see #mount_table_for_hash
214
+ #
215
+ # @return [Hash] returns the tables that will be used to render on the template as a Hash
119
216
  def mount_tables_for_fieldsets
120
217
  hash = fetch_fieldsets
121
218
  hash.each do |key, value|
@@ -125,6 +222,12 @@ module AsanaExceptionNotifier
125
222
  hash
126
223
  end
127
224
 
225
+ # iterates over the template params and sets the fieldsets that will be will be displayed in tables
226
+ # @see #set_fieldset_key
227
+ #
228
+ # @param [Hash] hash the hash that will contain the data will be displayed in tables
229
+ #
230
+ # @return [void]
128
231
  def build_template_params_hash(hash)
129
232
  @template_params.each_with_parent do |parent, key, value|
130
233
  next if value.blank? || key.blank?
@@ -133,17 +236,33 @@ module AsanaExceptionNotifier
133
236
  end
134
237
  end
135
238
 
239
+ # builds the template params that wil be used to construct the fieldsets and sorts them alphabetically
240
+ # @see #build_template_params_hash
241
+ #
242
+ # @param [Hash] hash the hash that will contain the template params that wil be used to construct the fieldsets sorted alphabetically
243
+ #
244
+ # @return [Hash] returns the hash that will contain the template params that wil be used to construct the fieldsets sorted alphabetically
136
245
  def fetch_fieldsets(hash = {})
137
246
  build_template_params_hash(hash)
138
247
  hash.keys.map(&:to_s).sort
139
248
  hash
140
249
  end
141
250
 
251
+ # adds the fieldsets and the fieldsets_links to the template params
252
+ # @see #fieldsets
253
+ # @see #fieldsets_links
254
+ #
255
+ # @return [void]
142
256
  def setup_template_params_for_rendering
143
257
  @template_params[:fieldsets] = fieldsets
144
258
  @template_params[:fieldsets_links] = fieldsets_links
145
259
  end
146
260
 
261
+ # renders the template or the default template with the template params
262
+ # @see #execute_with_rescue
263
+ # @see #setup_template_params_for_rendering
264
+ #
265
+ # @return [void]
147
266
  def render_template(template = nil)
148
267
  execute_with_rescue do
149
268
  current_template = template.present? ? template : @template_path
@@ -152,6 +271,14 @@ module AsanaExceptionNotifier
152
271
  end
153
272
  end
154
273
 
274
+ # Creates a archive from the render_template outpout and returns the filename and the path of the file
275
+ # @see Tempfile#new
276
+ # @see Tempfile#write
277
+ # @see ObjectSpace#undefine_finalizer
278
+ # @see Tempfile#close
279
+ # @see #tempfile_details
280
+ #
281
+ # @return [Array<String>] returns an array containing the filename as first value, and the path to the tempfile created as second value
155
282
  def create_tempfile(output = render_template)
156
283
  tempfile = Tempfile.new([SecureRandom.uuid, ".#{@template_details[:template_extension]}"], encoding: 'utf-8')
157
284
  tempfile.write(output)
@@ -160,20 +287,35 @@ module AsanaExceptionNotifier
160
287
  tempfile_details(tempfile).slice(:filename, :path).values
161
288
  end
162
289
 
290
+ # Executes the fetch_archives and returns the result or empty array in case of exception
291
+ # @see #fetch_archives
292
+ #
293
+ # @return [Array] returns an array with file paths to the created archives
163
294
  def fetch_all_archives
164
295
  fetch_archives
165
296
  rescue
166
297
  []
167
298
  end
168
299
 
300
+ # Creates the archive, compresses it , and then removes the temporary file and splits the archive if needed
301
+ # @see #create_tempfile
302
+ # @see #archive_files
303
+ # @see #remove_tempfile
304
+ # @see #split_archive
305
+ #
306
+ # @return [Array] returns an array with file paths to the created archives
169
307
  def fetch_archives(output = render_template)
170
308
  return [] if output.blank?
171
309
  filename, path = create_tempfile(output)
172
- archive = compress_files(File.dirname(path), filename, [expanded_path(path)])
310
+ archive = archive_files(File.dirname(path), filename, [expanded_path(path)])
173
311
  remove_tempfile(path)
174
- split_archive(archive, "part_#{filename}", 1024 * 1024 * 100)
312
+ split_archive(archive, "part_#{filename}", 1024 * 1024 * 100) # 104_857_600
175
313
  end
176
314
 
315
+ # If DEBUG_ASANA_TEMPLATE is present this method will only log the path , otherwise will remove the file.
316
+ # @param [String] path The path of the Tempfile that needs to be removed or logged
317
+ #
318
+ # @return [void]
177
319
  def remove_tempfile(path)
178
320
  if ENV['DEBUG_ASANA_TEMPLATE']
179
321
  logger.debug(path)
@@ -2,16 +2,36 @@
2
2
  require_relative '../helpers/application_helper'
3
3
  module AsanaExceptionNotifier
4
4
  # class used to filter unsafe params
5
+ #
6
+ # @!attribute [r] arguments
7
+ # @return [#delete] THe arguments that will be filtered
8
+ # @!attribute [r] unsafe_options
9
+ # @return [Array<String>, Array<Symbol>] Additional unsafe options that will be used for filtering
5
10
  class UnsafeFilter
6
11
  include AsanaExceptionNotifier::ApplicationHelper
7
12
 
13
+ # the default options that are considered unsafe
8
14
  UNSAFE_OPTIONS = %w(
9
15
  password password_confirmation new_password new_password_confirmation
10
16
  old_password email_address email authenticity_token utf8
11
17
  ).freeze
12
18
 
13
- attr_reader :arguments, :unsafe_options
19
+ # The arguments that will be filtered
20
+ # @return [#delete] THe arguments that will be filtered
21
+ attr_reader :arguments
14
22
 
23
+ # Additional unsafe options that will be used for filtering
24
+ # @return [Array<String>, Array<Symbol>] Additional unsafe options that will be used for filtering
25
+ attr_reader :unsafe_options
26
+
27
+ # Initializes the instance with the arguments that will be filtered and the additional unsafe options
28
+ # and starts filtering the arguments
29
+ # @see #remove_unsafe
30
+ #
31
+ # @param [#delete] arguments The arguments that will be filtered
32
+ # @param [Array<String>, Array<Symbol>] unsafe_options Additional unsafe options that will be used for filtering
33
+ #
34
+ # @return [void]
15
35
  def initialize(arguments, unsafe_options = [])
16
36
  @unsafe_options = unsafe_options.present? && unsafe_options.is_a?(Array) ? unsafe_options.map(&:to_s) : []
17
37
  @arguments = arguments.present? ? arguments : {}
@@ -20,6 +40,15 @@ module AsanaExceptionNotifier
20
40
 
21
41
  private
22
42
 
43
+ # Returns the arguments, if they are blank
44
+ # Otherwise first tries to remove attributes
45
+ # then the blank values, and then tries to remove any remaining unsafe from the remaining object
46
+ # @see #remove_blank
47
+ # @see #remove_unsafe_from_object
48
+ #
49
+ # @param [#delete] args The arguments that will be filtered
50
+ #
51
+ # @return [Object, nil]
23
52
  def remove_unsafe(args)
24
53
  return args if args.blank?
25
54
  args.delete(:attributes!)
@@ -28,6 +57,14 @@ module AsanaExceptionNotifier
28
57
  args
29
58
  end
30
59
 
60
+ # If arguments is a hash will try to remove any unsafe values
61
+ # otherwise will call the remove_unsafe to start removing from object
62
+ # @see #verify_unsafe_pair
63
+ # @see #remove_unsafe
64
+ #
65
+ # @param [#delete] args The arguments that will be filtered
66
+ #
67
+ # @return [Object, nil]
31
68
  def remove_unsafe_from_object(args)
32
69
  if args.is_a?(Hash)
33
70
  args.each_pair do |key, value|
@@ -38,10 +75,21 @@ module AsanaExceptionNotifier
38
75
  end
39
76
  end
40
77
 
78
+ # returns true if the key is included in the default unsafe options or in the custom ones, otherwise false
79
+ #
80
+ # @param [String] key The key that will be checked if is unsafe
81
+ #
82
+ # @return [Boolean] returns true if the key is included in the default unsafe options or in the custom ones, otherwise false
41
83
  def unsafe?(key)
42
84
  @unsafe_options.include?(key) || AsanaExceptionNotifier::UnsafeFilter::UNSAFE_OPTIONS.include?(key)
43
85
  end
44
86
 
87
+ # If the value is a hash, we start removing unsafe options from the hash, otherwise we check the key
88
+ # @see #unsafe?
89
+ # @param [String] key The key that will be checked if is unsafe
90
+ # @param [Object] value The value that will be checked if it is unsafe
91
+ #
92
+ # @return [void]
45
93
  def verify_unsafe_pair(key, value)
46
94
  case value
47
95
  when Hash
@@ -8,6 +8,9 @@ module AsanaExceptionNotifier
8
8
 
9
9
  module_function
10
10
 
11
+ # returns the Hash containing as keys the permitted options and as values their default values
12
+ #
13
+ # @return [Hash] Returns the Hash containing as keys the permitted options and as values their default values
11
14
  def permitted_options
12
15
  {
13
16
  asana_api_key: nil,
@@ -29,18 +32,28 @@ module AsanaExceptionNotifier
29
32
  }
30
33
  end
31
34
 
35
+ # returns the expanded path of a file path
36
+ #
37
+ # @param [String] path The file path that will be expanded
38
+ #
39
+ # @return [String] returns the expanded path of a file path
32
40
  def expanded_path(path)
33
41
  File.expand_path(path)
34
42
  end
35
43
 
44
+ # checks to see if a path is valid
45
+ # @see #template_path_exist
46
+ # @param [String] path The file path that will be used
47
+ #
48
+ # @return [Boolean] returns true if the path is valid otherwise false
36
49
  def path_is_a_template?(path)
37
- path.present? && template_path_exist(expanded_path(path))
38
- end
39
-
40
- def multi_request_manager
41
- @multi_manager ||= EventMachine::MultiRequest.new
50
+ path.present? && template_path_exist(path)
42
51
  end
43
52
 
53
+ # method used to extract the body of a IO object
54
+ # @param [IO] io The IO object that will be used
55
+ #
56
+ # @return [String] returns the body of the IO object by rewinding it and reading the content, or executes inspect if a exception happens
44
57
  def extract_body(io)
45
58
  return unless io.respond_to?(:rewind)
46
59
  io.rewind
@@ -49,6 +62,11 @@ module AsanaExceptionNotifier
49
62
  io.inspect
50
63
  end
51
64
 
65
+ # method used to return the file and the path of a tempfile , along with the extension and the name of the file
66
+ # @see #get_extension_and_name_from_file
67
+ # @param [Tempfile] tempfile the temporary file that will be used
68
+ #
69
+ # @return [Hash] returns the the file and the path of a tempfile , along with the extension and the name of the file
52
70
  def tempfile_details(tempfile)
53
71
  file_details = get_extension_and_name_from_file(tempfile)
54
72
  {
@@ -59,39 +77,62 @@ module AsanaExceptionNotifier
59
77
 
60
78
  # Returns utf8 encoding of the msg
61
79
  # @param [String] msg
62
- # @return [String] ReturnsReturns utf8 encoding of the msg
80
+ # @return [String] Returns utf8 encoding of the msg
63
81
  def force_utf8_encoding(msg)
64
82
  msg.respond_to?(:force_encoding) && msg.encoding.name != 'UTF-8' ? msg.force_encoding('UTF-8') : msg
65
83
  end
66
84
 
67
- # returns the logger used to log messages and errors
85
+ # returns the logger used to log messages and errors
68
86
  #
69
87
  # @return [Logger]
70
- #
71
- # @api public
72
88
  def logger
73
89
  @logger ||= (defined?(Rails) && rails_logger.present? ? rails_logger : ExceptionNotifier.logger)
74
90
  @logger = @logger.present? ? @logger : Logger.new(STDOUT)
75
91
  end
76
92
 
93
+ # returns the rails logger
94
+ #
95
+ # @return [Rails::Logger]
77
96
  def rails_logger
78
97
  Rails.logger
79
98
  end
80
99
 
100
+ # returns the newly created thread
101
+ # @see #run_new_thread
102
+ # @see Thread#abort_on_exception
103
+ # @param [Proc] &block the block that the new thread will execute
104
+ #
105
+ # @return [Thread] returns the newly created thread
81
106
  def ensure_thread_running(&block)
82
107
  Thread.abort_on_exception = true
83
108
  run_new_thread(&block)
84
109
  end
85
110
 
111
+ # method used to log exceptions
112
+ # @see #log_bactrace
113
+ # @param [Exception] exception the exception that will be used
114
+ #
115
+ # @return [void]
86
116
  def log_exception(exception)
87
117
  logger.debug exception.inspect
88
118
  log_bactrace(exception) if exception.respond_to?(:backtrace)
89
119
  end
90
120
 
121
+ # method used to log exception backtrace
122
+ # @param [Exception] exception the exception that will be used
123
+ #
124
+ # @return [void]
91
125
  def log_bactrace(exception)
92
126
  logger.debug exception.backtrace.join("\n")
93
127
  end
94
128
 
129
+ # method used to rescue exceptions
130
+ # @see #rescue_interrupt
131
+ # @see #log_exception
132
+ #
133
+ # @param [Hash] options Additional options used for returning values when a exception occurs, or empty string
134
+ #
135
+ # @return [String, nil, Object] Returns nil if the exception is a interrupt or a String empty if no value was provided in the options hash or the value from the options
95
136
  def execute_with_rescue(options = {})
96
137
  yield if block_given?
97
138
  rescue Interrupt
@@ -101,87 +142,165 @@ module AsanaExceptionNotifier
101
142
  options.fetch(:value, '')
102
143
  end
103
144
 
145
+ # method used to rescue from interrupt and show a message
146
+ #
147
+ # @return [void]
104
148
  def rescue_interrupt
105
149
  `stty icanon echo`
106
150
  puts "\n Command was cancelled due to an Interrupt error."
107
151
  end
108
152
 
153
+ # method used to create a thread and execute a block
154
+ # @see Thread#new
155
+ # @return [Thread] returns the newly created thread
109
156
  def run_new_thread
110
157
  Thread.new do
111
158
  yield if block_given?
112
159
  end.join
113
160
  end
114
161
 
162
+ # returns the templates directory
163
+ # @see #root
164
+ # @return [String] returns the path to the templates directory
115
165
  def template_dir
116
166
  File.expand_path(File.join(root, 'templates'))
117
167
  end
118
168
 
169
+ # returns true if file exists or false otherwise
170
+ #
171
+ # @see File#exist?
172
+ #
173
+ # @return [String] returns the path to the templates directory
119
174
  def template_path_exist(path)
120
- File.exist?(path)
175
+ File.exist?(expanded_path(path))
121
176
  end
122
177
 
123
- def get_hash_rows(hash, rows = [], _prefix = '')
178
+ # Method used to construct table rows from a Hash, by constructing an array of arrays with two elements ( First is the key and the value )
179
+ # This is useful for constructing the table, the number of elements in a array means the number of columns of the table
180
+ #
181
+ # This is a recursive function if the Hash contains other Hash values.
182
+ # @see #inspect_value
183
+ #
184
+ # @param [Hash] hash the Hash that wil be used to construct the array of arrays with two columns
185
+ # @param [Array<Array<String>>] rows This is the array that will contain the result ( Default: empty array).
186
+ #
187
+ # @return [Array<Array<String>>] Returns an array of arrays (with two elements), useful for printing tables from a Hash
188
+ def get_hash_rows(hash, rows = [])
124
189
  hash.each do |key, value|
125
190
  if value.is_a?(Hash)
126
- get_object_rows(value, rows)
191
+ get_hash_rows(value, rows)
127
192
  else
128
- rows.push([key.inspect, escape(inspect_value(value).inspect)])
193
+ rows.push([inspect_value(key), inspect_value(value)])
129
194
  end
130
195
  end
131
196
  rows
132
197
  end
133
198
 
199
+ # Method used to inspect a value, by checking if is a IO object, and in that case extract the body from the IO object,
200
+ # otherwise will just use the "inpspect" method. The final result will be escaped so that it can be printed in HTML
201
+ # @see #extract_body
202
+ # @see #escape
203
+ #
204
+ # @param [#inspect, #to_s] value The value that will be inspected and escaped
205
+ #
206
+ # @return [String] Returns the value inspected and escaped
134
207
  def inspect_value(value)
135
- value.is_a?(IO) ? extract_body(value) : value
208
+ inspected_value = value.is_a?(IO) ? extract_body(value) : value.inspect
209
+ escape(inspected_value)
136
210
  end
137
211
 
212
+ # Method used to escape a text by escaping some characters like '&', '<' and '>' , which could affect HTML format
213
+ # @param [#to_s] text The text that will be escaped
214
+ #
215
+ # @return [String] Returns the text HTML escaped
138
216
  def escape(text)
139
- text.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;')
217
+ text.to_s.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;')
140
218
  end
141
219
 
220
+ # Method used to set the prefix name on the links Hash, this is needed when building a table from a hash,
221
+ # because when going through the first level of a Hash, we don't have a title of what this level is about ,
222
+ # but deeper levels can have a title, by using the key to which the value is associated
223
+ #
224
+ # Because of this this method expects a default name, in case the prefix is blank
225
+ # @param [Hash] links The links Hash object that will be used to print the fieldsets links in HTML template
226
+ # @param [String] prefix The prefix that will be set as key on the links Hash and associated with a empty Hash , if the links hash does not have this key yet
227
+ # @param [String] default The default prefix that will be set as key on the links Hash and associated with a empty Hash , if the links hash does not have this key yet and the prefix is blank
228
+ #
229
+ # @return [String] Returns the prefix that was used to set the key on the links Hash, either the 'prefix' variable or the 'default' variable
142
230
  def set_fieldset_key(links, prefix, default)
143
231
  prefix_name = prefix.present? ? prefix : default
144
232
  links[prefix_name] ||= {}
145
233
  prefix_name
146
234
  end
147
235
 
148
- # Mount table for hash, using name and value and adding a name_value class
149
- # to the generated table.
236
+ # This method is used to mount a table from a hash. After the table is mounted, since the generated table has two columns ( Array of array with two elements),
237
+ # We're going to prepend to this generated table a array with two elements (Name and Value) , which will be the columns headers on the generated table .
238
+ # We also will add a HTML class attribute to the generated table ('name_values')
239
+ # @see #get_hash_rows
240
+ # @see #mount_table
150
241
  #
242
+ # @param [Hash] hash The Hash that will be used to mount a table from the keys and values
243
+ # @param [Hash] options Additional options that will be used to set HTML attributes on the generated table
244
+ #
245
+ # @return [String] Returns the HTML table generated as a string, which can be printed anywhere
151
246
  def mount_table_for_hash(hash, options = {})
152
247
  return if hash.blank?
153
248
  rows = get_hash_rows(hash, options.fetch('rows', []))
154
249
  mount_table(rows.unshift(%w(Name Value)), { class: 'name_values' }.merge(options))
155
250
  end
156
251
 
252
+ # This method receives a options list which will be used to construct a string which will be used to set HTML attributes on a HTML element
253
+ #
254
+ # @param [Hash] hash The Hash that will be used to construct the string of HTML attributes
255
+ #
256
+ # @return [String] Returns the string of HTML attributes which can be used on any HTML element
157
257
  def hash_to_html_attributes(hash)
158
258
  hash.map do |key, value|
159
259
  "#{key}=\"#{value.gsub('"', '\"')}\" "
160
260
  end.join(' ')
161
261
  end
162
262
 
263
+ # This method can receive either a Hash or an Array, which will be filtered of blank values
264
+ #
265
+ # @param [Hash, Array] args The Hash or the array which will be used for filtering blank values
266
+ #
267
+ # @return [Hash, Array] Returns the Hash or the array received , filtered of blank values
163
268
  def remove_blank(args)
164
269
  args.delete_if { |_key, value| value.blank? } if args.is_a?(Hash)
165
270
  args.reject!(&:blank?) if args.is_a?(Array)
166
271
  end
167
272
 
273
+ # This method is used to construct the Th header elements that can be used on HTML table from a array, by humanizing and escaping the values
274
+ # @see #escape
275
+ #
276
+ # @param [#map] header the Header array that will be used to construct the Th header elements that can be used on HTML table
277
+ #
278
+ # @return [String] Returns the HTML th elements constructed from the array , that can be used on a HTML table
168
279
  def get_table_headers(header)
169
280
  header.map { |name| escape(name.to_s.humanize) }.join('</th><th>')
170
281
  end
171
282
 
283
+ # This method is to construct a HTML row for each value that exists in the array, each value from the array is a array itself.
284
+ # The row is constructed by joining the values from each array with td element, so the result will be a valid HTML row element
285
+ # The final result is a concatenation of multiple row elements that can be displayed inside a tbody element from a HTML table
286
+ # @param [#map] array The Array that will be used to construct the inner rows of a HTML table
287
+ #
288
+ # @return [String] Returns a concatenation of multiple HTML tr and td elements that are in fact the inner rows of HTML table
172
289
  def get_table_rows(array)
173
290
  array.map { |name| "<tr><td>#{name.join('</td><td>')}</td></tr>" }.join
174
291
  end
175
292
 
176
- # returns the root path of the gem
177
- #
178
- # @return [void]
293
+ # returns the root path of the gem ( the lib directory )
179
294
  #
180
- # @api public
295
+ # @return [String] Returns the root path of the gem ( the lib directory )
181
296
  def root
182
297
  File.expand_path(File.dirname(__dir__))
183
298
  end
184
299
 
300
+ # returns the extension of the file, the filename and the file path of the Tempfile file received as argument
301
+ # @param [Tempfile] tempfile the Tempfile that will be used
302
+ #
303
+ # @return [Hash] returns the extension of the file, the filename and the file path of the Tempfile file received as argument
185
304
  def get_extension_and_name_from_file(tempfile)
186
305
  path = tempfile.respond_to?(:path) ? tempfile.path : tempfile
187
306
  pathname = Pathname.new(path)
@@ -193,6 +312,13 @@ module AsanaExceptionNotifier
193
312
  }
194
313
  end
195
314
 
315
+ # Splits a archive into multiple archives if the size of the archive is greater than the segment_size received as argument
316
+ # and returns a array that contains the paths to each of the archives that were resulted after splitting
317
+ # @param [::Zip::File] archive the archive that will try to be splitted
318
+ # @param [String] partial_name the partial name that will be used when splitting the archives
319
+ # @param [Integer] segment_size the size that will be used for splitting the archive
320
+ #
321
+ # @return [Array<String>] returns a array that contains the paths to each of the archives that were resulted after splitting
196
322
  def split_archive(archive, partial_name, segment_size)
197
323
  indexes = Zip::File.split(archive, segment_size, true, partial_name)
198
324
  archives = Array.new(indexes) do |index|
@@ -201,21 +327,42 @@ module AsanaExceptionNotifier
201
327
  archives.blank? ? [archive] : archives
202
328
  end
203
329
 
204
- def compress_files(directory, name, files)
205
- archive = create_archive(directory, name)
330
+ # This method receives multiple files, that will be added to a archive and will return the resulting archive
331
+ # @see #prepare_archive_creation
332
+ # @see #add_files_to_zip
333
+ # @see Zip::File::open
334
+ #
335
+ # @param [String] directory The directory where the archive will be created
336
+ # @param [String] name The name of the archive ( without the .zip extension )
337
+ # @param [Array<File>] files The Array of files that will be added to the archive
338
+ #
339
+ # @return [Zip::File] returns the archive that was created after each of the files were added to the archive and compressed
340
+ def archive_files(directory, name, files)
341
+ archive = prepare_archive_creation(directory, name)
206
342
  ::Zip::File.open(archive, Zip::File::CREATE) do |zipfile|
207
343
  add_files_to_zip(zipfile, files)
208
344
  end
209
345
  archive
210
346
  end
211
347
 
348
+ # This method receives multiple files, that will be added to a archive
349
+ # @param [::Zip::File] zipfile The archive that will be used to add files to it
350
+ # @param [Array<File>] files The Array of files that will be added to the archive
351
+ #
352
+ # @return [void]
212
353
  def add_files_to_zip(zipfile, files)
213
354
  files.each do |file|
214
355
  zipfile.add(file.sub(File.dirname(file) + '/', ''), file)
215
356
  end
216
357
  end
217
358
 
218
- def create_archive(directory, name)
359
+ # This method prepares the creation of a archive, by making sure that the directory is created and
360
+ # if the archive already exists, will be removed, and the path to where this archive needs to be created will be returned
361
+ # @param [String] directory The directory where the archive should be created
362
+ # @param [String] name The name of the archive ( without the .zip extension )
363
+ #
364
+ # @return [String] returns the path to where this archive needs to be created
365
+ def prepare_archive_creation(directory, name)
219
366
  archive = File.join(directory, name + '.zip')
220
367
  archive_dir = File.dirname(archive)
221
368
  FileUtils.mkdir_p(archive_dir) unless File.directory?(archive_dir)
@@ -4,6 +4,10 @@ module AsanaExceptionNotifier
4
4
  module HeredocHelper
5
5
  module_function
6
6
 
7
+ # This method creates a HTML link , that when will be clicked will trigger the toggle of a fieldset, by either making it hidden or visible
8
+ # @param [String] link The link id of the fieldset that will be toggled when the resulting HTML link will be clicked
9
+ #
10
+ # @return [String] returns HTML link that will be used to toggle between fieldsets
7
11
  def link_helper(link)
8
12
  <<-HTML
9
13
  <a href="javascript:void(0)" onclick="AjaxExceptionNotifier.hideAllAndToggle('#{link.downcase}')">#{link.camelize}</a>
@@ -12,13 +16,16 @@ module AsanaExceptionNotifier
12
16
 
13
17
  # Gets a bidimensional array and create a table.
14
18
  # The first array is used as label.
19
+ # @param [Array<Array<String>>] array The array of arrays of strings that will be used for constructing the HTML table
20
+ # @param [Hash] options The options list that will be used to construct the HTML attributes on the HTML table
15
21
  #
22
+ # @return [String] returns the HTML table that was generated from the received array
16
23
  def mount_table(array, options = {})
17
24
  header = array.extract_options!
18
25
  <<-HTML
19
26
  <table #{hash_to_html_attributes(options)}>
20
- <thead><tr><th>#{get_table_headers(header)}</th></tr></thead>
21
- <tbody>#{get_table_rows(array)}</tbody>
27
+ <thead><tr><th>#{get_table_headers(header)}</th></tr></thead>
28
+ <tbody>#{get_table_rows(array)}</tbody>
22
29
  </table>
23
30
  HTML
24
31
  end
@@ -1,6 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
  # override Hash class
3
3
  class Hash
4
+ # This method is used for iterating over a Hash , but besides yielding the key and the value, this will also yield the parent
5
+ # key of the current Hash object if the Hash is associated to a key
6
+ #
7
+ # @example Printing a Hash that contains other hashes inside, and fetching the parent keys
8
+ # hash = {
9
+ # key: :my_value,
10
+ # key2: {
11
+ # innner_key: :inner_value
12
+ # }
13
+ # }
14
+ # hash.each_with_parent { |parent, key, value| puts [parent, key, value].inspect }
15
+ # will print:
16
+ # [nil, :key, :my_value]
17
+ # [:key2, :innner_key, :inner_value]
18
+ #
19
+ # @see #deep_hash_with_parent
20
+ #
21
+ # @param [String, nil] parent The parent key of the current level of the Hash
22
+ # ( first level of any Hash has no parent, but if the hash has a value which is also a Hash,
23
+ # the parent of that value will be the key associated to the value )
24
+ # @param [Proc] &block The block which will be used to yield the parent string, the current key and the value,
25
+ # while the Hash is being iterated over
26
+ #
27
+ # @return [void]
4
28
  def each_with_parent(parent = nil, &block)
5
29
  each do |key, value|
6
30
  if value.is_a?(Hash)
@@ -11,7 +35,17 @@ class Hash
11
35
  end
12
36
  end
13
37
 
38
+ # Checks if the value is a Hash , and will execute the each with parent for the given hash
39
+ # @see #each_with_parent
40
+ #
41
+ # @param [Hash] value The Hash that will be used for iteration
42
+ # @param [Hash] key The key that will be sent as the parent key of the specified Hash
43
+ # @param [Proc] &block The block which will be used to yield the parent string, the current key and the value,
44
+ # while the Hash is being iterated over
45
+ #
46
+ # @return [void]
14
47
  def deep_hash_with_parent(value, key, &block)
48
+ return unless value.is_a?(Hash)
15
49
  value.each_with_parent(key, &block)
16
50
  end
17
51
  end
@@ -1,15 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
  Zip.setup do |c|
3
+ # By default, rubyzip will not overwrite files if they already exist inside of the extracted path.
4
+ # To change this behavior, you may specify a configuration option like so:
3
5
  c.on_exists_proc = true
6
+ # Additionally, if you want to configure rubyzip to overwrite existing files while creating a .zip file, you can do so with the following:
4
7
  c.continue_on_exists_proc = true
8
+ # If you want to store non-english names and want to open them on Windows(pre 7) you need to set this option: ( We don't want that)
5
9
  c.unicode_names = false
10
+ # Some zip files might have an invalid date format, which will raise a warning. You can hide this warning with the following setting:
11
+ c.warn_invalid_date = false
12
+ # You can set the default compression level like so: Possible values are Zlib::BEST_COMPRESSION, Zlib::DEFAULT_COMPRESSION and Zlib::NO_COMPRESSION
6
13
  c.default_compression = Zlib::BEST_COMPRESSION
14
+ # To save zip archives in sorted order like below, you need to set ::Zip.sort_entries to true
15
+ c.sort_entries = true
7
16
  end
8
17
 
9
- Zip::File.class_eval do
10
- singleton_class.send(:alias_method, :original_get_segment_size_for_split, :get_segment_size_for_split)
11
-
12
- def self.get_segment_size_for_split(segment_size)
13
- segment_size
14
- end
15
- end
18
+ # Zip::File.class_eval do
19
+ # singleton_class.send(:alias_method, :original_get_segment_size_for_split, :get_segment_size_for_split)
20
+ #
21
+ # # This method was overidden from the original method that contained this code
22
+ # # case
23
+ # # when MIN_SEGMENT_SIZE > segment_size
24
+ # # MIN_SEGMENT_SIZE
25
+ # # when MAX_SEGMENT_SIZE < segment_size
26
+ # # MAX_SEGMENT_SIZE
27
+ # # else
28
+ # # segment_size
29
+ # # end
30
+ # # where
31
+ # # MAX_SEGMENT_SIZE = 3_221_225_472 (1024 * 1024 * 1024 * 3)
32
+ # # MIN_SEGMENT_SIZE = 65_536 (1024 * 64)
33
+ # #
34
+ # # Because if you wanted to split a archive using a smaller size than the minimum size, it wouldn't be possible
35
+ # # because will always return the minimum size which is 64 Kb
36
+ # #
37
+ # # @param [Integer] segment_size the size that will be used to split an archive called by the Zip::File.split method
38
+ # #
39
+ # # @return [Integer] returns the size that will be used for splitting an archive
40
+ # def self.get_segment_size_for_split(segment_size)
41
+ # segment_size
42
+ # end
43
+ # end
@@ -16,7 +16,7 @@ module AsanaExceptionNotifier
16
16
  # major release version
17
17
  MAJOR = 0
18
18
  # minor release version
19
- MINOR = 4
19
+ MINOR = 5
20
20
  # tiny release version
21
21
  TINY = 0
22
22
  # prelease version ( set this only if it is a prelease)
@@ -22,6 +22,7 @@ require 'exception_notification'
22
22
  require 'asana'
23
23
  require 'typhoeus'
24
24
  require 'typhoeus/adapters/faraday'
25
+ require 'faraday_middleware'
25
26
 
26
27
  require 'rack'
27
28
  require 'zip'
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module AsanaExceptionNotifier
3
+ # module that defines the available generators for this gem
3
4
  module Generators
4
5
  # module that is used for formatting numbers using metrics
5
6
  class InstallGenerator < Rails::Generators::Base
@@ -7,6 +8,9 @@ module AsanaExceptionNotifier
7
8
 
8
9
  source_root File.expand_path('../templates', __FILE__)
9
10
 
11
+ # This method will copy the template from this gem into the `config/initializers/asana_exception_notifier.rb` file in the application
12
+ #
13
+ # @return [void]
10
14
  def copy_initializer
11
15
  template 'asana_exception_notifier.rb', 'config/initializers/asana_exception_notifier.rb'
12
16
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asana_exception_notifier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bogdanRada
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-08 00:00:00.000000000 Z
11
+ date: 2016-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport