hover-ruby-client 0.3.1
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/.github/workflows/ruby.yml +30 -0
- data/.gitignore +64 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +161 -0
- data/README.md +50 -0
- data/Rakefile +14 -0
- data/hover-ruby-client.gemspec +38 -0
- data/lib/hover/client/basic_auth.rb +18 -0
- data/lib/hover/client/hmac.rb +20 -0
- data/lib/hover/client/hover.rb +29 -0
- data/lib/hover/client/http.rb +141 -0
- data/lib/hover/client/manowar.rb +17 -0
- data/lib/hover/client/midas.rb +120 -0
- data/lib/hover/client/static.rb +89 -0
- data/lib/hover/decoder/json_stream.rb +25 -0
- data/lib/hover/encoder/json_stream.rb +35 -0
- data/lib/hover/importer/active_record.rb +134 -0
- data/lib/hover/jobs/concerns/retry_network_errors.rb +45 -0
- data/lib/hover/version.rb +3 -0
- data/test/app/Gemfile +1 -0
- data/test/files/archive.zip +0 -0
- data/test/files/encoded.json +106 -0
- data/test/test_helper.rb +80 -0
- data/test/unit/client/basic_auth_test.rb +21 -0
- data/test/unit/client/hmac_test.rb +277 -0
- data/test/unit/client/hover_test.rb +62 -0
- data/test/unit/client/manowar_test.rb +34 -0
- data/test/unit/client/midas_test.rb +187 -0
- data/test/unit/client/static_test.rb +169 -0
- data/test/unit/decoder/json_stream_test.rb +8 -0
- data/test/unit/encoder/json_stream_test.rb +135 -0
- data/test/unit/importer/active_record_test.rb +354 -0
- data/test/unit/jobs/concerns/retry_network_errors_test.rb +23 -0
- metadata +286 -0
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'api-auth'
|
2
|
+
require 'uri'
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Hover
|
7
|
+
module Client
|
8
|
+
class HTTP
|
9
|
+
attr_accessor :prefix, :site
|
10
|
+
|
11
|
+
def initialize(site = 'http://localhost:3000', prefix = 'api/v1')
|
12
|
+
@prefix = prefix
|
13
|
+
@site = site
|
14
|
+
end
|
15
|
+
|
16
|
+
def authenticate(request)
|
17
|
+
end
|
18
|
+
|
19
|
+
def make_uri(path, parameters = {})
|
20
|
+
parts = [site, prefix, path]
|
21
|
+
uri = URI.parse(parts.map(&:presence).compact.join('/'))
|
22
|
+
uri = URI.parse("#{uri.to_s}?#{URI.encode_www_form(parameters)}") unless parameters.empty?
|
23
|
+
|
24
|
+
uri
|
25
|
+
end
|
26
|
+
|
27
|
+
def make_request(request, uri)
|
28
|
+
authenticate(request)
|
29
|
+
|
30
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
31
|
+
http.use_ssl = true if 'https' == uri.scheme
|
32
|
+
|
33
|
+
response = http.request(request)
|
34
|
+
|
35
|
+
raise_errors_for_cloudfront_response(response)
|
36
|
+
|
37
|
+
response
|
38
|
+
end
|
39
|
+
|
40
|
+
def get(path, parameters = {})
|
41
|
+
uri = make_uri(path, parameters)
|
42
|
+
request = Net::HTTP::Get.new(uri)
|
43
|
+
response = make_request(request, uri)
|
44
|
+
|
45
|
+
response
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_redirect_location(path, parameters = {}, &block)
|
49
|
+
uri = make_uri(path, parameters)
|
50
|
+
request = Net::HTTP::Get.new(uri)
|
51
|
+
redirect_response = make_request(request, uri)
|
52
|
+
|
53
|
+
if redirect_response.is_a?(Net::HTTPRedirection) && redirect_response['location']
|
54
|
+
redirect_response['location']
|
55
|
+
else
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def post(path, parameters = {})
|
61
|
+
uri = make_uri(path)
|
62
|
+
request = Net::HTTP::Post.new(uri)
|
63
|
+
make_form_request(request, uri, parameters)
|
64
|
+
end
|
65
|
+
|
66
|
+
def make_form_request(request, uri, parameters)
|
67
|
+
request.set_form_data(parameters)
|
68
|
+
response = make_request(request, uri)
|
69
|
+
response
|
70
|
+
end
|
71
|
+
|
72
|
+
def put(path, parameters = {})
|
73
|
+
uri = make_uri(path)
|
74
|
+
request = Net::HTTP::Put.new(uri)
|
75
|
+
make_form_request(request, uri, parameters)
|
76
|
+
end
|
77
|
+
|
78
|
+
def patch(path, parameters = {})
|
79
|
+
uri = make_uri(path)
|
80
|
+
request = Net::HTTP::Patch.new(uri)
|
81
|
+
make_form_request(request, uri, parameters)
|
82
|
+
end
|
83
|
+
|
84
|
+
def delete(path, parameters = {})
|
85
|
+
uri = make_uri(path)
|
86
|
+
request = Net::HTTP::Delete.new(uri)
|
87
|
+
make_form_request(request, uri, parameters)
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_response(response)
|
91
|
+
raise_errors(response)
|
92
|
+
|
93
|
+
case response.code.to_i
|
94
|
+
when 204 # no content
|
95
|
+
{}
|
96
|
+
else
|
97
|
+
::JSON.parse(response.body)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def raise_errors_for_cloudfront_response(response)
|
102
|
+
#
|
103
|
+
# If the server can't be reached cloudfront will respond with an
|
104
|
+
# error message of it's own. We need to catch those so that we can retry
|
105
|
+
# the requests.
|
106
|
+
#
|
107
|
+
|
108
|
+
return unless (response.header['Server'] =~ /Cloudfront/i)
|
109
|
+
|
110
|
+
raise ::Hover::Client::Unavailable
|
111
|
+
end
|
112
|
+
|
113
|
+
def raise_errors(response)
|
114
|
+
message = "(#{response.code}): - #{response.body}"
|
115
|
+
|
116
|
+
case response.code.to_i
|
117
|
+
when 400
|
118
|
+
raise BadRequest, message
|
119
|
+
when 401
|
120
|
+
raise Unauthorized, message
|
121
|
+
when 403
|
122
|
+
raise General, message
|
123
|
+
when 404
|
124
|
+
raise NotFound, message
|
125
|
+
when 500
|
126
|
+
raise InternalError, "Internal error: #{message}"
|
127
|
+
when 502..503
|
128
|
+
raise Unavailable, message
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class BadRequest < StandardError; end
|
134
|
+
class Unauthorized < StandardError; end
|
135
|
+
class General < StandardError; end
|
136
|
+
class Unavailable < StandardError; end
|
137
|
+
class InternalError < StandardError; end
|
138
|
+
class NotFound < StandardError; end
|
139
|
+
class AloomaReportingError < StandardError; end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'hover/client/hover'
|
2
|
+
|
3
|
+
module Hover
|
4
|
+
module Client
|
5
|
+
class Manowar < ::Hover::Client::Hover
|
6
|
+
|
7
|
+
def submit_order(parameters = {})
|
8
|
+
json_post('orders.json', parameters)
|
9
|
+
end
|
10
|
+
|
11
|
+
def order_results(order_id)
|
12
|
+
json_get("orders/#{order_id}/results.json")
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'zip'
|
4
|
+
|
5
|
+
require 'hover/client/hover'
|
6
|
+
|
7
|
+
module Hover
|
8
|
+
module Client
|
9
|
+
class Midas < ::Hover::Client::Hover
|
10
|
+
|
11
|
+
#
|
12
|
+
# Users
|
13
|
+
#
|
14
|
+
|
15
|
+
def me
|
16
|
+
json_get('me.json')
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Orders
|
21
|
+
#
|
22
|
+
|
23
|
+
def orders(parameters = {})
|
24
|
+
json_get('orders.json', parameters)
|
25
|
+
end
|
26
|
+
|
27
|
+
def order(id, parameters = {})
|
28
|
+
json_get("orders/#{id}.json", parameters)
|
29
|
+
end
|
30
|
+
|
31
|
+
def order_complete_work(order_id)
|
32
|
+
put("orders/#{order_id}/work_complete.json")
|
33
|
+
end
|
34
|
+
|
35
|
+
#def update_order(id, order_attributes)
|
36
|
+
# put("orders/#{id}.json", order_parameters(order_attributes))
|
37
|
+
#end
|
38
|
+
|
39
|
+
def create_order(params = {})
|
40
|
+
json_post('orders.json', params)
|
41
|
+
end
|
42
|
+
|
43
|
+
def complete_order_upload(order_id)
|
44
|
+
patch("orders/#{order_id}/archive_uploading_complete.json")
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Images
|
49
|
+
#
|
50
|
+
|
51
|
+
def images(parameters = {})
|
52
|
+
json_get("images.json", parameters)
|
53
|
+
end
|
54
|
+
|
55
|
+
def image(id)
|
56
|
+
json_get("images/#{id}.json")
|
57
|
+
end
|
58
|
+
|
59
|
+
def download_image(image, parameters = {}, &block)
|
60
|
+
return unless url = get_redirect_location("images/#{image["id"]}.jpg", parameters)
|
61
|
+
|
62
|
+
Tempfile.open(["image-download", ".jpg"]) do |output|
|
63
|
+
output.write(Kernel.open(url).read)
|
64
|
+
output.rewind
|
65
|
+
|
66
|
+
yield output
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def upload_image(image, io)
|
71
|
+
raise "Already Uploaded" unless upload_url = image["image"].try(:[], "upload_url")
|
72
|
+
|
73
|
+
file = (io.is_a?(String) ? File.open(io) : io)
|
74
|
+
|
75
|
+
uri = URI.parse(upload_url)
|
76
|
+
|
77
|
+
request = Net::HTTP::Put.new(uri.request_uri)
|
78
|
+
request.body_stream = file
|
79
|
+
request.content_length = file.size
|
80
|
+
request['Content-Type'] = ''
|
81
|
+
|
82
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
83
|
+
http.use_ssl = true
|
84
|
+
response = http.request(request)
|
85
|
+
end
|
86
|
+
|
87
|
+
def image_upload_urls(order_id)
|
88
|
+
json_get("images/upload_urls.json", order_id: order_id)
|
89
|
+
end
|
90
|
+
|
91
|
+
def upload_images_for_order(order_id, input_path)
|
92
|
+
image_array = image_upload_urls(order_id).map { |upload_url| {"image" => {"upload_url" => upload_url}} }
|
93
|
+
file_paths = Dir.glob(File.join(input_path, "*.jp*"))
|
94
|
+
|
95
|
+
if file_paths.size > image_array.size
|
96
|
+
raise("Trying to upload #{file_paths.size} files. #{image_array.size} is the maximum.")
|
97
|
+
end
|
98
|
+
|
99
|
+
file_paths.each do |file_path|
|
100
|
+
image = image_array.pop
|
101
|
+
upload_image(image, file_path)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def upload_images_from_prometheus_export(order_id, export_zip_path)
|
106
|
+
Zip::File.open(export_zip_path) do |zip|
|
107
|
+
Dir.mktmpdir do |directory_path|
|
108
|
+
zip.entries.select { |entry| entry.name =~ /original_image\.jp(g|eg)$/ }.each do |entry|
|
109
|
+
basename = File.basename(entry.name)
|
110
|
+
next if basename =~ /^\./
|
111
|
+
entry.extract(File.join(directory_path, basename)) { true }
|
112
|
+
end
|
113
|
+
|
114
|
+
upload_images_for_order(order_id, directory_path)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'hover/client/hover'
|
2
|
+
|
3
|
+
module Hover
|
4
|
+
module Client
|
5
|
+
class Static < ::Hover::Client::Hover
|
6
|
+
|
7
|
+
attr_accessor :application, :environment
|
8
|
+
|
9
|
+
def initialize(application, environment, client_id, client_secret, site = 'http://localhost:3000', prefix = 'api/v1')
|
10
|
+
super(client_id, client_secret, site, prefix)
|
11
|
+
|
12
|
+
self.application = application
|
13
|
+
self.environment = environment
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_metric(name:, value:, happened_at: Time.now.utc, tags: {}, remote_record_id: nil)
|
17
|
+
return unless alooma_url
|
18
|
+
|
19
|
+
parameters = {
|
20
|
+
"metric[name]" => name,
|
21
|
+
'metric[value]' => value,
|
22
|
+
'metric[happened_at]' => happened_at.to_s,
|
23
|
+
'metric[environment]' => environment,
|
24
|
+
'metric[application]' => application
|
25
|
+
}.merge(self.class.tags_to_params(tags))
|
26
|
+
parameters['metric[remote_record_id]'] = remote_record_id if remote_record_id
|
27
|
+
add_unique_identifier(parameters)
|
28
|
+
|
29
|
+
post_to_alooma(parameters, alooma_url)
|
30
|
+
end
|
31
|
+
|
32
|
+
def alooma_url
|
33
|
+
@alooma_url ||= if Rails.env.staging?
|
34
|
+
"https://inputs.alooma.com/rest/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnROYW1lIjoiaG92ZXItc2YtMSIsImlucHV0TGFiZWwiOiJzdGF0aWNfc3RhZ2luZyIsImlucHV0VHlwZSI6IlJFU1RBUEkifQ.Iba3VM9GaOjzJEmUFPuHDA6oaQT7TBe9A0iMxfYW0x0"
|
35
|
+
elsif Rails.env.production?
|
36
|
+
"https://inputs.alooma.com/rest/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnROYW1lIjoiaG92ZXItc2YtMSIsImlucHV0TGFiZWwiOiJzdGF0aWNfcHJvZHVjdGlvbiIsImlucHV0VHlwZSI6IlJFU1RBUEkifQ.jiuWVLDPDvZSv4bmiy54GVEpOre19bDqr9ZC7Y-HKwo"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def post_to_alooma(parameters, url)
|
41
|
+
uri = URI.parse(url)
|
42
|
+
request = Net::HTTP::Post.new(uri)
|
43
|
+
request.set_form_data(parameters)
|
44
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
45
|
+
http.use_ssl = true if 'https' == uri.scheme
|
46
|
+
response = http.request(request)
|
47
|
+
|
48
|
+
unless (200 .. 299).include?(response.code.to_i)
|
49
|
+
raise AloomaReportingError.new("Error reporting to alooma. #{response.inspect}")
|
50
|
+
end
|
51
|
+
|
52
|
+
response
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_unique_identifier(parameters)
|
56
|
+
sorted_array = parameters.to_a.sort_by(&:first)
|
57
|
+
metric_identifier = Digest::SHA256.hexdigest(sorted_array.to_json)
|
58
|
+
|
59
|
+
parameters['metric[identifier]'] = metric_identifier
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Class Methods
|
64
|
+
#
|
65
|
+
|
66
|
+
def self.tags_to_params(tags, parent_keys = ['metric', 'tags'])
|
67
|
+
params = {}
|
68
|
+
|
69
|
+
tags.each do |key, value|
|
70
|
+
if value.is_a?(Hash)
|
71
|
+
params.merge!(tags_to_params(value, (parent_keys + [key])))
|
72
|
+
elsif value.is_a?(Array)
|
73
|
+
params["#{param_name(*parent_keys, key)}[]"] = value
|
74
|
+
else
|
75
|
+
params[param_name(*parent_keys, key)] = value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
params
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.param_name(*args)
|
83
|
+
first = args.shift
|
84
|
+
"#{first}" + args.map{ |arg| "[#{arg}]" }.join
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'yajl'
|
2
|
+
|
3
|
+
module Hover
|
4
|
+
module Decoder
|
5
|
+
class JSONStream
|
6
|
+
|
7
|
+
def initialize(s3_object, block = ->(object) {})
|
8
|
+
@parser = Yajl::Parser.new
|
9
|
+
@s3_object = s3_object
|
10
|
+
|
11
|
+
@parser.on_parse_complete = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
bucket_name = @s3_object.bucket.name
|
16
|
+
key = @s3_object.key
|
17
|
+
|
18
|
+
@s3_object.client.get_object(bucket: bucket_name, key: key) do |chunk|
|
19
|
+
@parser << chunk
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Hover
|
2
|
+
module Encoder
|
3
|
+
class JSONStream
|
4
|
+
attr_accessor :file
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
end
|
8
|
+
|
9
|
+
def open(path)
|
10
|
+
self.file = File.open(path, 'w')
|
11
|
+
end
|
12
|
+
|
13
|
+
def close
|
14
|
+
self.file.close
|
15
|
+
self.file = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def append(attributes, klass)
|
19
|
+
self.file.puts(JSON.dump(attributes.merge('class' => klass.name)))
|
20
|
+
end
|
21
|
+
|
22
|
+
def append_each(attributes_sets, klass)
|
23
|
+
attributes_sets.each do |attributes|
|
24
|
+
append(attributes, klass)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def append_records(scope)
|
29
|
+
scope.find_each do |record|
|
30
|
+
append(record.attributes.as_json, record.class)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Hover
|
2
|
+
module Importer
|
3
|
+
class ActiveRecord
|
4
|
+
attr_accessor :remote_local_map, :update_finder, :column_changes
|
5
|
+
|
6
|
+
def initialize(remote_local_map, update_finder = nil, column_changes = {})
|
7
|
+
self.remote_local_map = Hash.new { |hash, key| hash[key] = {} }
|
8
|
+
self.remote_local_map.merge!(remote_local_map)
|
9
|
+
self.update_finder = update_finder
|
10
|
+
self.column_changes = column_changes
|
11
|
+
end
|
12
|
+
|
13
|
+
def change_columns(attributes)
|
14
|
+
attributes.inject({}) do |hash, key_value|
|
15
|
+
hash[(column_changes[key_value[0]] || key_value[0])] = key_value[1]
|
16
|
+
hash
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def update(table_name, records_attributes)
|
21
|
+
klass = table_name.singularize.camelize.constantize
|
22
|
+
records_attributes.collect { |attributes| update_record(klass, attributes) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_record(klass, attributes)
|
26
|
+
return unless record = find_record_to_update(klass, attributes)
|
27
|
+
|
28
|
+
attributes = change_columns(attributes.dup)
|
29
|
+
s3_file_attributes = s3_file_keys(klass, attributes)
|
30
|
+
|
31
|
+
attributes.delete(klass.primary_key)
|
32
|
+
attributes.reject! { |key, value| !klass.column_names.include?(key) }
|
33
|
+
attributes.merge!(s3_file_attributes) if s3_file_attributes.is_a?(Hash)
|
34
|
+
|
35
|
+
record.assign_attributes(attributes)
|
36
|
+
record.save!(validate: false)
|
37
|
+
|
38
|
+
[klass, record.id]
|
39
|
+
end
|
40
|
+
|
41
|
+
def s3_file_keys(klass, attributes)
|
42
|
+
klass.try(:s3_file_versions).try(:keys).try(:inject, {}) do |output, attribute|
|
43
|
+
if value = attributes.delete(attribute.to_s)
|
44
|
+
output["#{attribute}_s3_keys"] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
output
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_record_to_update(klass, attributes)
|
52
|
+
self.update_finder.call(self, klass, attributes)
|
53
|
+
end
|
54
|
+
|
55
|
+
def import_results(results)
|
56
|
+
class_id_sets = []
|
57
|
+
|
58
|
+
results.each do |table_name, records_attributes|
|
59
|
+
if table_class_id_sets = import_table(table_name, records_attributes)
|
60
|
+
class_id_sets.concat(table_class_id_sets)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class_id_sets
|
65
|
+
end
|
66
|
+
|
67
|
+
def import_table(table_name, records_attributes)
|
68
|
+
klass = table_name.singularize.camelize.constantize
|
69
|
+
|
70
|
+
records_attributes.collect { |attributes|
|
71
|
+
import_record(klass, attributes) unless attributes.nil?
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def import_record(klass, attributes)
|
76
|
+
attributes = change_columns(attributes.dup)
|
77
|
+
external_primary_key = attributes.delete(klass.primary_key)
|
78
|
+
s3_file_attributes = s3_file_keys(klass, attributes)
|
79
|
+
|
80
|
+
attributes.reject! { |key, value| !klass.column_names.include?(key) }
|
81
|
+
attributes.merge!(s3_file_attributes) if s3_file_attributes.is_a?(Hash)
|
82
|
+
|
83
|
+
record = klass.new(attributes)
|
84
|
+
return unless last_insert_id = insert_record(record)
|
85
|
+
|
86
|
+
remote_local_map[klass.table_name][external_primary_key] = last_insert_id
|
87
|
+
|
88
|
+
[klass, last_insert_id]
|
89
|
+
end
|
90
|
+
|
91
|
+
# see https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/persistence.rb#L729-L741
|
92
|
+
def insert_record(record)
|
93
|
+
attribute_names = record.class.column_names
|
94
|
+
attributes_values = record.send(:attributes_with_values_for_create, attribute_names)
|
95
|
+
|
96
|
+
primary_key = record.class._insert_record(attributes_values)
|
97
|
+
|
98
|
+
record[record.class.primary_key] = primary_key
|
99
|
+
end
|
100
|
+
|
101
|
+
def new_foreign_keys(record)
|
102
|
+
record.class.reflect_on_all_associations.inject({}) do |hash, association|
|
103
|
+
method = "new_foreign_keys_for_#{association.macro}"
|
104
|
+
method += "_polymorphic" if association.options[:polymorphic]
|
105
|
+
|
106
|
+
if respond_to?(method) && before_after = send(method, record, association)
|
107
|
+
hash.store(*before_after)
|
108
|
+
end
|
109
|
+
|
110
|
+
hash
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def new_foreign_keys_for_belongs_to(record, association)
|
115
|
+
return unless id = self.remote_local_map[association.klass.table_name].try(:[], record[association.foreign_key])
|
116
|
+
[association.foreign_key, id]
|
117
|
+
end
|
118
|
+
|
119
|
+
def new_foreign_keys_for_belongs_to_polymorphic(record, association)
|
120
|
+
return unless id = self.remote_local_map[record[association.foreign_type].tableize].try(:[], record[association.foreign_key])
|
121
|
+
[association.foreign_key, id]
|
122
|
+
end
|
123
|
+
|
124
|
+
def update_foreign_keys(record)
|
125
|
+
foreign_key_attributes = new_foreign_keys(record)
|
126
|
+
return if foreign_key_attributes.empty?
|
127
|
+
|
128
|
+
record.class.unscoped.where(id: record.id).update_all(foreign_key_attributes)
|
129
|
+
|
130
|
+
[record.reload.class, record.id]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|