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
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
|
+
[![Build Status](https://travis-ci.org/arkes/liveqa-ruby.svg?branch=master)](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
|