monet 0.2.3 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2a11fc10105edf8cce2166fb0bf751e84115374f
4
- data.tar.gz: e895a699a3246d30ce4d46231a54eaaa0e4f58a7
3
+ metadata.gz: 4245512ca8fcbcf954b6e276d7dc978c9a680444
4
+ data.tar.gz: 19dd5cf3f272708cb87754604cc7902d33501144
5
5
  SHA512:
6
- metadata.gz: 693e7e62a8ccbe89d7a17370961576848c48e7c172a8184db9c5447f533062251e6ba46ff8e9e29fc1b118cad1853e70b22dfe877eba8901395733119b83fee1
7
- data.tar.gz: be761e3bf2fe43a9764a1b8b326bf779672113941ab1ee356b41c53436675ae7ce44c2b8b4dfa0da71c9a47553046ae4d6b5059f57bcf737b69cad7bf9c9911c
6
+ metadata.gz: ac0adb807b480ab762d914312f79938df763d165c52a0c43586cccd11c8b680863df007d7fd3fe132b6b33d4469e3104795fcf17924f9fa4fd287475294176b3
7
+ data.tar.gz: 95da6332f5b6d0af5ce48980ceedd1535ea21d472ac11c42e938a6fae25bf1b472efa6a74fd1739a61ab7afb1349bf6f5445d6800244e5f4a7b91ae3fc4de87c
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.0
1
+ 2.0.0-p247
data/.travis.yml CHANGED
@@ -1,7 +1,14 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.0
4
+ - 2.1
4
5
  - 1.9.3
5
6
  - rbx
7
+ - jruby-19mode
8
+
9
+ matrix:
10
+ allow_failures:
11
+ - rvm: rbx
12
+ - rvm: jruby-19mode
6
13
 
7
14
  script: bundle exec rake test
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gemspec
5
5
 
6
6
  group :test do
7
7
  gem 'flexmock'
8
- gem 'pry'
8
+ gem 'jazz_hands'
9
9
  gem 'webmock'
10
10
  gem 'vcr'
11
11
  end
@@ -7,5 +7,9 @@ module Monet
7
7
  def initialize(path)
8
8
  @path = path
9
9
  end
10
+
11
+ def image
12
+ Monet::Image.new @path
13
+ end
10
14
  end
11
- end
15
+ end
@@ -1,4 +1,4 @@
1
- require 'monet/path_router'
1
+ require 'monet/image'
2
2
  require 'monet/changeset'
3
3
  require 'monet/baseless_image'
4
4
  require 'monet/compare'
@@ -11,20 +11,18 @@ module Monet
11
11
  attr_reader :flags
12
12
 
13
13
  def initialize(config)
14
- @capture_dir = config.capture_dir
15
- @baseline_dir = config.baseline_dir
14
+ @config = config
16
15
 
17
16
  strategy = Monet.const_get(config.compare_type)
18
17
 
18
+ @router = Monet::Router.new config
19
19
  @comparer = Monet::Compare.new(strategy)
20
- @router = Monet::PathRouter.new(config)
21
20
  @flags = []
22
21
  end
23
22
 
24
23
  def run
25
- captures.each do |capture|
26
- baseline_path = @router.capture_to_baseline(capture)
27
- compare @comparer.compare(baseline_path, capture)
24
+ captures.each do |img|
25
+ compare @comparer.compare(@router.baseline_dir(img.name), img.path)
28
26
  end
29
27
 
30
28
  @flags
@@ -40,29 +38,44 @@ module Monet
40
38
  end
41
39
 
42
40
  def captures
43
- Dir.glob File.join(site_dir(@capture_dir), "*.png")
41
+ files = Dir.glob(File.join(@config.capture_dir, @config.site, "*.png"))
42
+ files.map do |path|
43
+ Monet::Image.new(path)
44
+ end
44
45
  end
45
46
 
46
47
  def discard(path)
47
48
  puts "discarding #{path}"
48
- FileUtils.remove(path)
49
+ FileUtils.remove(path) if File.exists?(path)
49
50
  end
50
51
 
51
52
  def baseline(diff)
52
- path = diff.path
53
- to = site_dir(@baseline_dir)
53
+ image = diff.is_a?(String) ? Monet::Image.new(diff) : diff.image
54
54
 
55
- FileUtils.mkpath to unless Dir.exists? to
56
- FileUtils.move(path, to)
55
+ puts "baselining #{image.path}"
56
+ image = rebase(image)
57
57
 
58
- puts "baselining #{path}"
58
+ # delete diff image
59
+ discard @router.diff_dir(image.name)
59
60
 
60
- @router.capture_to_baseline(path)
61
+ image.thumbnail!(@router.thumbnail_dir) if @config.thumbnail?
62
+ image.path
61
63
  end
62
64
 
63
65
  private
64
- def site_dir(base)
65
- File.join base, @router.root_dir
66
+ # returns a new image for the moved image
67
+ def rebase(image)
68
+ new_path = @router.baseline_dir(image.name)
69
+
70
+ create_path_for_file(new_path)
71
+ FileUtils.move(image.path, new_path)
72
+
73
+ Monet::Image.new(new_path)
74
+ end
75
+
76
+ def create_path_for_file(file)
77
+ path = File.dirname(file)
78
+ FileUtils.mkpath path unless Dir.exists? path
66
79
  end
67
80
  end
68
81
  end
data/lib/monet/capture.rb CHANGED
@@ -4,8 +4,8 @@ require "capybara"
4
4
  require 'capybara/poltergeist'
5
5
  require "capybara/dsl"
6
6
 
7
- require 'monet/config'
8
- require 'monet/path_router'
7
+ require 'monet/router'
8
+ require 'monet/image'
9
9
 
10
10
  module Monet
11
11
  class Capture
@@ -14,45 +14,46 @@ module Monet
14
14
  MAX_HEIGHT = 10000
15
15
 
16
16
  def initialize(config)
17
- @config = Monet::Config.build_config(config)
18
- @router = Monet::PathRouter.new(@config)
17
+ @config = config
18
+ @router = Monet::Router.new config
19
19
 
20
20
  Capybara.default_driver = @config.driver
21
21
  end
22
22
 
23
23
  def capture_all
24
- @config.map.paths.each do |path|
25
- capture(path)
26
- end
27
- end
28
-
29
- def capture(path)
30
- visit @router.build_url(path)
31
-
32
- @config.dimensions.each do |width|
33
- file_path = @router.route_url_path(path, width)
24
+ images = []
25
+ @router.capture_routes.map do |url, paths|
26
+ visit_once url
34
27
 
35
- page.driver.resize(width, MAX_HEIGHT)
36
- page.driver.render(file_path, full: true)
28
+ paths.each do |path|
29
+ image = Monet::Image.new path
30
+ capture(url, image)
37
31
 
38
- thumbnail(file_path) if @config.thumbnail?
32
+ images << image
33
+ end
39
34
  end
35
+
36
+ images
40
37
  end
41
38
 
42
- def thumbnail(path)
43
- img = ChunkyPNG::Image.from_file(path)
44
- short_edge = [img.width, img.height].min
45
- save_path = @router.to_thumbnail_path(path)
46
- save_dir = File.dirname save_path
39
+ def capture(url, image_or_save_path)
40
+ if image_or_save_path.is_a? String
41
+ image = Monet::Image.new image_or_save_path
42
+ else
43
+ image = image_or_save_path
44
+ end
47
45
 
48
- puts save_path
46
+ visit_once url
49
47
 
50
- cropped = img.crop(0, 0, short_edge, short_edge)
51
- resized = cropped.resize(200, 200)
48
+ page.driver.resize(image.width, MAX_HEIGHT)
49
+ page.driver.render(image.path, full: true)
52
50
 
53
- FileUtils.mkdir_p save_dir unless Dir.exists? save_dir
51
+ image.thumbnail!(@router.thumbnail_dir) if @config.thumbnail?
52
+ end
54
53
 
55
- resized.save save_path
54
+ private
55
+ def visit_once(url)
56
+ visit url unless current_url == url
56
57
  end
57
58
  end
58
59
  end
@@ -1,11 +1,14 @@
1
1
  require 'spidr'
2
2
  require 'monet/url_helpers'
3
+ require 'forwardable'
3
4
 
4
5
  module Monet
5
6
  class CaptureMap
6
7
  extend Forwardable
7
8
 
8
9
  class PathCollection
10
+ extend Forwardable
11
+
9
12
  include URLHelpers
10
13
  attr_reader :paths, :root_url
11
14
 
@@ -25,6 +28,8 @@ module Monet
25
28
  def normalized_path(path)
26
29
  path.chomp "/"
27
30
  end
31
+
32
+ def_delegators :paths, :size, :length, :count
28
33
  end
29
34
 
30
35
  class PathSpider < PathCollection
@@ -32,7 +37,10 @@ module Monet
32
37
  SKIP_PATHS = [/\?.*/]
33
38
 
34
39
  def paths
35
- @paths = normalize Spidr.site(@root_url, ignore_links: ignores)
40
+ return @paths unless @paths.empty?
41
+
42
+ normalize Spidr.site(@root_url, ignore_links: ignores)
43
+ @paths.uniq!
36
44
  end
37
45
 
38
46
  def ignores
@@ -41,7 +49,7 @@ module Monet
41
49
 
42
50
  private
43
51
  def normalize(spider_results)
44
- spider_results.history.map &:to_s
52
+ spider_results.history.each {|path| add(path.path) }
45
53
  end
46
54
  end
47
55
 
@@ -54,7 +62,7 @@ module Monet
54
62
  yield(@path_helper) if block_given?
55
63
  end
56
64
 
57
- def_delegators :@path_helper, :paths, :paths=, :add, :root_url
65
+ def_delegators :@path_helper, :paths, :paths=, :add, :root_url, :size, :count, :length
58
66
 
59
67
  def type_mapper
60
68
  case @type
@@ -8,6 +8,10 @@ module Monet
8
8
  @path = path
9
9
  end
10
10
 
11
+ def image
12
+ Monet::Image.new @path
13
+ end
14
+
11
15
  def modified?
12
16
  pixels_changed > 0
13
17
  end
data/lib/monet/config.rb CHANGED
@@ -21,29 +21,29 @@ module Monet
21
21
 
22
22
  attr_accessor *DEFAULT_OPTIONS.keys
23
23
 
24
+ # configure via options
24
25
  def initialize(opts={})
25
26
  DEFAULT_OPTIONS.each do |opt, default|
26
27
  send "#{opt}=", opts[opt] || default
27
28
  end
28
29
  end
29
30
 
31
+ # configure via block
30
32
  def self.config(&block)
31
33
  cfg = new
32
- block.call cfg
34
+ yield cfg if block_given?
35
+
33
36
  cfg
34
37
  end
35
38
 
39
+ # configure via YAML
36
40
  def self.load(path="./config.yaml")
37
- config = YAML::load(File.open(path))
38
- new(config)
39
- end
40
-
41
- def self.build_config(opts)
42
- (opts.is_a? Monet::Config) ? opts : new(opts)
41
+ opts = YAML::load(File.open(path))
42
+ new(opts)
43
43
  end
44
44
 
45
45
  def base_url=(url)
46
- @base_url ||= parse_uri(url) unless url.nil?
46
+ @base_url = parse_uri(url) unless url.nil?
47
47
  end
48
48
 
49
49
  def base_url
@@ -51,6 +51,12 @@ module Monet
51
51
  @base_url
52
52
  end
53
53
 
54
+ def site
55
+ parse_uri(base_url).host
56
+ end
57
+ alias_method :host, :site
58
+ alias_method :root_dir, :site
59
+
54
60
  def thumbnail?
55
61
  !!thumbnail
56
62
  end
@@ -68,13 +74,14 @@ module Monet
68
74
  end
69
75
 
70
76
  def map=(paths)
71
- map.paths = paths unless paths.nil?
77
+ @map = nil
78
+ return @map if paths.nil?
79
+ paths == :spider ? map(paths) : (map.paths = paths)
72
80
  end
73
81
 
74
82
  def map(type=:explicit, &block)
75
83
  @map ||= CaptureMap.new(base_url, type)
76
-
77
- block.call(@map) if block_given? && type == :explicit
84
+ yield @map if block_given?
78
85
 
79
86
  @map
80
87
  end
@@ -0,0 +1,67 @@
1
+ require 'chunky_png'
2
+ require 'monet/config'
3
+ require 'monet/url_helpers'
4
+
5
+ module Monet
6
+ class Image
7
+ include URLHelpers
8
+
9
+ attr_reader :path
10
+
11
+ def initialize(path)
12
+ @path = File.expand_path path
13
+ end
14
+
15
+ def is_diff?
16
+ name.include? "-diff"
17
+ end
18
+
19
+ def diff_path
20
+ @path.gsub(".png", "-diff.png")
21
+ end
22
+
23
+ def flagged?
24
+ File.exists? diff_path
25
+ end
26
+
27
+ def width
28
+ @width ||= File.basename(@path, ".png").split("-").last.to_i
29
+ end
30
+
31
+ def name
32
+ @name ||= File.basename @path
33
+ end
34
+
35
+ def basename
36
+ @path.split(File::SEPARATOR)[-2..-1].join(File::SEPARATOR)
37
+ end
38
+
39
+ def to_image
40
+ ChunkyPNG::Image.from_file(@path)
41
+ end
42
+
43
+ def root_dir
44
+ File.dirname @path
45
+ end
46
+
47
+ def thumbnail!(save_to=nil)
48
+ img = to_image
49
+ short_edge = [img.width, img.height].min
50
+
51
+ cropped = img.crop(0, 0, short_edge, short_edge)
52
+ resized = cropped.resize(200, 200)
53
+
54
+ if save_to.nil?
55
+ save_to = @path.gsub(".png", "-thumb.png")
56
+ else
57
+ save_to = File.join save_to, File.basename(@path)
58
+ end
59
+
60
+ save_dir = File.dirname(save_to)
61
+ FileUtils.mkdir_p save_dir unless Dir.exists?(save_dir)
62
+
63
+ resized.save save_to
64
+ save_to
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,71 @@
1
+ module Monet
2
+ class Router
3
+ include URLHelpers
4
+ TYPES = [
5
+ :baseline,
6
+ :capture,
7
+ :thumbnail
8
+ ]
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ end
13
+
14
+ TYPES.each do |type|
15
+ define_method "#{type}_dir" do |filename=""|
16
+ File.join(@config.send("#{type}_dir"), @config.site, filename).chomp("/")
17
+ end
18
+
19
+ define_method "#{type}_url" do |filename|
20
+ type_dir = @config.send "#{type}_dir"
21
+ image_url(type_dir, filename)
22
+ end
23
+ end
24
+
25
+ def original_url(path)
26
+ url = path.split("/").last
27
+ path = url.split('|')[1..-1].join("/").gsub(/-\d+\.png/, "")
28
+
29
+ "#{@config.base_url}/#{path}"
30
+ end
31
+
32
+ def diff_dir(filename="")
33
+ filename = filename.gsub(".png", "-diff.png") unless filename.empty?
34
+ File.join(@config.baseline_dir, @config.site, filename).chomp("/")
35
+ end
36
+
37
+ def diff_url(path)
38
+ diff = basename diff_dir(path)
39
+ image_url(@config.baseline_dir, diff)
40
+ end
41
+
42
+ def capture_routes
43
+ urls = {}
44
+ @config.map.paths.each do |path|
45
+ url = "#{@config.base_url}#{path}".chomp("/")
46
+ @config.dimensions.each do |width|
47
+ urls[url] ||= []
48
+ urls[url] << url_to_filepath(url, width)
49
+ end
50
+ end
51
+
52
+ urls
53
+ end
54
+
55
+ def url_to_filepath(url, width="*")
56
+ uri = parse_uri(url)
57
+ name = File.join @config.site, "#{@config.site}#{uri.path.gsub(/\//, '|')}"
58
+ File.join @config.capture_dir, "#{name}-#{width}.png"
59
+ end
60
+
61
+ private
62
+ def basename(path)
63
+ File.basename path
64
+ end
65
+
66
+ def image_url(type_dir, name)
67
+ relative_path = Pathname.new(type_dir).relative_path_from(Pathname.new(File.join(type_dir, "..")))
68
+ "/#{relative_path}/#{@config.root_dir}/#{name}"
69
+ end
70
+ end
71
+ end
data/lib/monet/tasks.rb CHANGED
@@ -12,27 +12,30 @@ end
12
12
  namespace :monet do
13
13
  desc "clean out the baseline directory"
14
14
  task :clean, :path do |t, args|
15
- Monet.clean load_config(args)
15
+ config = load_config(args)
16
+ Dir.glob(File.join(config.baseline_dir, "**", "*.png")).each do |img|
17
+ File.delete img
18
+ end
16
19
  end
17
20
 
18
21
  desc "Runs the site and grabs baselines"
19
22
  task :baseline => [:clean, :run] do
20
23
  end
21
24
 
22
-
23
25
  namespace :thumbnail do
24
26
  desc "Thumbnail all baseline images"
25
- task :baseline do
27
+ task :baseline, :path do |t, args|
26
28
  config = load_config(args)
27
29
  capture = Monet::Capture.new config
28
30
 
29
31
  images_from_dir(config.baseline_dir).each do |image|
30
- capture.thumbnail image
32
+ Monet::Image.new(image).thumbnail! config.thumbnail_dir
31
33
  end
32
34
  end
33
35
 
34
36
  desc "Thumnail all captured images"
35
- task :captures do
37
+ task :captures, :path do |t, args|
38
+ config = load_config(args)
36
39
  capture = Monet::Capture.new config
37
40
 
38
41
  images_from_dir(config.capture_dir).each do |image|
@@ -44,7 +47,7 @@ namespace :monet do
44
47
  desc "Run the baseline comparison"
45
48
  task :run, :path do |t, args|
46
49
  config = load_config(args)
47
- Monet.capture config
48
- Monet.compare config
50
+ Monet::Capture.new(config).capture_all
51
+ Monet::BaselineControl.new(config).run
49
52
  end
50
53
  end
data/lib/monet/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Monet
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.5"
3
3
  end
data/lib/monet.rb CHANGED
@@ -1,34 +1,9 @@
1
1
  require "monet/version"
2
2
  require 'monet/config'
3
- require "monet/capture"
4
- require "monet/compare"
5
- require "monet/baseline_control"
3
+ require 'monet/capture'
4
+ require 'monet/baseline_control'
5
+ require 'monet/router'
6
+ require 'monet/image'
6
7
 
7
8
  module Monet
8
- class << self
9
- def clean(opts)
10
- config = load_config(opts)
11
- Dir.glob(File.join(config.baseline_dir, "**", "*.png")).each do |img|
12
- File.delete img
13
- end
14
- end
15
-
16
- def capture(opts)
17
- agent = Monet::Capture.new(load_config(opts))
18
- agent.capture_all
19
- end
20
-
21
- def compare(opts)
22
- control = Monet::BaselineControl.new(opts)
23
- control.run
24
- end
25
-
26
- def config(&block)
27
- Monet::Config.config block
28
- end
29
-
30
- def load_config(options)
31
- Monet::Config.build_config(options)
32
- end
33
- end
34
9
  end
@@ -59,57 +59,12 @@ describe Monet::CaptureMap do
59
59
  end
60
60
  end
61
61
 
62
- context "spider mapper", vcr: { cassette_name: "spider", record: :new_episodes } do
63
- Given(:map) { Monet::CaptureMap.new("http://www.spider.io", :spider) }
62
+ context "spider mapper", vcr: { cassette_name: "spider-map", record: :new_episodes } do
63
+ Given(:map) { Monet::CaptureMap.new("http://staging.lance.com", :spider) }
64
64
  When(:paths) { map.paths }
65
- Then { paths.should == [
66
- "http://www.spider.io",
67
- "http://www.spider.io/anti-malware/",
68
- "http://www.spider.io/viewability/",
69
- "http://www.spider.io/press/",
70
- "http://www.spider.io/team/",
71
- "http://www.spider.io/blog/",
72
- "http://www.spider.io/",
73
- "http://www.spider.io/blog/2013/03/chameleon-botnet/",
74
- "http://www.spider.io/blog/2013/12/cyber-criminals-defraud-display-advertisers-with-tdss/",
75
- "http://www.spider.io/zeus",
76
- "http://www.spider.io/blog/2013/11/how-to-defraud-display-advertisers-with-zeus/",
77
- "http://www.spider.io/blog/2013/05/a-botnet-primer-for-display-advertisers/",
78
- "http://www.spider.io/blog/2013/09/display-advertisers-funding-cybercriminals-since-2011/",
79
- "http://www.spider.io/blog/2013/09/securing-the-legitimacy-of-display-ad-inventory/",
80
- "http://www.spider.io/blog/2013/04/display-advertising-fraud-is-a-sell-side-problem/",
81
- "http://www.spider.io/blog/2012/12/internet-explorer-data-leakage/",
82
- "http://www.spider.io/blog/2013/08/sambreel-is-still-injecting-ads-video-advertisers-beware/",
83
- "http://www.spider.io/blog/page/2/",
84
- "http://www.spider.io/blog/2011/10/the-problem-with-client-side-analytics/",
85
- "http://www.spider.io/blog/2012/12/responsible-disclosure/",
86
- "http://www.spider.io/blog/2013/05/spider-io-granted-mrc-accreditation-for-viewable-impression-measurement/",
87
- "http://www.spider.io/blog/2013/04/at-least-two-percent-of-monitored-display-ad-exchange-inventory-is-hidden/",
88
- "http://www.spider.io/blog/2013/03/who-is-behind-the-chameleon-botnet/",
89
- "http://www.spider.io/blog/page/3/",
90
- "http://www.spider.io/blog/2013/02/which-display-ad-exchange-sells-the-highest-quality-inventory/",
91
- "http://www.spider.io/blog/2012/12/there-are-two-ways-to-measure-ad-viewability-there-is-only-one-right-way/",
92
- "http://www.spider.io/blog/page/4/",
93
- "http://www.spider.io/blog/2012/12/review-of-iab-safeframe-1-0/",
94
- "http://www.spider.io/blog/2012/10/qa-about-ad-viewability/",
95
- "http://www.spider.io/blog/2012/10/the-first-technology-to-measure-the-viewability-of-iframed-ads-across-all-major-browsers-press-release/",
96
- "http://www.spider.io/vSta98h",
97
- "http://www.spider.io/blog/2012/07/join-us-for-a-tipple-at-spider-towers/",
98
- "http://www.spider.io/blog/2012/07/startups-acquiring-startups-for-equity/",
99
- "http://www.spider.io/blog/page/5/",
100
- "http://www.spider.io/visibility-demo-screencast/",
101
- "http://www.spider.io/blog/2012/07/whats-in-an-ip-address/",
102
- "http://www.spider.io/blog/2011/12/physical-hack-day/",
103
- "http://www.spider.io/blog/2011/11/our-first-hack-day/",
104
- "http://www.spider.io/careers/",
105
- "http://www.spider.io/blog/2011/10/extreme-architecting/",
106
- "http://www.spider.io/blog/2011/09/how-to-catch-a-bot/",
107
- "http://www.spider.io/blog/page/6/",
108
- "http://www.spider.io/blog/2011/10/testing-javascript-with-mturk/",
109
- "http://www.spider.io/blog/2011/10/demonstration-screencast-verifying-that-display-ads-are-visible-from-within-iframes/",
110
- "http://www.spider.io/blog/2011/10/calling-out-to-researchersacademics/",
111
- "http://www.spider.io/blog/page/7/"
112
- ]}
65
+ Then { paths.size.should == 64 }
66
+ And { paths.first.end_with?("/").should be false }
67
+ And { paths.first.start_with?("http://").should be_false }
113
68
  And { paths.any? {|p| p.end_with? "css" }.should be_false }
114
69
  end
115
70
  end