liveqa 1.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d6664a0eec986c9adae1bacead1983dc3d739162
|
4
|
+
data.tar.gz: 7036cb5e2e9b76d770b9ac7a9a9a5902b09aa33c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b83f428273d7e13842210e74ae6676368f57da02a758ad6ef7d905bd391eb110556af77e3b9a59d36490702a5fd03f7f8212d477b1a63b5240223fe59bb5a140
|
7
|
+
data.tar.gz: 4b4424ca15f6043a65fcf1651eb66f9a06b409692a31ba14e284bcff9810635c42234b31eecd40074bf4ca0c8c6e5343a2884448ab2b9ba6122e0c043efac02c
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
Metrics/AbcSize:
|
2
|
+
Max: 24
|
3
|
+
Metrics/LineLength:
|
4
|
+
Max: 120
|
5
|
+
Metrics/ClassLength:
|
6
|
+
CountComments: false
|
7
|
+
Max: 120
|
8
|
+
Metrics/MethodLength:
|
9
|
+
CountComments: false
|
10
|
+
Max: 20
|
11
|
+
Metrics/CyclomaticComplexity:
|
12
|
+
Max: 7
|
13
|
+
|
14
|
+
Layout/EmptyLinesAroundClassBody:
|
15
|
+
Enabled: false
|
16
|
+
Layout/EmptyLinesAroundModuleBody:
|
17
|
+
Enabled: false
|
18
|
+
Layout/EmptyLinesAroundBlockBody:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/FrozenStringLiteralComment:
|
22
|
+
Enabled: false
|
23
|
+
Style/MethodMissing:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Documentation:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Bundler/DuplicatedGem:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
AllCops:
|
33
|
+
TargetRubyVersion: 2.3
|
34
|
+
Exclude:
|
35
|
+
- 'spec/**/*'
|
36
|
+
Exclude:
|
37
|
+
- bin/**/*
|
38
|
+
- vendor/**/*
|
39
|
+
- tmp/**/*
|
40
|
+
- spec/**/*
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.3
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# LiveQA
|
2
|
+
|
3
|
+
[](https://travis-ci.org/arkes/liveqa-ruby)
|
4
|
+
|
5
|
+
LiveQA ruby integration for [LiveQA](https://www.liveqa.io)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
```sh
|
10
|
+
gem install liveqa
|
11
|
+
```
|
12
|
+
|
13
|
+
For Rails in your Gemfile
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'liveqa'
|
17
|
+
```
|
18
|
+
|
19
|
+
## Configuration
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
LiveQA.configure do |config|
|
23
|
+
config.api_key = 'your-api-key'
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
LiveQA.track('my event', {
|
31
|
+
user_id: 42,
|
32
|
+
properties: {
|
33
|
+
order_id: 84
|
34
|
+
}
|
35
|
+
});
|
36
|
+
```
|
37
|
+
|
38
|
+
## Issues
|
39
|
+
|
40
|
+
If you have any issue you can report them on github, or contact support@liveqa.io
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module LiveQA
|
2
|
+
class APIOperation
|
3
|
+
##
|
4
|
+
# == Save (create or update) a resource for the API
|
5
|
+
#
|
6
|
+
module Save
|
7
|
+
module ClassMethods
|
8
|
+
##
|
9
|
+
# Create an API Resource and validate the params for the API.
|
10
|
+
#
|
11
|
+
# @param [Hash] params for the request
|
12
|
+
# @param [Hash] Additional options for the request
|
13
|
+
#
|
14
|
+
# @return [LiveQA::Object] response from the API
|
15
|
+
def create(params = {}, options = {})
|
16
|
+
response = request(:post, resource_path, params, options)
|
17
|
+
initialize_from(response)
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Update an API Resource and validate params for the API
|
22
|
+
#
|
23
|
+
# @param [String] id for the request
|
24
|
+
# @param [Hash] params for the request
|
25
|
+
# @param [Hash] Additional options for the request
|
26
|
+
#
|
27
|
+
# @return [LiveQA::Object] response from the API
|
28
|
+
def update(id, params = {}, options = {})
|
29
|
+
response = request(:put, "#{resource_path}/#{id}", params, options)
|
30
|
+
initialize_from(response)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Create or Update an API Resource
|
37
|
+
#
|
38
|
+
# @param [Hash] params for the request
|
39
|
+
# @param [Hash] Additional options for the request
|
40
|
+
#
|
41
|
+
# @return [LiveQA::Object] response from the API
|
42
|
+
def save(params = {}, options = {})
|
43
|
+
path = singleton_methods.include?(:id) ? "#{resource_path}/#{id}" : resource_path
|
44
|
+
method = singleton_methods.include?(:id) ? :put : :post
|
45
|
+
|
46
|
+
response = request(method, path, to_hash.merge(params), options)
|
47
|
+
update_from(response)
|
48
|
+
end
|
49
|
+
alias update save
|
50
|
+
|
51
|
+
def self.included(base)
|
52
|
+
base.extend(ClassMethods)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module LiveQA
|
2
|
+
##
|
3
|
+
# == LiveQA \API \Resource
|
4
|
+
#
|
5
|
+
# Define the API requests methods
|
6
|
+
class APIResource < LiveQAObject
|
7
|
+
##
|
8
|
+
# @return [String] resource name
|
9
|
+
def resource_name
|
10
|
+
self.class.resource_name
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# @return [String] resource path
|
15
|
+
def resource_path(path = nil)
|
16
|
+
self.class.resource_path(path)
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Send a request to the API
|
21
|
+
#
|
22
|
+
# @param [Symbol] method for the request
|
23
|
+
# @param [String] endpoint for the request
|
24
|
+
# @param [Hash] options
|
25
|
+
# @param [Hash] Payload
|
26
|
+
#
|
27
|
+
# @return [Hash] response
|
28
|
+
# @raise [LiveQA::RequestError] if the request is invalid
|
29
|
+
def request(method, resource_path, payload = {}, options = {})
|
30
|
+
self.class.request(method, resource_path, payload, options)
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
|
35
|
+
##
|
36
|
+
# @return [String] resource path
|
37
|
+
def resource_path(path = nil)
|
38
|
+
"/api/#{configurations.api_version}/#{path || @resource_name || CGI.escape(resource_name)}"
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# @return [String] resource name
|
43
|
+
def resource_name
|
44
|
+
Util.underscore("#{class_name}s")
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# @return [String] the class name
|
49
|
+
def class_name
|
50
|
+
name.split('::')[-1]
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Send a request to the API
|
55
|
+
#
|
56
|
+
# @param [Symbol] method for the request
|
57
|
+
# @param [String] endpoint for the request
|
58
|
+
# @param [Hash] options
|
59
|
+
# @param [Hash] Payload
|
60
|
+
#
|
61
|
+
# @return [Hash] response
|
62
|
+
# @raise [LiveQA::RequestError] if the request is invalid
|
63
|
+
def request(method, path, payload = {}, options = {})
|
64
|
+
payload = Util.deep_obfuscate_value(payload, configurations.obfuscated_fields)
|
65
|
+
url_params = Util.encode_parameters(payload) if method == :get
|
66
|
+
uri = build_endpoint_url(path, url_params)
|
67
|
+
|
68
|
+
request_options = Util.compact(
|
69
|
+
method: method,
|
70
|
+
url: uri.to_s,
|
71
|
+
payload: payload.to_json,
|
72
|
+
proxy: configurations.proxy_url,
|
73
|
+
use_ssl: configurations.http_secure
|
74
|
+
).merge(headers).merge(options)
|
75
|
+
|
76
|
+
Request.execute(request_options)
|
77
|
+
rescue LiveQA::RequestError => error
|
78
|
+
return error.http_body if error.http_status == 422
|
79
|
+
raise
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def build_endpoint_url(path, params = nil)
|
85
|
+
host, port = configurations.api_host.split(':')
|
86
|
+
port = port.to_i if port
|
87
|
+
|
88
|
+
url_params = Util.compact(
|
89
|
+
host: host,
|
90
|
+
path: path,
|
91
|
+
port: port,
|
92
|
+
query: params
|
93
|
+
)
|
94
|
+
|
95
|
+
return URI::HTTPS.build(url_params) if configurations.http_secure
|
96
|
+
URI::HTTP.build(url_params)
|
97
|
+
end
|
98
|
+
|
99
|
+
def ssl_options
|
100
|
+
return {} unless configurations.http_secure
|
101
|
+
{
|
102
|
+
ca_file: File.expand_path(File.dirname(__FILE__) + '/../../vendor/cacert.pem'),
|
103
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def headers
|
108
|
+
{
|
109
|
+
headers: {
|
110
|
+
accept: 'application/json',
|
111
|
+
content_type: 'application/json',
|
112
|
+
x_request_key: configurations.api_key
|
113
|
+
}
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
def configurations
|
118
|
+
LiveQA.configurations
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module LiveQA
|
2
|
+
module AsyncHandlers
|
3
|
+
class Base
|
4
|
+
|
5
|
+
def enqueue(*)
|
6
|
+
raise LiveQA::MissingImplementation, 'Method \'enqueue\' need to be implemented in a subclass'
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute(args)
|
10
|
+
klass_name, method, payload, request_options = args
|
11
|
+
|
12
|
+
Object.const_get(klass_name).send(method.to_sym, payload, request_options)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'sidekiq'
|
2
|
+
|
3
|
+
module LiveQA
|
4
|
+
module AsyncHandlers
|
5
|
+
class Sidekiq < Base
|
6
|
+
include ::Sidekiq::Worker
|
7
|
+
|
8
|
+
OPTIONS = {
|
9
|
+
'queue' => 'liveqa',
|
10
|
+
'class' => self
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
attr_reader :options
|
14
|
+
|
15
|
+
def initialize(options = {})
|
16
|
+
@options = OPTIONS.merge(
|
17
|
+
Util.deep_stringify_key(options)
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def enqueue(*args)
|
22
|
+
::Sidekiq::Client.push(
|
23
|
+
options.merge('args' => args)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def perform(*args)
|
28
|
+
execute(args)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module LiveQA
|
2
|
+
##
|
3
|
+
# == LiveQA \Config
|
4
|
+
#
|
5
|
+
# Represent the LiveQA configuration for the API
|
6
|
+
class Config
|
7
|
+
|
8
|
+
ASYNC_HANDLERS = {
|
9
|
+
sidekiq: {
|
10
|
+
class: 'LiveQA::AsyncHandlers::Sidekiq',
|
11
|
+
require: 'liveqa/async_handlers/sidekiq'
|
12
|
+
}
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
DEFAULT_OBFUSCATED_FIELDS = %w[
|
16
|
+
password
|
17
|
+
password_confirmation
|
18
|
+
secret
|
19
|
+
secret_token
|
20
|
+
authenticity_token
|
21
|
+
token
|
22
|
+
api_key
|
23
|
+
access_token
|
24
|
+
credit_card_number
|
25
|
+
cvv
|
26
|
+
ccv
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
##
|
30
|
+
# @return [String] API key
|
31
|
+
attr_accessor :api_key
|
32
|
+
|
33
|
+
##
|
34
|
+
# @return [String] API host
|
35
|
+
attr_accessor :api_host
|
36
|
+
|
37
|
+
##
|
38
|
+
# @return [String] API version
|
39
|
+
attr_accessor :api_version
|
40
|
+
|
41
|
+
##
|
42
|
+
# @return [String] proxy url
|
43
|
+
attr_accessor :proxy_url
|
44
|
+
|
45
|
+
##
|
46
|
+
# @return [Boolean] http secure
|
47
|
+
attr_accessor :http_secure
|
48
|
+
|
49
|
+
##
|
50
|
+
# @return [Boolean] service is enable
|
51
|
+
attr_accessor :enabled
|
52
|
+
|
53
|
+
##
|
54
|
+
# @return [Array[String]] fields to obfuscate
|
55
|
+
attr_accessor :obfuscated_fields
|
56
|
+
|
57
|
+
##
|
58
|
+
# @return [Null|Symbol|Proc] asynchronous handler
|
59
|
+
attr_accessor :async_handler
|
60
|
+
|
61
|
+
##
|
62
|
+
# @return [Hash] options for asynchronous handler
|
63
|
+
attr_accessor :async_options
|
64
|
+
|
65
|
+
##
|
66
|
+
# @param [Hash{Symbol=>Object}]
|
67
|
+
# Initialize and validate the configuration
|
68
|
+
def initialize(options = {})
|
69
|
+
self.api_key = options[:api_key]
|
70
|
+
self.api_host = options[:api_host] || 'api.liveqa.io'
|
71
|
+
self.api_version = options[:api_version] || 'v1'
|
72
|
+
self.proxy_url = options[:proxy_url]
|
73
|
+
self.http_secure = options[:http_secure] || true
|
74
|
+
self.enabled = options[:enabled] || true
|
75
|
+
self.obfuscated_fields = options[:obfuscated_fields] || []
|
76
|
+
self.async_handler = options[:async_handler]
|
77
|
+
self.async_options = options[:async_options] || {}
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# validate the configuration
|
82
|
+
# Raise when configuration are not valid
|
83
|
+
# @return [Boolean] true
|
84
|
+
def valid!
|
85
|
+
format!
|
86
|
+
|
87
|
+
%i[api_key api_host api_version].each do |field|
|
88
|
+
validate_presence(field)
|
89
|
+
end
|
90
|
+
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
##
|
97
|
+
# Format configuration fields
|
98
|
+
#
|
99
|
+
# * Set obfuscated_fields to string
|
100
|
+
# * Change to the class for async handler
|
101
|
+
#
|
102
|
+
def format!
|
103
|
+
self.obfuscated_fields = (obfuscated_fields.map(&:to_s) + DEFAULT_OBFUSCATED_FIELDS).uniq
|
104
|
+
|
105
|
+
return unless ASYNC_HANDLERS[async_handler]
|
106
|
+
|
107
|
+
require ASYNC_HANDLERS[async_handler][:require]
|
108
|
+
self.async_handler = Object.const_get(ASYNC_HANDLERS[async_handler][:class]).new(async_options)
|
109
|
+
end
|
110
|
+
|
111
|
+
def validate_presence(field)
|
112
|
+
raise LiveQA::ConfigurationError, "#{field} can't be blank" if send(field).nil? || send(field).empty?
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module LiveQA
|
2
|
+
|
3
|
+
##
|
4
|
+
# Configuration Error
|
5
|
+
class ConfigurationError < StandardError; end
|
6
|
+
|
7
|
+
##
|
8
|
+
# Missing Implementation Error
|
9
|
+
class MissingImplementation < StandardError; end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Request to API Error
|
13
|
+
class RequestError < StandardError; end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Method unknown for the request
|
17
|
+
class UnknownRequestMethod < StandardError; end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Request to API Error
|
21
|
+
class RequestError < StandardError
|
22
|
+
|
23
|
+
attr_reader :http_body
|
24
|
+
attr_reader :http_status
|
25
|
+
attr_reader :http_status_type
|
26
|
+
attr_reader :http_message
|
27
|
+
|
28
|
+
def initialize(response, message: nil)
|
29
|
+
@http_status = response.code.to_i
|
30
|
+
@http_status_type = response.code_type
|
31
|
+
@http_body = response.body
|
32
|
+
@http_message = message || response.message
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
"Status #{http_status}: #{http_message}"
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/lib/liveqa/event.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module LiveQA
|
2
|
+
##
|
3
|
+
# == LiveQA \Event
|
4
|
+
#
|
5
|
+
# @example: Usage
|
6
|
+
#
|
7
|
+
# request = LiveQA::Event.create('Event Name') #=> #<LiveQA::Response...>
|
8
|
+
#
|
9
|
+
class Event < APIResource
|
10
|
+
include LiveQA::APIOperation::Save
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def build_payload(payload)
|
15
|
+
Message
|
16
|
+
.to_h
|
17
|
+
.merge(payload)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module LiveQA
|
2
|
+
##
|
3
|
+
# == LiveQA \Object
|
4
|
+
#
|
5
|
+
# Define the API objects
|
6
|
+
class LiveQAObject
|
7
|
+
##
|
8
|
+
# @return [Hash] JSON parsed response
|
9
|
+
attr_reader :raw
|
10
|
+
|
11
|
+
##
|
12
|
+
# @return [Hash] JSON parsed response
|
13
|
+
attr_reader :data
|
14
|
+
|
15
|
+
attr_reader :accepted
|
16
|
+
alias accepted? accepted
|
17
|
+
|
18
|
+
attr_reader :errors
|
19
|
+
|
20
|
+
class << self
|
21
|
+
|
22
|
+
##
|
23
|
+
# Initialize from the API response
|
24
|
+
#
|
25
|
+
# @return [LiveQA::LiveQAObject]
|
26
|
+
def initialize_from(_response = '')
|
27
|
+
object = new
|
28
|
+
|
29
|
+
object.successful = true
|
30
|
+
|
31
|
+
object
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Initialize and create accessor for values
|
38
|
+
#
|
39
|
+
# @params [Hash] values
|
40
|
+
def initialize(values = {})
|
41
|
+
@data = []
|
42
|
+
@values = {}
|
43
|
+
|
44
|
+
update_attributes(values)
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# get attribute value
|
49
|
+
def [](key)
|
50
|
+
@values[key.to_sym]
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# set attribute value
|
55
|
+
def []=(key, value)
|
56
|
+
send(:"#{key}=", value)
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# @return [Array] all the keys
|
61
|
+
def keys
|
62
|
+
@values.keys
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# @return [Hash] values to hash
|
67
|
+
def to_hash
|
68
|
+
@values
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# @return [JSON] values to JSON
|
73
|
+
def to_json
|
74
|
+
JSON.generate(@values)
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Update the attribute and add accessor for new attributes
|
79
|
+
#
|
80
|
+
# @param [Hash] values
|
81
|
+
def update_attributes(attributes)
|
82
|
+
attributes.each do |(key, value)|
|
83
|
+
add_accessor(key, value)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Add data for sub-object
|
89
|
+
#
|
90
|
+
# @param [Object] data
|
91
|
+
def add_data(data)
|
92
|
+
@data << data
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
|
97
|
+
def inspect
|
98
|
+
id_string = respond_to?(:id) && !id.nil? ? " id=#{id}" : ''
|
99
|
+
"#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values)
|
100
|
+
end
|
101
|
+
|
102
|
+
def method_missing(name, *args)
|
103
|
+
super unless name.to_s.end_with?('=')
|
104
|
+
|
105
|
+
attribute = name.to_s[0...-1].to_sym
|
106
|
+
value = args.first
|
107
|
+
|
108
|
+
add_accessor(attribute, value)
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def add_accessors(keys, payload = raw)
|
114
|
+
keys.each do |key|
|
115
|
+
add_accessor(key, payload[key])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_accessor(name, value)
|
120
|
+
@values[name] = value
|
121
|
+
|
122
|
+
define_singleton_method(name) { @values[name] }
|
123
|
+
define_singleton_method(:"#{name}=") do |v|
|
124
|
+
@values[name] = v
|
125
|
+
end
|
126
|
+
|
127
|
+
define_singleton_method(:"#{name}?") { value } if [FalseClass, TrueClass].include?(value.class)
|
128
|
+
end
|
129
|
+
|
130
|
+
def remove_accessor(name)
|
131
|
+
@values.delete(name)
|
132
|
+
|
133
|
+
singleton_class.class_eval { remove_method name.to_sym } if singleton_methods.include?(name.to_sym)
|
134
|
+
singleton_class.class_eval { remove_method "#{name}=".to_sym } if singleton_methods.include?("#{name}=".to_sym)
|
135
|
+
singleton_class.class_eval { remove_method "#{name}?".to_sym } if singleton_methods.include?("#{name}?".to_sym)
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|