liveqa 1.4.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.rubocop.yml +40 -0
- data/.ruby-version +1 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +12 -0
- data/README.md +40 -0
- data/Rakefile +4 -0
- data/lib/liveqa/api_operation/save.rb +57 -0
- data/lib/liveqa/api_resource.rb +123 -0
- data/lib/liveqa/async_handlers/base.rb +17 -0
- data/lib/liveqa/async_handlers/sidekiq.rb +33 -0
- data/lib/liveqa/config.rb +116 -0
- data/lib/liveqa/errors.rb +41 -0
- data/lib/liveqa/event.rb +22 -0
- data/lib/liveqa/library_name.rb +3 -0
- data/lib/liveqa/liveqa_object.rb +139 -0
- data/lib/liveqa/message.rb +50 -0
- data/lib/liveqa/plugins/rack/middleware.rb +121 -0
- data/lib/liveqa/plugins/rails/middleware_data.rb +44 -0
- data/lib/liveqa/plugins/rails/railtie.rb +23 -0
- data/lib/liveqa/plugins/sidekiq/client_middleware.rb +19 -0
- data/lib/liveqa/plugins/sidekiq/load.rb +14 -0
- data/lib/liveqa/plugins/sidekiq/server_middleware.rb +47 -0
- data/lib/liveqa/plugins.rb +16 -0
- data/lib/liveqa/request.rb +132 -0
- data/lib/liveqa/store.rb +49 -0
- data/lib/liveqa/util.rb +148 -0
- data/lib/liveqa/version.rb +3 -0
- data/lib/liveqa.rb +105 -0
- data/liveqa.gemspec +26 -0
- data/spec/lib/liveqa/async_handlers/base_spec.rb +19 -0
- data/spec/lib/liveqa/async_handlers/sidekiq_spec.rb +40 -0
- data/spec/lib/liveqa/config_spec.rb +40 -0
- data/spec/lib/liveqa/event_spec.rb +36 -0
- data/spec/lib/liveqa/liveqa_object_spec.rb +72 -0
- data/spec/lib/liveqa/message_spec.rb +101 -0
- data/spec/lib/liveqa/plugins/rack/middleware_spec.rb +25 -0
- data/spec/lib/liveqa/plugins/rails/middleware_data_spec.rb +67 -0
- data/spec/lib/liveqa/plugins/sidekiq/client_middleware_spec.rb +15 -0
- data/spec/lib/liveqa/plugins/sidekiq/server_middleware_spec.rb +63 -0
- data/spec/lib/liveqa/store_spec.rb +82 -0
- data/spec/lib/liveqa/util_spec.rb +123 -0
- data/spec/lib/liveqa_spec.rb +78 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/rack_app.rb +12 -0
- metadata +176 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module LiveQA
|
2
|
+
##
|
3
|
+
# == LiveQA \Message
|
4
|
+
#
|
5
|
+
# Return the base message for an event
|
6
|
+
class Message
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def to_h
|
10
|
+
Util.deep_compact(
|
11
|
+
tracker_id: tracker_id,
|
12
|
+
message_id: SecureRandom.uuid,
|
13
|
+
timestamp: Time.now.utc.iso8601,
|
14
|
+
session_tracker_id: LiveQA::Store.get(:tracker_id),
|
15
|
+
library: library,
|
16
|
+
server: server,
|
17
|
+
request: LiveQA::Store.get(:request),
|
18
|
+
worker: LiveQA::Store.get(:worker),
|
19
|
+
stack: LiveQA::Store.get(:stack),
|
20
|
+
environement: LiveQA::Store.get(:environement)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def tracker_id
|
27
|
+
return LiveQA::Store.get(:tracker_id) if LiveQA::Store.exist?(:tracker_id)
|
28
|
+
|
29
|
+
LiveQA::Store.set(:tracker_id, SecureRandom.uuid)
|
30
|
+
end
|
31
|
+
|
32
|
+
def library
|
33
|
+
{
|
34
|
+
name: LiveQA::LIBRARY_NAME,
|
35
|
+
language: 'ruby',
|
36
|
+
version: LiveQA::VERSION
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def server
|
41
|
+
{
|
42
|
+
host: Socket.gethostname,
|
43
|
+
pid: Process.pid,
|
44
|
+
software: LiveQA::Store.get(:server_software)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module LiveQA
|
4
|
+
module Plugins
|
5
|
+
module Rack
|
6
|
+
##
|
7
|
+
# LiveQA \Plugins \Rack \Middleware
|
8
|
+
#
|
9
|
+
# Middleware for Rack
|
10
|
+
#
|
11
|
+
class Middleware
|
12
|
+
|
13
|
+
def initialize(app)
|
14
|
+
@app = app
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
request = ::Rack::Request.new(env)
|
19
|
+
|
20
|
+
store_tracker(request)
|
21
|
+
store_request_data(request)
|
22
|
+
store_stack
|
23
|
+
|
24
|
+
LiveQA::Plugins::Rails::MiddlewareData.store_data(request) if defined?(::Rails)
|
25
|
+
|
26
|
+
status, headers, body = @app.call(env)
|
27
|
+
|
28
|
+
write_cookie_tracker_id!(headers)
|
29
|
+
|
30
|
+
[status, headers, body]
|
31
|
+
ensure
|
32
|
+
LiveQA::Store.clear!
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def tracker_id_name
|
38
|
+
'liveqa_tracker_id'.freeze
|
39
|
+
end
|
40
|
+
|
41
|
+
def store_tracker(request)
|
42
|
+
LiveQA::Store.set(
|
43
|
+
:tracker_id,
|
44
|
+
request.cookies[tracker_id_name] || SecureRandom.uuid
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
# rubocop:disable Metrics/MethodLength
|
49
|
+
def store_request_data(request)
|
50
|
+
LiveQA::Store.set(
|
51
|
+
:request,
|
52
|
+
url: obfuscate_uri(request.url),
|
53
|
+
ssl: request.ssl?,
|
54
|
+
host: request.host,
|
55
|
+
port: request.port,
|
56
|
+
path: request.path,
|
57
|
+
referrer: obfuscate_uri(request.referrer),
|
58
|
+
method: request.request_method,
|
59
|
+
xhr: request.xhr?,
|
60
|
+
user_agent: request.user_agent,
|
61
|
+
ip: request.ip,
|
62
|
+
get_params: Util.deep_obfuscate_value(
|
63
|
+
request.GET,
|
64
|
+
LiveQA.configurations.obfuscated_fields
|
65
|
+
),
|
66
|
+
post_params: Util.deep_obfuscate_value(
|
67
|
+
request.POST,
|
68
|
+
LiveQA.configurations.obfuscated_fields
|
69
|
+
)
|
70
|
+
)
|
71
|
+
|
72
|
+
LiveQA::Store.bulk_set(
|
73
|
+
server_software: request.env['SERVER_SOFTWARE']
|
74
|
+
)
|
75
|
+
end
|
76
|
+
# rubocop:enable Metrics/MethodLength
|
77
|
+
|
78
|
+
def store_stack
|
79
|
+
stack = LiveQA::Store.get(:stack) || []
|
80
|
+
|
81
|
+
LiveQA::Store.set(
|
82
|
+
:stack,
|
83
|
+
stack.push(
|
84
|
+
name: 'rack',
|
85
|
+
version: ::Rack.version
|
86
|
+
)
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
def write_cookie_tracker_id!(headers)
|
91
|
+
::Rack::Utils.set_cookie_header!(
|
92
|
+
headers || {},
|
93
|
+
tracker_id_name,
|
94
|
+
value: LiveQA::Store.get(:tracker_id),
|
95
|
+
path: '/'
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
def obfuscate_uri(url)
|
100
|
+
uri = URI.parse(url)
|
101
|
+
|
102
|
+
params =
|
103
|
+
Util.deep_obfuscate_value(
|
104
|
+
::Rack::Utils.parse_query(uri.query),
|
105
|
+
LiveQA.configurations.obfuscated_fields,
|
106
|
+
'HIDDEN'
|
107
|
+
)
|
108
|
+
|
109
|
+
return url if params.blank?
|
110
|
+
|
111
|
+
uri.merge(
|
112
|
+
"?#{::Rack::Utils.build_query(params)}"
|
113
|
+
).to_s
|
114
|
+
rescue
|
115
|
+
''
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module LiveQA
|
2
|
+
module Plugins
|
3
|
+
module Rails
|
4
|
+
class MiddlewareData
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def store_data(request)
|
9
|
+
LiveQA::Store.bulk_set(
|
10
|
+
environement: ::Rails.env
|
11
|
+
)
|
12
|
+
store_request_data(request)
|
13
|
+
store_framework
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def store_request_data(request)
|
19
|
+
object = LiveQA::Store.get(:request) || {}
|
20
|
+
|
21
|
+
LiveQA::Store.set(
|
22
|
+
:request,
|
23
|
+
object.merge(id: request.env['action_dispatch.request_id'])
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def store_framework
|
28
|
+
stack = LiveQA::Store.get(:stack) || []
|
29
|
+
|
30
|
+
LiveQA::Store.set(
|
31
|
+
:stack,
|
32
|
+
stack.push(
|
33
|
+
name: 'rails',
|
34
|
+
version: ::Rails.version
|
35
|
+
)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module LiveQA
|
2
|
+
module Plugins
|
3
|
+
module Rails
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
|
6
|
+
initializer 'liveqa.middleware' do |app|
|
7
|
+
app.config.middleware.use(LiveQA::Plugins::Rack::Middleware)
|
8
|
+
|
9
|
+
if ActiveSupport.const_defined?(:Reloader) && ActiveSupport::Reloader.respond_to?(:to_complete)
|
10
|
+
ActiveSupport::Reloader.to_complete do
|
11
|
+
LiveQA::Store.clear!
|
12
|
+
end
|
13
|
+
elsif ActionDispatch.const_defined?(:Reloader) && ActionDispatch::Reloader.respond_to?(:to_cleanup)
|
14
|
+
ActionDispatch::Reloader.to_cleanup do
|
15
|
+
LiveQA::Store.clear!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module LiveQA
|
2
|
+
module Plugins
|
3
|
+
module Sidekiq
|
4
|
+
##
|
5
|
+
# LiveQA \Plugins \Sidekiq \Client \Middleware
|
6
|
+
#
|
7
|
+
# Client middleware for sidekiq
|
8
|
+
#
|
9
|
+
class ClientMiddleware
|
10
|
+
|
11
|
+
def call(_worker_class, job, _queue, _redis_pool)
|
12
|
+
job['liveqa_store'] = LiveQA::Store.store
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'liveqa/plugins/sidekiq/client_middleware'
|
2
|
+
require 'liveqa/plugins/sidekiq/server_middleware'
|
3
|
+
|
4
|
+
Sidekiq.configure_client do |config|
|
5
|
+
config.client_middleware do |chain|
|
6
|
+
chain.add LiveQA::Plugins::Sidekiq::ClientMiddleware
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
Sidekiq.configure_server do |config|
|
11
|
+
config.server_middleware do |chain|
|
12
|
+
chain.add LiveQA::Plugins::Sidekiq::ServerMiddleware
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module LiveQA
|
2
|
+
module Plugins
|
3
|
+
module Sidekiq
|
4
|
+
##
|
5
|
+
# LiveQA \Plugins \Sidekiq \Server \Middleware
|
6
|
+
#
|
7
|
+
# Server middleware for sidekiq
|
8
|
+
#
|
9
|
+
class ServerMiddleware
|
10
|
+
|
11
|
+
def call(_worker, job, _queue)
|
12
|
+
LiveQA::Store.load_from_hash(job['liveqa_store'])
|
13
|
+
store_worker_data(job)
|
14
|
+
yield
|
15
|
+
ensure
|
16
|
+
LiveQA::Store.clear!
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def store_worker_data(job)
|
22
|
+
LiveQA::Store.set(
|
23
|
+
:worker,
|
24
|
+
name: 'sidekiq',
|
25
|
+
version: ::Sidekiq::VERSION,
|
26
|
+
queue: job['queue'],
|
27
|
+
class: job['class'],
|
28
|
+
id: job['jid'],
|
29
|
+
created_at: convert_to_iso8601(job['created_at']),
|
30
|
+
process_at: convert_to_iso8601(job['enqueued_at']),
|
31
|
+
args: job['args'],
|
32
|
+
retried: job['retry_count'].is_a?(Integer),
|
33
|
+
retry_number: job['retry_count'],
|
34
|
+
failed_at: convert_to_iso8601(job['failed_at'])
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def convert_to_iso8601(int_time)
|
39
|
+
Time.at(int_time).utc.iso8601
|
40
|
+
rescue
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
##
|
2
|
+
# Rack & Rails
|
3
|
+
#
|
4
|
+
if defined?(Rack)
|
5
|
+
require 'liveqa/plugins/rack/middleware'
|
6
|
+
|
7
|
+
if defined?(Rails)
|
8
|
+
require 'liveqa/plugins/rails/railtie'
|
9
|
+
require 'liveqa/plugins/rails/middleware_data'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# Sidekiq
|
15
|
+
#
|
16
|
+
require 'liveqa/plugins/sidekiq/load' if defined?(Sidekiq)
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module LiveQA
|
2
|
+
##
|
3
|
+
# == LiveQA \Request
|
4
|
+
#
|
5
|
+
# Build request
|
6
|
+
class Request
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def execute(params = {})
|
10
|
+
klass = new(params)
|
11
|
+
|
12
|
+
if params[:method] == :post
|
13
|
+
klass.post
|
14
|
+
elsif params[:method] == :put
|
15
|
+
klass.put
|
16
|
+
elsif params[:method] == :delete
|
17
|
+
klass.delete
|
18
|
+
elsif params[:method] == :get
|
19
|
+
klass.get
|
20
|
+
else
|
21
|
+
raise LiveQA::UnknownRequestMethod, "#{params[:method]} haven't been implemented"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def post(params = {})
|
26
|
+
new(params).post
|
27
|
+
end
|
28
|
+
|
29
|
+
def put(params = {})
|
30
|
+
new(params).put
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(params = {})
|
34
|
+
new(params).get
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(params = {})
|
38
|
+
new(params).delete
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :params, :http, :request
|
43
|
+
|
44
|
+
def initialize(params = {})
|
45
|
+
@params = params
|
46
|
+
@http = Net::HTTP.new(uri.host, uri.port, uri_proxy.host, uri_proxy.port)
|
47
|
+
|
48
|
+
setup_ssl if params[:use_ssl]
|
49
|
+
end
|
50
|
+
|
51
|
+
def post
|
52
|
+
@request = Net::HTTP::Post.new(uri.path)
|
53
|
+
|
54
|
+
set_header
|
55
|
+
request.body = params[:payload]
|
56
|
+
|
57
|
+
handle_request
|
58
|
+
end
|
59
|
+
|
60
|
+
def put
|
61
|
+
@request = Net::HTTP::Put.new(uri.path)
|
62
|
+
|
63
|
+
request.body = params[:payload]
|
64
|
+
|
65
|
+
handle_request
|
66
|
+
end
|
67
|
+
|
68
|
+
def get
|
69
|
+
@request = Net::HTTP::Get.new(uri)
|
70
|
+
|
71
|
+
set_header
|
72
|
+
|
73
|
+
handle_request
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete
|
77
|
+
@request = Net::HTTP::Delete.new(uri.path)
|
78
|
+
|
79
|
+
request.body = params[:payload]
|
80
|
+
|
81
|
+
handle_request
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def handle_request
|
87
|
+
response = http.request(request)
|
88
|
+
|
89
|
+
case [response.code_type]
|
90
|
+
when [Net::HTTPOK], [Net::HTTPCreated], [Net::HTTPAccepted]
|
91
|
+
response
|
92
|
+
when [Net::HTTPUnauthorized]
|
93
|
+
raise RequestError.new(response, message: 'Unauthorized, please check your api key')
|
94
|
+
when [Net::HTTPNotFound]
|
95
|
+
raise RequestError.new(response, message: 'Requested Resource not found')
|
96
|
+
when [Net::HTTPRequestTimeOut]
|
97
|
+
raise RequestError.new(response, message: 'Server timeout, verify status of the server')
|
98
|
+
when [Net::HTTPInternalServerError]
|
99
|
+
raise RequestError.new(response, message: 'Server Error, please check with LiveQA')
|
100
|
+
else
|
101
|
+
raise RequestError, response
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def set_header
|
106
|
+
params[:headers].each do |(type, value)|
|
107
|
+
formated_type = type.to_s.split(/_/).map(&:capitalize).join('-')
|
108
|
+
request[formated_type] = value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def setup_ssl
|
113
|
+
http.use_ssl = true
|
114
|
+
http.ca_file = params[:ca_file]
|
115
|
+
http.verify_mode = params[:verify_mode]
|
116
|
+
end
|
117
|
+
|
118
|
+
def uri
|
119
|
+
@uri ||= URI(params[:url])
|
120
|
+
end
|
121
|
+
|
122
|
+
def uri_proxy
|
123
|
+
@uri_proxy ||=
|
124
|
+
if params[:proxy]
|
125
|
+
URI(params[:proxy])
|
126
|
+
else
|
127
|
+
OpenStruct.new
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
data/lib/liveqa/store.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module LiveQA
|
2
|
+
##
|
3
|
+
# == LiveQA \Store
|
4
|
+
#
|
5
|
+
# Store environement data
|
6
|
+
#
|
7
|
+
class Store
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def store
|
11
|
+
Thread.current[:request_store] ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear!
|
15
|
+
Thread.current[:request_store] = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def load_from_hash(object = {})
|
19
|
+
clear!
|
20
|
+
bulk_set(Util.deep_symbolize_key(object)) if object.is_a?(Hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(key)
|
24
|
+
store[key]
|
25
|
+
end
|
26
|
+
alias [] get
|
27
|
+
|
28
|
+
def set(key, value)
|
29
|
+
store[key] = value
|
30
|
+
end
|
31
|
+
alias []= set
|
32
|
+
|
33
|
+
def bulk_set(attributes = {})
|
34
|
+
attributes.each do |(key, value)|
|
35
|
+
set(key, value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def exist?(key)
|
40
|
+
store.key?(key)
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(key, &block)
|
44
|
+
store.delete(key, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/liveqa/util.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
module LiveQA
|
2
|
+
module Util
|
3
|
+
class << self
|
4
|
+
|
5
|
+
DATE_FORMAT = '%Y/%m/%d'.freeze
|
6
|
+
OBFUSCATED = '[HIDDEN]'.freeze
|
7
|
+
|
8
|
+
##
|
9
|
+
# Remove nil value from hash
|
10
|
+
#
|
11
|
+
# @return [Hash]
|
12
|
+
def compact(hash)
|
13
|
+
hash.reject { |_, value| value.nil? }
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Remove nil value from hash recursively
|
18
|
+
#
|
19
|
+
# @return [Hash]
|
20
|
+
def deep_compact(object)
|
21
|
+
object.each_with_object({}) do |(key, value), result|
|
22
|
+
if value.is_a?(Hash)
|
23
|
+
value = deep_compact(value)
|
24
|
+
result[key] = value if !value.nil? && !value.empty?
|
25
|
+
else
|
26
|
+
result[key] = value unless value.nil?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Encodes a hash of parameters in a way that's suitable for use as query
|
33
|
+
# parameters in a URI or as form parameters in a request body. This mainly
|
34
|
+
# involves escaping special characters from parameter keys and values (e.g.
|
35
|
+
# `&`).
|
36
|
+
def encode_parameters(params = {})
|
37
|
+
params.map { |k, v| "#{url_encode(k)}=#{url_encode(v)}" }.join('&')
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Encodes a string in a way that makes it suitable for use in a set of
|
42
|
+
# query parameters in a URI or in a set of form parameters in a request
|
43
|
+
# body.
|
44
|
+
def url_encode(key)
|
45
|
+
CGI.escape(key.to_s).gsub('%5B', '[').gsub('%5D', ']')
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Convert string to underscore
|
50
|
+
#
|
51
|
+
# @param [String]
|
52
|
+
#
|
53
|
+
# @example
|
54
|
+
# underscore('MyModel') => 'my_model'
|
55
|
+
def underscore(string)
|
56
|
+
string
|
57
|
+
.gsub(/::/, '/')
|
58
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
59
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
60
|
+
.tr('-', '_')
|
61
|
+
.downcase
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Deep convert hash to underscore case keys
|
66
|
+
#
|
67
|
+
# @param [Hash] hash to transform
|
68
|
+
#
|
69
|
+
# @return [Hash] transformed
|
70
|
+
def deep_underscore_key(hash_object)
|
71
|
+
deep_transform_keys_in_object(hash_object) do |key|
|
72
|
+
begin
|
73
|
+
underscore(key).to_sym
|
74
|
+
rescue
|
75
|
+
key
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Deep convert hash to string keys
|
82
|
+
#
|
83
|
+
# @param [Hash] hash to transform
|
84
|
+
#
|
85
|
+
# @return [Hash] transformed
|
86
|
+
def deep_stringify_key(hash_object)
|
87
|
+
deep_transform_keys_in_object(hash_object) do |key|
|
88
|
+
begin
|
89
|
+
key.to_s
|
90
|
+
rescue
|
91
|
+
key
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Deep convert hash to symbol keys
|
98
|
+
#
|
99
|
+
# @param [Hash] hash to transform
|
100
|
+
#
|
101
|
+
# @return [Hash] transformed
|
102
|
+
def deep_symbolize_key(hash_object)
|
103
|
+
deep_transform_keys_in_object(hash_object) do |key|
|
104
|
+
begin
|
105
|
+
key.to_sym
|
106
|
+
rescue
|
107
|
+
key
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Deep remove key from hash
|
114
|
+
#
|
115
|
+
# @param [Hash] hash to obfuscate
|
116
|
+
#
|
117
|
+
# @return [Hash] hash obfuscated
|
118
|
+
def deep_obfuscate_value(object, fields, obfuscate_name = OBFUSCATED)
|
119
|
+
case object
|
120
|
+
when Hash
|
121
|
+
object.each_with_object({}) do |(key, value), result|
|
122
|
+
result[key] = fields.include?(key.to_s) ? obfuscate_name : deep_obfuscate_value(value, fields)
|
123
|
+
end
|
124
|
+
when Array
|
125
|
+
object.map { |e| deep_obfuscate_value(e, fields) }
|
126
|
+
else
|
127
|
+
object
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def deep_transform_keys_in_object(object, &block)
|
134
|
+
case object
|
135
|
+
when Hash
|
136
|
+
object.each_with_object({}) do |(key, value), result|
|
137
|
+
result[yield(key)] = deep_transform_keys_in_object(value, &block)
|
138
|
+
end
|
139
|
+
when Array
|
140
|
+
object.map { |e| deep_transform_keys_in_object(e, &block) }
|
141
|
+
else
|
142
|
+
object
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|