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 +5 -0
- data/bin/utd +5 -0
- data/lib/url_tracker.rb +70 -0
- data/test/test_client.rb +88 -0
- data/test/test_helper.rb +42 -0
- data/test/test_page.rb +52 -0
- data/test/test_periodic.rb +78 -0
- data/test/test_server.rb +71 -0
- data/test/test_socket_communication.rb +89 -0
- metadata +110 -0
data/bin/ut
ADDED
data/bin/utd
ADDED
data/lib/url_tracker.rb
ADDED
|
@@ -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
|
data/test/test_client.rb
ADDED
|
@@ -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
|
data/test/test_helper.rb
ADDED
|
@@ -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
|
data/test/test_server.rb
ADDED
|
@@ -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: []
|