flash_flow 1.4.6 → 1.4.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|