flash_flow 1.4.6 → 1.4.7
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 +4 -4
- data/Gemfile.lock +14 -6
- data/bin/flash_flow +4 -0
- data/flash_flow.gemspec +1 -0
- data/lib/flash_flow/git.rb +4 -5
- data/lib/flash_flow/merge.rb +1 -0
- data/lib/flash_flow/merge/base.rb +25 -0
- data/lib/flash_flow/merge/master.rb +75 -0
- data/lib/flash_flow/merge/release.rb +3 -16
- data/lib/flash_flow/options.rb +5 -3
- data/lib/flash_flow/release.rb +4 -0
- data/lib/flash_flow/release/pdf_diff_generator.rb +159 -0
- data/lib/flash_flow/release/percy_client.rb +22 -5
- data/lib/flash_flow/version.rb +1 -1
- data/test/lib/release/test_pdf_diff_generator.rb +24 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15f833dbdf036b8b927f9aa237a16eeb7517dddd
|
4
|
+
data.tar.gz: 23a4cfc9d4c72be08b0d62033135761ad2b1b312
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c13b101fa30de5b08f047f5f595505b183d0e93a2ec79ffdef3537d751d0aa0503552c18533c07a3b5b15c63ca1f35e7464192eaec2e5fd193b439d9e51b82e
|
7
|
+
data.tar.gz: 9eff4cf8331cb77cc4c29db326f08e793c8ce2db05d3e2f5aa3b09502cff9bbde14e16ddb963986bf5f4bfb6889c7ea8489cc74d86d74968ef8eee57cecad947
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
flash_flow (1.4.
|
4
|
+
flash_flow (1.4.7)
|
5
5
|
hipchat (~> 1.5)
|
6
6
|
mail
|
7
7
|
octokit (~> 4.1)
|
8
8
|
percy-client
|
9
9
|
pivotal-tracker (~> 0.5)
|
10
|
+
prawn
|
10
11
|
ruby-graphviz (> 0)
|
11
12
|
|
12
13
|
GEM
|
@@ -33,11 +34,13 @@ GEM
|
|
33
34
|
domain_name (~> 0.5)
|
34
35
|
httparty (0.14.0)
|
35
36
|
multi_xml (>= 0.5.2)
|
36
|
-
httpclient (2.8.
|
37
|
-
mail (2.6.
|
38
|
-
mime-types (>= 1.16, <
|
39
|
-
mime-types (
|
40
|
-
|
37
|
+
httpclient (2.8.2)
|
38
|
+
mail (2.6.4)
|
39
|
+
mime-types (>= 1.16, < 4)
|
40
|
+
mime-types (3.1)
|
41
|
+
mime-types-data (~> 3.2015)
|
42
|
+
mime-types-data (3.2016.0521)
|
43
|
+
mimemagic (0.3.2)
|
41
44
|
mini_portile2 (2.1.0)
|
42
45
|
minitest (5.3.5)
|
43
46
|
minitest-stub_any_instance (1.0.1)
|
@@ -51,6 +54,7 @@ GEM
|
|
51
54
|
nokogiri (~> 1.5)
|
52
55
|
octokit (4.3.0)
|
53
56
|
sawyer (~> 0.7.0, >= 0.5.3)
|
57
|
+
pdf-core (0.6.1)
|
54
58
|
percy-client (1.6.0)
|
55
59
|
faraday (>= 0.9)
|
56
60
|
httpclient (>= 2.6)
|
@@ -61,6 +65,9 @@ GEM
|
|
61
65
|
nokogiri-happymapper (>= 0.5.4)
|
62
66
|
rest-client (>= 1.8.0)
|
63
67
|
pkg-config (1.1.7)
|
68
|
+
prawn (2.1.0)
|
69
|
+
pdf-core (~> 0.6.1)
|
70
|
+
ttfunk (~> 1.4.0)
|
64
71
|
rake (10.4.2)
|
65
72
|
rest-client (2.0.0)
|
66
73
|
http-cookie (>= 1.0.2, < 2.0)
|
@@ -72,6 +79,7 @@ GEM
|
|
72
79
|
addressable (>= 2.3.5, < 2.5)
|
73
80
|
faraday (~> 0.8, < 0.10)
|
74
81
|
slop (3.6.0)
|
82
|
+
ttfunk (1.4.0)
|
75
83
|
unf (0.1.4)
|
76
84
|
unf_ext
|
77
85
|
unf_ext (0.0.7.2)
|
data/bin/flash_flow
CHANGED
@@ -32,6 +32,10 @@ case
|
|
32
32
|
FlashFlow::Merge::Status.new(FlashFlow::Config.configuration.issue_tracker, FlashFlow::Config.configuration.branches, FlashFlow::Config.configuration.branch_info_file, FlashFlow::Config.configuration.git, logger: FlashFlow::Config.configuration.logger).status_html
|
33
33
|
when options[:release_branches]
|
34
34
|
FlashFlow::Merge::Release.new(options).run
|
35
|
+
when options[:gen_pdf_diffs]
|
36
|
+
FlashFlow::Release::Base.new(FlashFlow::Config.configuration.release).gen_pdf_diffs(*options[:gen_pdf_diffs])
|
37
|
+
when options[:merge_release]
|
38
|
+
FlashFlow::Merge::Master.new(options).run
|
35
39
|
else
|
36
40
|
FlashFlow::Merge::Acceptance.new(options).run
|
37
41
|
FlashFlow::IssueTracker::Base.new(FlashFlow::Config.configuration.issue_tracker).stories_pushed
|
data/flash_flow.gemspec
CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_dependency 'ruby-graphviz', "> 0"
|
24
24
|
spec.add_dependency 'percy-client'
|
25
25
|
spec.add_dependency 'mail'
|
26
|
+
spec.add_dependency 'prawn'
|
26
27
|
|
27
28
|
spec.add_development_dependency "bundler", "~> 1.6"
|
28
29
|
spec.add_development_dependency "rake", "> 0"
|
data/lib/flash_flow/git.rb
CHANGED
@@ -60,12 +60,12 @@ module FlashFlow
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def branch_contains?(branch, ref)
|
63
|
-
run("branch --contains #{ref}", log: CmdRunner::LOG_CMD)
|
63
|
+
run("branch -a --contains #{ref}", log: CmdRunner::LOG_CMD)
|
64
64
|
last_stdout.split("\n").detect { |str| str[2..-1] == branch }
|
65
65
|
end
|
66
66
|
|
67
|
-
def master_branch_contains?(
|
68
|
-
branch_contains?(master_branch,
|
67
|
+
def master_branch_contains?(sha)
|
68
|
+
branch_contains?("remotes/#{remote}/#{master_branch}", sha)
|
69
69
|
end
|
70
70
|
|
71
71
|
def in_original_merge_branch
|
@@ -175,13 +175,12 @@ module FlashFlow
|
|
175
175
|
run("push #{'-f' if force} #{remote} #{branch}")
|
176
176
|
end
|
177
177
|
|
178
|
-
def
|
178
|
+
def copy_temp_to_branch(branch, squash_message = nil)
|
179
179
|
run("checkout #{temp_merge_branch}")
|
180
180
|
run("merge --strategy=ours --no-edit #{branch}")
|
181
181
|
run("checkout #{branch}")
|
182
182
|
run("merge #{temp_merge_branch}")
|
183
183
|
|
184
|
-
|
185
184
|
squash_commits(branch, squash_message) if squash_message
|
186
185
|
end
|
187
186
|
|
data/lib/flash_flow/merge.rb
CHANGED
@@ -15,6 +15,7 @@ module FlashFlow
|
|
15
15
|
class VersionError < RuntimeError; end
|
16
16
|
class OutOfSyncWithRemote < RuntimeError; end
|
17
17
|
class UnmergeableBranch < RuntimeError; end
|
18
|
+
class NothingToMergeError < RuntimeError; end
|
18
19
|
|
19
20
|
def initialize(opts={})
|
20
21
|
@local_git = Git.new(Config.configuration.git, logger)
|
@@ -70,6 +71,30 @@ module FlashFlow
|
|
70
71
|
branch.ref == @git.working_branch
|
71
72
|
end
|
72
73
|
|
74
|
+
def pending_release
|
75
|
+
@data.releases.detect { |r| r['status'] == 'Pending' }
|
76
|
+
end
|
77
|
+
|
78
|
+
def release_ahead_of_master
|
79
|
+
@git.branch_exists?("#{@git.remote}/#{@git.release_branch}") &&
|
80
|
+
!@git.master_branch_contains?(@git.get_sha("#{@git.remote}/#{@git.release_branch}"))
|
81
|
+
end
|
82
|
+
|
83
|
+
def write_data(commit_msg)
|
84
|
+
@git.in_temp_merge_branch do
|
85
|
+
@git.run("reset --hard #{@git.remote}/#{@git.merge_branch}")
|
86
|
+
end
|
87
|
+
@git.in_merge_branch do
|
88
|
+
@git.run("reset --hard #{@git.remote}/#{@git.merge_branch}")
|
89
|
+
end
|
90
|
+
|
91
|
+
@data.save!
|
92
|
+
|
93
|
+
@git.copy_temp_to_branch(@git.merge_branch, commit_msg)
|
94
|
+
@git.delete_temp_merge_branch
|
95
|
+
@git.push(@git.merge_branch, false)
|
96
|
+
end
|
97
|
+
|
73
98
|
end
|
74
99
|
end
|
75
100
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'flash_flow/merge/base'
|
2
|
+
|
3
|
+
module FlashFlow
|
4
|
+
module Merge
|
5
|
+
class Master < Base
|
6
|
+
|
7
|
+
class GitPushFailure < RuntimeError; end
|
8
|
+
|
9
|
+
def initialize(opts={})
|
10
|
+
super(opts)
|
11
|
+
|
12
|
+
@data = Data::Base.new({}, Config.configuration.branch_info_file, @git, logger: logger)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
begin
|
17
|
+
check_version
|
18
|
+
puts "Merging #{@git.release_branch} into #{@git.master_branch}"
|
19
|
+
logger.info "\n\n### Beginning merge of #{@git.release_branch} into #{@git.master_branch} ###\n\n"
|
20
|
+
|
21
|
+
mergers, errors = [], []
|
22
|
+
|
23
|
+
@lock.with_lock do
|
24
|
+
release = pending_release
|
25
|
+
if !release
|
26
|
+
raise NothingToMergeError.new("There is no pending release.")
|
27
|
+
elsif !release_ahead_of_master
|
28
|
+
raise NothingToMergeError.new("The release branch '#{@git.release_branch}' has no commits that are not in master")
|
29
|
+
end
|
30
|
+
|
31
|
+
@git.in_original_merge_branch do
|
32
|
+
@git.initialize_rerere
|
33
|
+
end
|
34
|
+
|
35
|
+
@git.in_branch(@git.master_branch) do
|
36
|
+
@git.run("reset --hard origin/master")
|
37
|
+
merge_branches([Data::Branch.new(@git.release_branch)]) do |branch, merger|
|
38
|
+
mergers << [branch, merger]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
errors = mergers.select { |m| m.last.result != :success }
|
43
|
+
|
44
|
+
if errors.empty?
|
45
|
+
unless @git.push("#{@git.master_branch}:#{@git.master_branch}", true)
|
46
|
+
raise GitPushFailure.new("Unable to push to #{@git.master_branch}. See log for details.")
|
47
|
+
end
|
48
|
+
|
49
|
+
released_sha = @git.get_sha(@git.master_branch)
|
50
|
+
|
51
|
+
release['status'] = 'Success'
|
52
|
+
release['released_sha'] = released_sha
|
53
|
+
|
54
|
+
write_data('Release merged [ci skip]')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if errors.empty?
|
59
|
+
puts 'Success!'
|
60
|
+
else
|
61
|
+
raise UnmergeableBranch.new("#{@git.release_branch} didn't merge successfully to #{@git.master_branch}:\n #{errors.map { |e| e.first.ref }.join("\n ")}")
|
62
|
+
end
|
63
|
+
|
64
|
+
logger.info "### Finished merge of #{@git.release_branch} into #{@git.master_branch} ###"
|
65
|
+
rescue Lock::Error, OutOfSyncWithRemote, UnmergeableBranch, GitPushFailure, NothingToMergeError, VersionError => e
|
66
|
+
puts 'Failure!'
|
67
|
+
puts e.message
|
68
|
+
ensure
|
69
|
+
@local_git.run("checkout #{@local_git.working_branch}")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -5,7 +5,6 @@ module FlashFlow
|
|
5
5
|
class Release < Base
|
6
6
|
|
7
7
|
class PendingReleaseError < RuntimeError; end
|
8
|
-
class NothingToMergeError < RuntimeError; end
|
9
8
|
class GitPushFailure < RuntimeError; end
|
10
9
|
|
11
10
|
def initialize(opts={})
|
@@ -26,11 +25,10 @@ module FlashFlow
|
|
26
25
|
mergers, errors = [], []
|
27
26
|
|
28
27
|
@lock.with_lock do
|
29
|
-
release =
|
28
|
+
release = pending_release
|
30
29
|
if release
|
31
30
|
raise PendingReleaseError.new("There is already a pending release: #{release}")
|
32
|
-
elsif
|
33
|
-
!@git.branch_contains?(@git.master_branch, @git.get_sha("#{@git.remote}/#{@git.release_branch}"))
|
31
|
+
elsif release_ahead_of_master
|
34
32
|
raise PendingReleaseError.new("The release branch '#{@git.release_branch}' has commits that are not in master")
|
35
33
|
end
|
36
34
|
|
@@ -56,18 +54,7 @@ module FlashFlow
|
|
56
54
|
|
57
55
|
@data.releases.unshift({ created_at: Time.now, sha: release_sha, status: 'Pending' })
|
58
56
|
|
59
|
-
|
60
|
-
@git.run("reset --hard #{@git.remote}/#{@git.merge_branch}")
|
61
|
-
end
|
62
|
-
@git.in_merge_branch do
|
63
|
-
@git.run("reset --hard #{@git.remote}/#{@git.merge_branch}")
|
64
|
-
end
|
65
|
-
|
66
|
-
@data.save!
|
67
|
-
|
68
|
-
@git.copy_temp_to_branch(@git.merge_branch, 'Release data updated [ci skip]')
|
69
|
-
@git.delete_temp_merge_branch
|
70
|
-
@git.push(@git.merge_branch, false)
|
57
|
+
write_data('Release branch created [ci skip]')
|
71
58
|
end
|
72
59
|
end
|
73
60
|
|
data/lib/flash_flow/options.rb
CHANGED
@@ -5,8 +5,8 @@ module FlashFlow
|
|
5
5
|
def self.parse
|
6
6
|
options = {}
|
7
7
|
opt_parser = OptionParser.new do |opts|
|
8
|
-
opts.banner =
|
9
|
-
opts.separator
|
8
|
+
opts.banner = 'Usage: flash_flow [options]'
|
9
|
+
opts.separator ''
|
10
10
|
|
11
11
|
opts.on('--install', 'Copy flash_flow.yml.erb to your repo and exit') { |v| options[:install] = true }
|
12
12
|
opts.on('-v', '--version', 'Print the current version of flash flow and exit') { |v| options[:version] = true }
|
@@ -25,8 +25,10 @@ module FlashFlow
|
|
25
25
|
opts.on('--merge-status', 'Show status of all branches and their stories and exit') { |v| options[:merge_status] = true }
|
26
26
|
opts.on('--merge-status-html', 'Show status of all branches and their stories in html format and exit') { |v| options[:merge_status_html] = true }
|
27
27
|
opts.on('--make-release branch1,branch2', 'Comma-delimited list of branches to merge to the release branch. Run "--merge-release ready" to merge all ready to ship branches') { |v| options[:release_branches] = v.split(',') }
|
28
|
+
opts.on('--gen-pdf-diffs output_file,build_id,threshold', 'Generate a pdf file with screenshot differences for the specified (latest) build. output_file is required. build_id defaults to the latest build. threshold defaults to 0') { |v| options[:gen_pdf_diffs] = v.split(',') }
|
29
|
+
opts.on('--merge-release', 'Merge the release branch into the master branch and push') { |v| options[:merge_release] = true }
|
28
30
|
|
29
|
-
opts.on_tail(
|
31
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
30
32
|
puts opts
|
31
33
|
exit
|
32
34
|
end
|
data/lib/flash_flow/release.rb
CHANGED
@@ -19,6 +19,10 @@ module FlashFlow
|
|
19
19
|
@release.send_release_email if @release.respond_to?(:send_release_email)
|
20
20
|
end
|
21
21
|
|
22
|
+
def gen_pdf_diffs(output_file, threshold=0.0)
|
23
|
+
@release.gen_pdf_diffs(output_file, threshold) if @release.respond_to?(:gen_pdf_diffs)
|
24
|
+
end
|
25
|
+
|
22
26
|
end
|
23
27
|
end
|
24
28
|
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'prawn'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module FlashFlow
|
5
|
+
module Release
|
6
|
+
class PdfDiffGenerator
|
7
|
+
|
8
|
+
NUM_COLUMNS = 3
|
9
|
+
SPACE_BETWEEN = 10
|
10
|
+
|
11
|
+
def generate(compare_data, filename, threshold, verbose=true)
|
12
|
+
info = collect_comparison_info(compare_data, threshold)
|
13
|
+
@orientation = :portrait
|
14
|
+
Prawn::Document.generate(filename, page_layout: @orientation, margin: [10, 10, 10, 10]) do |pdf|
|
15
|
+
set_dimensions(*pdf.bounds.top_right)
|
16
|
+
generate_title_page(pdf)
|
17
|
+
info.each do |comparison|
|
18
|
+
add_comparison_to_pdf(pdf, comparison)
|
19
|
+
end
|
20
|
+
pdf.number_pages('<page> of <total>', { start_count_at: 1, align: :right, size: 12 })
|
21
|
+
@num_pages = pdf.page_count
|
22
|
+
end
|
23
|
+
puts "Wrote #{@num_pages} pages to: #{filename}" if verbose
|
24
|
+
filename
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
##########################
|
30
|
+
# #
|
31
|
+
# PDF Generation methods #
|
32
|
+
# #
|
33
|
+
##########################
|
34
|
+
|
35
|
+
def set_dimensions(width, height)
|
36
|
+
@page_width = width
|
37
|
+
@page_height = height
|
38
|
+
@column_landscape = compute_column_width([width, height].max)
|
39
|
+
@column_portrait = compute_column_width([width, height].min)
|
40
|
+
end
|
41
|
+
|
42
|
+
def compute_column_width(page_width)
|
43
|
+
(page_width / NUM_COLUMNS) - (SPACE_BETWEEN * (NUM_COLUMNS - 1))
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_title_page(pdf)
|
47
|
+
pdf.text("Compliance Diffs Generated At: #{Time.now.to_s}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def compute_scale_factor(column_width, page_height, info)
|
51
|
+
x_scale_factor = column_width / info[:width]
|
52
|
+
y_scale_factor = page_height / info[:height]
|
53
|
+
[x_scale_factor, y_scale_factor].min
|
54
|
+
end
|
55
|
+
|
56
|
+
def compute_scale_and_orientation(info)
|
57
|
+
scale_portrait = compute_scale_factor(@column_portrait, [@page_width, @page_height].max, info)
|
58
|
+
scale_landscape = compute_scale_factor(@column_landscape, [@page_width, @page_height].min, info)
|
59
|
+
if scale_portrait > scale_landscape
|
60
|
+
@orientation = :portrait
|
61
|
+
@column_width = @column_portrait
|
62
|
+
scale_portrait
|
63
|
+
else
|
64
|
+
@orientation = :landscape
|
65
|
+
@column_width = @column_landscape
|
66
|
+
scale_landscape
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_comparison_to_pdf(pdf, comparison)
|
71
|
+
scale_factor = compute_scale_and_orientation(comparison['head-screenshot'])
|
72
|
+
options = { vposition: :top, scale: scale_factor }
|
73
|
+
|
74
|
+
pdf.start_new_page(layout: @orientation)
|
75
|
+
|
76
|
+
place_image(pdf, comparison.dig('head-screenshot', :url), options, 1)
|
77
|
+
place_image(pdf, comparison.dig('base-screenshot', :url), options, 2)
|
78
|
+
place_image(pdf, comparison.dig('base-screenshot', :url), options, 3)
|
79
|
+
place_image(pdf, comparison.dig('pdiff', :url), options, 3)
|
80
|
+
end
|
81
|
+
|
82
|
+
def place_image(pdf, url, options, column)
|
83
|
+
pdf.float do
|
84
|
+
options[:position] = (column - 1) * (@column_width + SPACE_BETWEEN)
|
85
|
+
pdf.image(open(url), options)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
####################################
|
90
|
+
# #
|
91
|
+
# Methods to traverse Percy output #
|
92
|
+
# #
|
93
|
+
####################################
|
94
|
+
|
95
|
+
def collect_comparison_info(compare_info, threshold=0.0)
|
96
|
+
[].tap do |result|
|
97
|
+
compare_info['data'].each do |record|
|
98
|
+
if record['type'] == 'comparisons'
|
99
|
+
comparison_urls = get_comparison_info(record, compare_info)
|
100
|
+
result << comparison_urls if comparison_urls&.dig('diff-ratio').to_f > threshold
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_comparison_info(record, data)
|
107
|
+
{ id: record['id'] }.tap do |h|
|
108
|
+
%w(head-screenshot base-screenshot pdiff).each do |attr|
|
109
|
+
info = record.dig('relationships', attr, 'data')
|
110
|
+
unless info.nil?
|
111
|
+
attr_record = lookup_record(info['id'], info['type'], 'included', data)
|
112
|
+
h[attr] = lookup_image_url(lookup_image_id(attr_record, attr), data)
|
113
|
+
h['diff-ratio'] = attr_record.dig('attributes', 'diff-ratio') if attr == 'pdiff'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def lookup_image_id(record, attr)
|
120
|
+
if attr == 'pdiff'
|
121
|
+
record.dig('relationships', 'diff-image', 'data', 'id')
|
122
|
+
else
|
123
|
+
record.dig('relationships', 'image', 'data', 'id')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def lookup_image_url(id, data)
|
128
|
+
record = lookup_image(id, data)
|
129
|
+
unless record.nil?
|
130
|
+
{ url: record.dig('attributes', 'url'),
|
131
|
+
width: record.dig('attributes', 'width'),
|
132
|
+
height: record.dig('attributes', 'height') }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def lookup_comparison(id, data)
|
137
|
+
lookup_record(id, 'comparisons', 'data', data)
|
138
|
+
end
|
139
|
+
|
140
|
+
def lookup_image(id, data)
|
141
|
+
lookup_record(id, 'images', 'included', data)
|
142
|
+
end
|
143
|
+
|
144
|
+
def lookup_screenshot(id, data)
|
145
|
+
lookup_record(id, 'screenshots', 'data', data)
|
146
|
+
end
|
147
|
+
|
148
|
+
def lookup_snapshot(id, data)
|
149
|
+
lookup_record(id, 'snapshots', 'data', data)
|
150
|
+
end
|
151
|
+
|
152
|
+
def lookup_record(id, kind, where, data)
|
153
|
+
data[where].detect { |item| item['type'] == kind && item['id'] == id }
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|