monet 0.2.3 → 0.2.5

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 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