url_tracker 0.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.
data/bin/ut ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/url_tracker'
4
+
5
+ UrlTracker::Client.new.run(ARGV)
data/bin/utd ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/url_tracker'
4
+
5
+ UrlTracker::Server.new.run(ARGV)
@@ -0,0 +1,70 @@
1
+ require 'notifier'
2
+
3
+ module UrlTracker
4
+ require_relative 'url_tracker/version'
5
+ require_relative 'url_tracker/page'
6
+ require_relative 'url_tracker/periodic'
7
+ require_relative 'url_tracker/socket_communication'
8
+ require_relative 'url_tracker/client'
9
+ require_relative 'url_tracker/server'
10
+
11
+ extend self
12
+
13
+ require 'set'
14
+
15
+ # Tracks +url+ fetching its content every +time+ seconds (defaults
16
+ # to 5*60 - 5 minutes).
17
+ def track_uri(uri, time = 5*60)
18
+ init_ivars
19
+
20
+ p = Page.new(URI(uri))
21
+
22
+ if @pages.add?(p)
23
+ @scheduler.task(uri).every(:minute) { check_change(p) }
24
+ 'ok'
25
+ else
26
+ 'error'
27
+ end
28
+ end
29
+
30
+ # Returns an array with all URIs currently being tracked
31
+ def list_all
32
+ init_ivars
33
+ uri_list = @pages.map { |page| page.uri }
34
+
35
+ uri_list.empty? ? ',' : uri_list.join(',')
36
+ end
37
+
38
+ # Stops tracking given URI
39
+ def release_uri(uri)
40
+ init_ivars
41
+
42
+ p = Page.new(URI(uri))
43
+ if @pages.include?(p)
44
+ @pages.delete(p)
45
+ @scheduler.remove_task(uri)
46
+ 'ok'
47
+ else
48
+ 'error'
49
+ end
50
+ end
51
+
52
+ # Forget about current tracked URIs
53
+ def restart
54
+ init_ivars
55
+ @pages.clear
56
+ @scheduler.restart
57
+ end
58
+
59
+ private
60
+
61
+ def check_change(page)
62
+ Notifier.notify(title: 'Change!', message: "Page #{page.uri} changed!") if page.changed?
63
+ end
64
+
65
+ def init_ivars
66
+ @pages ||= Set.new
67
+ @scheduler ||= Periodic.new
68
+ end
69
+
70
+ end
@@ -0,0 +1,88 @@
1
+ require_relative 'test_helper'
2
+
3
+ class TestClient < MiniTest::Unit::TestCase
4
+
5
+ def setup
6
+ remove_socket
7
+ @server = Object.new.extend(UrlTracker::SocketCommunication)
8
+ @server.bind(socket_file)
9
+
10
+ async do
11
+ @server.wait_for_connection
12
+ end
13
+
14
+ @client = UrlTracker::Client.new(socket_file)
15
+
16
+ wait_sync
17
+ end
18
+
19
+ def teardown
20
+ @client.close_connection
21
+ @server.close_connection
22
+ end
23
+
24
+ def test_successful_track_new_url
25
+ async { @success = @client.track(fake_url) }
26
+ @server.write('ok')
27
+ wait_sync
28
+
29
+ assert_equal "track #{fake_url}", @server.next_message
30
+ assert @success
31
+ end
32
+
33
+ def test_server_could_not_track_url
34
+ async { @success = @client.track(fake_url) }
35
+ @server.write('error')
36
+ wait_sync
37
+
38
+ assert !@success
39
+ end
40
+
41
+ def test_list_urls_empty
42
+ async { @urls = @client.list }
43
+ @server.write(',')
44
+ wait_sync
45
+
46
+ assert_empty @urls
47
+ end
48
+
49
+ def test_list_single_url
50
+ async { @urls = @client.list }
51
+ @server.write(fake_url)
52
+ wait_sync
53
+
54
+ assert_equal [fake_url], @urls
55
+ end
56
+
57
+ def test_list_multiple_urls
58
+ async { @urls = @client.list }
59
+ @server.write(fake_url + ',http://foo.bar,http://google.com')
60
+ wait_sync
61
+
62
+ assert_equal [fake_url, 'http://foo.bar', 'http://google.com'], @urls
63
+ end
64
+
65
+ def test_release_tracked_url
66
+ async { @client.track(fake_url) }
67
+ @server.write('ok')
68
+ wait_sync
69
+
70
+ @server.next_message # so that we don't get the tracking request on the assertion
71
+
72
+ async { @success = @client.release(fake_url) }
73
+ @server.write('ok')
74
+ wait_sync
75
+
76
+ assert_equal "release #{fake_url}", @server.next_message
77
+ assert @success
78
+ end
79
+
80
+ def test_release_url_not_tracked
81
+ async { @success = @client.release(fake_url) }
82
+ @server.write('error')
83
+ wait_sync
84
+
85
+ assert !@success
86
+ end
87
+
88
+ end
@@ -0,0 +1,42 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require_relative '../lib/url_tracker'
4
+
5
+ # messages sent via sockets in the tests cannot be longer than 100 bytes
6
+ MAX_TEST_SOCK_MESSAGE = 100
7
+
8
+ puts "Url Tracker: running on Ruby version #{RUBY_VERSION}"
9
+
10
+ def assert_socket_received(message, socket)
11
+ assert_block("Expected #{socket.inspect} to receive #{message.inspect}") do
12
+ socket.recvfrom(MAX_TEST_SOCK_MESSAGE).first == message
13
+ end
14
+ end
15
+
16
+ def async(stop = false, &block)
17
+ @t = Thread.new(&block)
18
+ @t.abort_on_exception = true
19
+
20
+ while !@t.stop?; end if stop
21
+ end
22
+
23
+ def wait_sync
24
+ @t.join
25
+ end
26
+
27
+ def finish_async
28
+ @t.terminate
29
+ @t.join
30
+ end
31
+
32
+ def remove_socket
33
+ File.unlink(socket_file) if File.exists?(socket_file)
34
+ end
35
+
36
+ def socket_file
37
+ '/tmp/.dk29ei39kd3.sock'
38
+ end
39
+
40
+ def fake_url
41
+ 'http://fake.com'
42
+ end
data/test/test_page.rb ADDED
@@ -0,0 +1,52 @@
1
+ require_relative 'test_helper'
2
+
3
+ class TestPage < MiniTest::Unit::TestCase
4
+
5
+ def setup
6
+ @page_fetcher = MiniTest::Mock.new
7
+ @content = 'web page'
8
+
9
+ # should receive method get with argument fake_url and return @content
10
+ @page_fetcher.expect(:get, @content, [fake_url])
11
+
12
+ @page = UrlTracker::Page.new(fake_url, @page_fetcher)
13
+ end
14
+
15
+ def test_page_content
16
+ assert_equal @content, @page.content
17
+ end
18
+
19
+ def test_page_content_bang
20
+ assert_equal @content, @page.content
21
+
22
+ change_page_content('new content')
23
+ assert_equal 'new content', @page.content!
24
+ end
25
+
26
+ def test_page_changed_before_cached_content
27
+ assert !@page.changed?, 'Expected no changes when there is no cache of the page'
28
+ end
29
+
30
+ def test_page_changed_when_no_changes_happened
31
+ @page.content # we now have a cache of the page
32
+
33
+ assert !@page.changed?, 'Expected a false result when no changes occur to a page'
34
+ end
35
+
36
+ def test_page_changed_when_changes_really_happened
37
+ @page.content
38
+ change_page_content('new content')
39
+
40
+ assert @page.changed?, 'Expected Page to indicate change when its content was changed'
41
+ end
42
+
43
+ def change_page_content(new_content)
44
+ @page_fetcher.expect(:get, new_content, [fake_url])
45
+ end
46
+
47
+ def test_page_equality
48
+ other_page = UrlTracker::Page.new(fake_url)
49
+ assert_equal @page, other_page
50
+ end
51
+
52
+ end
@@ -0,0 +1,78 @@
1
+ require_relative 'test_helper'
2
+
3
+ class TestPeriodic < MiniTest::Unit::TestCase
4
+
5
+ def setup
6
+ @p = UrlTracker::Periodic.new
7
+ end
8
+
9
+ def teardown
10
+ @p.stop
11
+ end
12
+
13
+ def test_every_with_symbol_argument
14
+ assert_equal 60, @p.every(:minute) {}
15
+ end
16
+
17
+ def test_every_with_hour_symbol
18
+ assert_equal 60*60, @p.every(:hour) {}
19
+ end
20
+
21
+ def test_two_minutes
22
+ assert_equal 2*60, @p.every(2, :minutes) {}
23
+ end
24
+
25
+ def test_two_hours
26
+ assert_equal 2*60*60, @p.every(2, :hours) {}
27
+ end
28
+
29
+ def test_invalid_first_argument
30
+ assert_raises(RuntimeError) do
31
+ @p.every(:invalid) {}
32
+ end
33
+ end
34
+
35
+ def test_invalid_time_unit
36
+ assert_raises(RuntimeError) do
37
+ @p.every(2, :invalid) {}
38
+ end
39
+ end
40
+
41
+ def test_add_named_task
42
+ @p.task("dummy").every(:minute) {}
43
+ assert_equal ["dummy"], @p.named_tasks
44
+ end
45
+
46
+ def test_remove_named_task
47
+ @p.task("dummy").every(:minute) {}
48
+ @p.remove_task("dummy")
49
+
50
+ assert_empty @p.named_tasks
51
+ end
52
+
53
+ def test_remove_inexistent_task
54
+ assert_raises(RuntimeError) do
55
+ @p.remove_task("dummy")
56
+ end
57
+ end
58
+
59
+ def test_restart
60
+ @p.stop
61
+ assert !@p.running?
62
+
63
+ @p.restart
64
+ assert @p.running?
65
+ end
66
+
67
+ def test_running
68
+ assert @p.running?, 'Expected Periodic to be running when `stop` was not called.'
69
+ end
70
+
71
+ def test_running_when_asked_to_stop
72
+ @p.stop
73
+
74
+ assert !@p.running?, 'Expected Periodic to stop'
75
+ @p.restart
76
+ end
77
+
78
+ end
@@ -0,0 +1,71 @@
1
+ require_relative 'test_helper'
2
+
3
+ class TestServer < MiniTest::Unit::TestCase
4
+
5
+ def setup
6
+ remove_socket
7
+ @server = UrlTracker::Server.new(Logger.new('/dev/null'))
8
+
9
+ async(true) { @server.run(socket_file: socket_file) }
10
+
11
+ @client = UrlTracker::Client.new(socket_file) # connected!
12
+ end
13
+
14
+ def teardown
15
+ @client.close_connection
16
+ finish_async
17
+ @server.close_connection
18
+ UrlTracker.restart
19
+ end
20
+
21
+ def test_successfully_tracks_url
22
+ assert @client.track(fake_url), 'Expected to be able to track new URI'
23
+ end
24
+
25
+ def test_track_same_url
26
+ @client.track(fake_url)
27
+ restart_client
28
+
29
+ assert !@client.track(fake_url), 'Expected to return false when tracking same URI'
30
+ end
31
+
32
+ def test_list_empty
33
+ assert_equal [], @client.list
34
+ end
35
+
36
+ def test_list_with_single_uri
37
+ @client.track(fake_url)
38
+ restart_client
39
+
40
+ assert_equal [fake_url], @client.list
41
+ end
42
+
43
+ def test_list_two_uris
44
+ @client.track(fake_url)
45
+ restart_client
46
+ @client.track(fake_url + '/fake')
47
+ restart_client
48
+
49
+ assert_equal [fake_url, fake_url + '/fake'], @client.list
50
+ end
51
+
52
+ def test_release_when_tracking_nothing
53
+ assert !@client.release(fake_url), 'Expected to return false when releasing not tracked URI'
54
+ end
55
+
56
+ def test_release_with_tracked_uri
57
+ @client.track(fake_url)
58
+ restart_client
59
+ assert @client.release(fake_url), 'Expected to release tracked URI'
60
+
61
+ restart_client
62
+ assert_equal [], @client.list
63
+ end
64
+
65
+ def restart_client
66
+ @client.close_connection
67
+
68
+ @client = UrlTracker::Client.new(socket_file)
69
+ end
70
+
71
+ end
@@ -0,0 +1,89 @@
1
+ require_relative 'test_helper'
2
+
3
+ class TestSocketCommunication < MiniTest::Unit::TestCase
4
+
5
+ def setup
6
+ @client = Object.new.extend(UrlTracker::SocketCommunication)
7
+ @server = Object.new.extend(UrlTracker::SocketCommunication)
8
+ @path = '/tmp/.dk29ei39kd3.sock'
9
+ remove_socket
10
+ end
11
+
12
+ def teardown
13
+ [@client, @server].each { |o| o.close_connection }
14
+ end
15
+
16
+ def test_connect_to_valid_socket
17
+ @server.bind(@path)
18
+ assert @client.connect(@path), 'Should connect when given a valid socket file'
19
+ end
20
+
21
+ def test_raises_invalid_socket_with_bind_and_connect
22
+ @server.bind(@path)
23
+
24
+ assert_raises(UrlTracker::SocketCommunication::InvalidSocketError) do
25
+ @server.connect(@path)
26
+ end
27
+ end
28
+
29
+ def test_raises_invalid_socket_with_connect_and_bind
30
+ @server.bind(@path)
31
+ @client.connect(@path)
32
+
33
+ assert_raises(UrlTracker::SocketCommunication::InvalidSocketError) do
34
+ @client.bind(@path)
35
+ end
36
+ end
37
+
38
+ def test_communication_client_to_server
39
+ connect_server_and_client
40
+
41
+ @client.write('message')
42
+
43
+ assert_equal 'message', @server.next_message
44
+ end
45
+
46
+ def test_communication_server_to_client
47
+ connect_server_and_client
48
+
49
+ @server.write('message')
50
+
51
+ assert_equal 'message', @client.next_message
52
+ end
53
+
54
+ def test_communication_bidirectional
55
+ connect_server_and_client
56
+
57
+ @server.write('server mesg')
58
+ @client.write('client mesg')
59
+
60
+ assert_equal 'client mesg', @server.next_message
61
+ assert_equal 'server mesg', @client.next_message
62
+ end
63
+
64
+ def test_closing_client_connection_doesnt_remove_socket_file
65
+ connect_server_and_client
66
+ @client.close_connection
67
+
68
+ assert File.exists?(@path), 'Expected socket file to exist when closing client connection'
69
+ end
70
+
71
+ def test_closing_server_connection_removes_socket_file
72
+ connect_server_and_client
73
+ @server.close_connection
74
+
75
+ assert !File.exists?(@path), 'Expected socket file to be removed when server closes connection'
76
+ end
77
+
78
+ def remove_socket
79
+ File.unlink @path if File.exists? @path
80
+ end
81
+
82
+ def connect_server_and_client
83
+ @server.bind(@path)
84
+ t = Thread.new { @server.wait_for_connection }
85
+ @client.connect(@path)
86
+ t.join # server is connected to client
87
+ end
88
+
89
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: url_tracker
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Renato Mascarenhas
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: libnotify
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: notifier
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A simple tool for tracking URLs and getting informed when they are changed.
63
+ email: renato.mascosta@gmail.com
64
+ executables:
65
+ - ut
66
+ - utd
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - lib/url_tracker.rb
71
+ - bin/ut
72
+ - bin/utd
73
+ - test/test_server.rb
74
+ - test/test_periodic.rb
75
+ - test/test_socket_communication.rb
76
+ - test/test_client.rb
77
+ - test/test_page.rb
78
+ - test/test_helper.rb
79
+ homepage:
80
+ licenses:
81
+ - MIT
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ segments:
93
+ - 0
94
+ hash: -2248500158094718750
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ segments:
102
+ - 0
103
+ hash: -2248500158094718750
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 1.8.23
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: A simple tool for tracking URLs and getting informed when they are changed.
110
+ test_files: []