ip_tracker 0.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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source "http://rubygems.org"
2
+
3
+
4
+ platforms :mswin, :mingw do
5
+ gem 'win32console'
6
+ gem 'win32-changenotify'
7
+ gem 'win32-event'
8
+ gem 'rb-readline'
9
+ gem 'rb-notifu'
10
+ end
11
+
12
+ platform :ruby do
13
+ gem 'rb-inotify'
14
+ end
15
+
16
+ gem 'guard', :git => "git://github.com/guard/guard.git"
17
+ gem 'looper', :git => "git@github.com:diedthreetimes/looper.git"
18
+
19
+ # Specify your gem's dependencies in ip_tracker.gemspec
20
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :all_after_pass => false, :cli => '--color', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ end
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/ipme ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ip_tracker'
4
+
5
+ IpTracker::CLI.start
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ip_tracker/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ip_tracker"
7
+ s.version = IpTracker::VERSION
8
+ s.authors = ["Sky Faber"]
9
+ s.email = ["skyfaber@gmail.com"]
10
+ s.homepage = "https://github.com/diedthreetimes/IpTracker"
11
+ s.summary = %q{A command line utility to interface with IpMe}
12
+ s.description = %q{Keep a dynamic IP up to date without the use of DNS. Either sync manually or automatically to a known location.}
13
+
14
+ s.rubyforge_project = "ip_tracker"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec", '~> 2.6'
23
+ s.add_development_dependency "guard"
24
+ s.add_development_dependency "guard-rspec"
25
+ s.add_development_dependency "rb-readline"
26
+ s.add_development_dependency "webmock"
27
+
28
+ s.add_runtime_dependency "thor"
29
+ s.add_runtime_dependency "faraday_middleware"
30
+ s.add_runtime_dependency "yajl-ruby"
31
+ s.add_runtime_dependency "multi_json"
32
+ s.add_runtime_dependency "rash"
33
+ s.add_runtime_dependency "looper"
34
+ end
@@ -0,0 +1,24 @@
1
+ module IpTracker
2
+ class CLI
3
+ desc "register", "register this computer with IpMe"
4
+ method_option :hostname, :type => :string, :desc => "What name to store this hoste under"
5
+ def register
6
+ unless config.host_token
7
+ hostname = options[:hostname] || get_option(:hostname)
8
+
9
+ say "Attempting to register this computer.", :yellow
10
+
11
+ token = client.register(hostname)
12
+ say "Registration completed.", :green
13
+ config.update(:host_token, token)
14
+ else
15
+ say "This computer has already been registered."
16
+ end
17
+ rescue IpTracker::Client::TargetError
18
+ say "Registration failed."
19
+ rescue IpTracker::Client::HostTakenError
20
+ say "You must enter a unique name."
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,44 @@
1
+ module IpTracker
2
+ class CLI
3
+ desc "sync", "continuously sync the ip with IpMe"
4
+ method_option :start, :type => :boolean, :desc => "Command start", :default => true
5
+ method_option :stop, :type => :boolean, :desc => "Command stop", :default => false
6
+ method_option :daemon, :type => :boolean, :aliases => ["-d"], :default => false, :desc => "Run this as a daemon, only works with start"
7
+
8
+ def sync
9
+ command = options[:stop] ? :stop : :start
10
+ daemonize = options[:daemon] && Process.respond_to?(:fork)
11
+
12
+ case command
13
+ when :start
14
+ if config.pid
15
+ say "IpMe is already running."
16
+ else
17
+ if daemonize
18
+ pid = Process.fork do
19
+ SyncDaemon.new.run
20
+ Process.daemon
21
+ end
22
+
23
+ # TODO verify if detach and daemon work together
24
+ config.update(:pid, pid)
25
+ Process.detach(pid)
26
+ else
27
+ SyncDaemon.new.run
28
+ end
29
+ end
30
+ when :stop
31
+ if config.pid
32
+ say "Killing #{config.pid}"
33
+
34
+ Process.kill("TERM", config.pid)
35
+
36
+ say "Killed"
37
+ else
38
+ say "No PID saved"
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,21 @@
1
+ module IpTracker
2
+ class CLI
3
+ desc "update", "update IpMe with the specified IP"
4
+ method_option :ip, :type => :string, :desc => "Which IP to update use", :required => true
5
+ def update
6
+ ip = options[:ip]
7
+ host = config.host_token
8
+ if host.nil?
9
+ say "Please first register this computer."
10
+ else
11
+ say "Attempting to update ip.", :yellow
12
+
13
+ client.update(host, :ip, ip)
14
+
15
+ say "Update completed", :green
16
+ end
17
+ rescue IpTracker::Client::TargetError
18
+ say "Update failed."
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ require 'thor'
2
+
3
+ module IpTracker
4
+ class CLI < Thor
5
+ protected
6
+
7
+ def get_option(option)
8
+ value = ask("Please enter your #{option}:")
9
+ raise Thor::Error, "You must enter a value for that field." if value.empty?
10
+ value
11
+ end
12
+
13
+ def client
14
+ @client ||= IpTracker::Client.new
15
+ end
16
+
17
+ def config
18
+ @config ||= IpTracker::Config.new
19
+ end
20
+ end
21
+ end
22
+
23
+ require 'ip_tracker/cli/methods/register'
24
+ require 'ip_tracker/cli/methods/update'
25
+ require 'ip_tracker/cli/methods/sync'
@@ -0,0 +1,18 @@
1
+ module IpTracker
2
+ class Client
3
+ module Authentication
4
+ attr_accessor :auth_token
5
+ attr_reader :user
6
+
7
+ def login(username, password)
8
+ response = post("#{IpTracker::USERS_PATH}/#{username}/tokens",
9
+ :body => { :password => password })
10
+
11
+ raise TargetError if response.code == 200
12
+
13
+ @user = username
14
+ @auth_token = response.token
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ require'faraday_middleware'
2
+
3
+ module IpTracker
4
+ class Client
5
+ module Connection
6
+ private
7
+
8
+ def connection
9
+ connection = Faraday.new(:url => target_url) do |builder|
10
+ builder.use Faraday::Request::JSON
11
+ builder.use Faraday::Response::Rashify
12
+ builder.use Faraday::Response::ParseJson
13
+
14
+ builder.adapter http_adapter
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ module IpTracker
2
+ class Client
3
+ class HostTakenError < RuntimeError; end
4
+ class TargetError < RuntimeError; end
5
+ end
6
+ end
@@ -0,0 +1,23 @@
1
+ module IpTracker
2
+ class Client
3
+ module Request
4
+ def post(path, options={})
5
+ request(:post, path, options)
6
+ end
7
+
8
+ def put(path, options={})
9
+ request(:put, path, options)
10
+ end
11
+
12
+ private
13
+
14
+ def request(action, path, options)
15
+ response = connection.send(action, path) do |request|
16
+ request.body = options[:body] if options[:body]
17
+ end
18
+
19
+ response.body
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+
2
+ require 'ip_tracker/client/authentication'
3
+ require 'ip_tracker/client/connection'
4
+ require 'ip_tracker/client/errors'
5
+ require 'ip_tracker/client/request'
6
+
7
+ module IpTracker
8
+ class Client
9
+ attr_reader :http_adapter, :target_url
10
+
11
+ def initialize
12
+ @target_url = IpTracker::DEFAULT_LOCAL_TARGET
13
+ @http_adapter = :net_http
14
+ end
15
+
16
+ # include Authentication
17
+ include Connection
18
+ include Request
19
+
20
+ attr_accessor :host_token
21
+
22
+ def register hostname
23
+ response = post(IpTracker::HOSTS_PATH, :body => { name: hostname } )
24
+ raise HostTakenError if response.code == 201
25
+ raise TargetError if response.code == 200 || response.id == nil
26
+
27
+ @host_token = response.id
28
+ end
29
+
30
+ def update host_id, attr, value
31
+ response = put(IpTracker::HOSTS_PATH + "/#{host_id}", :body => {attr.to_sym => value})
32
+
33
+ raise TargetError if response.code == 200 || response.send(attr) != value
34
+
35
+ true
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,32 @@
1
+ module IpTracker
2
+ class Config
3
+ attr_accessor :config_hash, :settings_path
4
+
5
+ def initialize(options={})
6
+ @settings_path = File.expand_path(IpTracker::DEFAULT_CONFIG_PATH)
7
+ @config_hash = load_settings || {}
8
+ end
9
+
10
+ def host_token
11
+ config_hash["host_token"] || nil
12
+ end
13
+
14
+ def pid
15
+ config_hash["pid"] || nil
16
+ end
17
+
18
+ def update(attr, value)
19
+ config_hash[attr.to_s] = value
20
+
21
+ File.open(settings_path, 'w') do |out|
22
+ YAML.dump(config_hash, out)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def load_settings
29
+ File.exists?(settings_path) ? YAML.load_file(settings_path) : nil
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,64 @@
1
+ require 'socket'
2
+ require 'looper'
3
+
4
+ module IpTracker
5
+ class SyncDaemon
6
+ include ::Looper
7
+
8
+ attr_accessor :last_ip
9
+
10
+ def initialize(config = {})
11
+ @run = true
12
+ @runs = config[:runs].nil? ? nil : config[:runs]
13
+ @sleep = config[:sleep].nil? ? 60 : config[:sleep]
14
+ end
15
+
16
+ def run
17
+ loopme(@sleep) do
18
+ if @runs == 0
19
+ @run = false
20
+ elsif !@runs.nil?
21
+ @runs -= 1
22
+ end
23
+
24
+ begin
25
+ new_ip = local_ip
26
+
27
+ if @last_ip != new_ip
28
+ puts "#{Time.now}: Updating to #{new_ip}"
29
+ update_ip new_ip
30
+
31
+ @last_ip = new_ip
32
+ end
33
+ rescue Client::TargetError
34
+ puts "An error occured trying to communicate with the server, sleeping"
35
+ sleep(6000) unless !@runs.nil?
36
+ end
37
+ end
38
+ end
39
+
40
+ def local_ip
41
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
42
+
43
+ UDPSocket.open do |s|
44
+ s.connect '64.233.187.99', 1
45
+ s.addr.last
46
+ end
47
+ ensure
48
+ Socket.do_not_reverse_lookup = orig
49
+ end
50
+
51
+ def update_ip ip
52
+ client.update(config.host_token, :ip, ip)
53
+ # CLI.start( ['update', '--ip', ip] )
54
+ end
55
+
56
+ def config
57
+ @config ||= Config.new
58
+ end
59
+
60
+ def client
61
+ @client ||= Client.new
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module IpTracker
2
+ VERSION = "0.0.1"
3
+ end
data/lib/ip_tracker.rb ADDED
@@ -0,0 +1,13 @@
1
+ require "ip_tracker/version"
2
+
3
+ module IpTracker
4
+ DEFAULT_CONFIG_PATH = '~/.ip_tracker'
5
+ DEFAULT_LOCAL_TARGET = 'http://ipme.herokuapp.com'
6
+ # DEFAULT_LOCAL_TARGET = 'http://localhost:4567'
7
+ HOSTS_PATH = '/hosts'
8
+ end
9
+
10
+ require 'ip_tracker/config'
11
+ require 'ip_tracker/cli'
12
+ require 'ip_tracker/client'
13
+ require 'ip_tracker/sync_daemon'
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 403 Forbidden
2
+ Server: nginx/0.7.65
3
+ Date: Thu, 03 Mar 2011 18:29:40 GMT
4
+ Content-Type: application/json
5
+ Connection: keep-alive
6
+ Keep-Alive: timeout=20
7
+ Content-Length: 52
8
+
9
+ {"code":200,"description":"Operation not permitted"}
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 200 OK
2
+ Server: nginx/0.7.65
3
+ Date: Thu, 03 Mar 2011 18:26:45 GMT
4
+ Content-Type: application/json
5
+ Connection: keep-alive
6
+ Keep-Alive: timeout=20
7
+ Content-Length: 28
8
+
9
+ {"id":null}
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 200 OK
2
+ Server: nginx/0.7.65
3
+ Date: Thu, 03 Mar 2011 18:26:45 GMT
4
+ Content-Type: application/json
5
+ Connection: keep-alive
6
+ Keep-Alive: timeout=20
7
+ Content-Length: 28
8
+
9
+ {"id":6}
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 400 Bad Request
2
+ Server: nginx/0.7.65
3
+ Date: Thu, 03 Mar 2011 18:29:40 GMT
4
+ Content-Type: application/json
5
+ Connection: keep-alive
6
+ Keep-Alive: timeout=20
7
+ Content-Length: 52
8
+
9
+ {"code":201,"description":"Name already taken."}
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 403 Forbidden
2
+ Server: nginx/0.7.65
3
+ Date: Thu, 03 Mar 2011 18:29:40 GMT
4
+ Content-Type: application/json
5
+ Connection: keep-alive
6
+ Keep-Alive: timeout=20
7
+ Content-Length: 52
8
+
9
+ {"code":200,"description":"Operation not permitted"}
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 200 OK
2
+ Server: nginx/0.7.65
3
+ Date: Thu, 03 Mar 2011 18:26:45 GMT
4
+ Content-Type: application/json
5
+ Connection: keep-alive
6
+ Keep-Alive: timeout=20
7
+ Content-Length: 30
8
+
9
+ {"id":7, "ip":"127.0.1.10034"}
@@ -0,0 +1,9 @@
1
+ HTTP/1.1 200 OK
2
+ Server: nginx/0.7.65
3
+ Date: Thu, 03 Mar 2011 18:26:45 GMT
4
+ Content-Type: application/json
5
+ Connection: keep-alive
6
+ Keep-Alive: timeout=20
7
+ Content-Length: 28
8
+
9
+ {"id":7, "ip":"127.0.0.1"}
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+ require 'ip_tracker'
3
+
4
+ describe IpTracker::CLI do
5
+
6
+ describe 'executable' do
7
+ it 'should print usage' do
8
+ `ipme`.should match /Tasks:/
9
+ end
10
+ end
11
+
12
+ describe '#register' do
13
+ context "when there is no hostid token" do
14
+ def stub_client_and_config
15
+ mock_client.should_receive(:register).with('foo') { 'token' }
16
+
17
+ config = mock_config
18
+ config.should_receive(:update).with(:host_token, 'token')
19
+ config.should_receive(:host_token).and_return(nil)
20
+ end
21
+
22
+ context 'and no arguments are provided' do
23
+ let(:register) { IpTracker::CLI.start(['register']) }
24
+
25
+ it "asks for a new hostname" do
26
+ stub_client_and_config
27
+ $stdin.should_receive(:gets).and_return('foo')
28
+
29
+ results = capture(:stdout) { register }
30
+ results.should match /Please enter your hostname:/
31
+ end
32
+
33
+ context "displays an error if" do
34
+ it "hostname is blank" do
35
+ mock_config.should_receive(:host_token).and_return(nil)
36
+
37
+ $stdin.should_receive(:gets).and_return('')
38
+ results = capture(:stderr, :stdout) { register }
39
+ results.should match /must enter a value/
40
+ results.should_not match /success/
41
+ end
42
+
43
+ it "hostname is taken" do
44
+ mock_config.should_receive(:host_token).and_return(nil)
45
+
46
+ $stdin.should_receive(:gets).and_return('')
47
+ results = capture(:stderr, :stdout) { register }
48
+ results.should match /must enter a/
49
+ results.should_not match /success/
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ context "and a hostname is provided" do
56
+ let(:register) do
57
+ IpTracker::CLI.start(["register", "--hostname", "foo"])
58
+ end
59
+
60
+ it "does not ask for hostnme" do
61
+ stub_client_and_config
62
+ $stdin.should_not_receive(:gets)
63
+ results = capture(:stdout) { register }
64
+ results.should match /Attempting to register/
65
+ end
66
+
67
+ context "and hostname will fail" do
68
+ it "displays a failed message if host taken" do
69
+ mock_client.should_receive(:register).
70
+ with('foo') { raise IpTracker::Client::HostTakenError }
71
+ mock_config.should_receive(:host_token).and_return(nil)
72
+
73
+ results = capture(:stdout) { register }
74
+ results.should match /You must enter a unique name./
75
+ end
76
+
77
+ it "displays a failed message if client errors" do
78
+ mock_client.should_receive(:register).
79
+ with('foo') { raise IpTracker::Client::TargetError }
80
+ mock_config.should_receive(:host_token).and_return(nil)
81
+
82
+ results = capture(:stdout) { register }
83
+ results.should match /Registration failed./
84
+
85
+ end
86
+ end
87
+
88
+ context "and hostname will succeed" do
89
+ it "saves a hostid token and displays success" do
90
+ stub_client_and_config
91
+
92
+ results = capture(:stdout) { register }
93
+ results.should match /completed/
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ context "when there is a hostid token" do
100
+ let(:register) { IpTracker::CLI.start(['register']) }
101
+ it "should not ask for hostname and print a message" do
102
+ mock_config.should_receive(:host_token).and_return('token')
103
+ $stdin.should_not_receive(:gets)
104
+ results = capture(:stdout) { register }
105
+ results.should match /This computer has already been registered./
106
+ end
107
+ end
108
+ end # register
109
+ end # CLI
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+ require 'ip_tracker'
3
+
4
+ describe IpTracker::CLI do
5
+ describe '#sync' do
6
+
7
+ def stub_config(pid=nil)
8
+ config = mock_config
9
+ config.should_receive(:update).with(:pid, pid) if !pid.nil?
10
+ config.should_receive(:pid).and_return(nil)
11
+ end
12
+
13
+ def stub_daemon
14
+ daemon = double(IpTracker::SyncDaemon, {})
15
+ IpTracker::SyncDaemon.stub(:new) { daemon }
16
+ daemon.should_receive(:run)
17
+ end
18
+ describe "start" do
19
+ context "when there are no options" do
20
+ let(:sync) { IpTracker::CLI.start(['sync']) }
21
+
22
+ it "should call sync_daemon" do
23
+ stub_config
24
+ stub_daemon
25
+
26
+ sync
27
+ end
28
+
29
+ it "prints an error when a pid is saved" do
30
+ mock_config.should_receive(:pid).and_return(343)
31
+ IpTracker::SyncDaemon.should_not_receive(:new)
32
+
33
+ results = capture(:stdout) { sync }
34
+ results.should match /IpMe is already running./
35
+ end
36
+ end
37
+
38
+ context "when daemonize is provided" do
39
+ let(:sync) { IpTracker::CLI.start(['sync', '--start', '-d']) }
40
+
41
+ it "should detach and store a pid" do
42
+ stub_config(36)
43
+
44
+ Process.should_receive(:fork).and_return(36)
45
+ Process.should_receive(:respond_to?).with(:fork).and_return(true)
46
+ Process.should_receive(:detach)
47
+
48
+ results = capture(:stdout) { sync }
49
+ results.should == ""
50
+
51
+ #TODO: assert output is redirected to a file
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#stop" do
57
+ let(:sync) { IpTracker::CLI.start(['sync', '--stop']) }
58
+ context "when a pid is saved" do
59
+ it "should send sigterm to the daemon and delete the pid" do
60
+ mock_config.should_receive(:pid).at_least(1).times.and_return(36)
61
+ Process.should_receive(:kill).with("TERM", 36)
62
+
63
+ results = capture(:stdout) { sync }
64
+ results.should match /killed/i
65
+ end
66
+ end
67
+
68
+ it "should print a warning when a pid doesn't exist" do
69
+ stub_config
70
+
71
+ results = capture(:stdout) { sync }
72
+ results.should match /no pid/i
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'ip_tracker'
3
+
4
+ describe IpTracker::CLI do
5
+ describe '#update' do
6
+ let(:update) { IpTracker::CLI.start(['update','--ip','127.0.0.1']) }
7
+ context "should display an error if" do
8
+ it "no arguments are provided" do
9
+ mock_config
10
+ mock_client
11
+
12
+ results = capture(:stderr) { IpTracker::CLI.start(['update']) }
13
+ results.should match /No value provided/
14
+ end
15
+
16
+ # TODO: Automatically call register
17
+ it "no hostid token present" do
18
+ mock_config.should_receive(:host_token).and_return nil
19
+ mock_client
20
+
21
+ results = capture(:stdout) { update }
22
+ results.should match /Please first register this computer./
23
+ end
24
+ end
25
+
26
+ context "when a hostid token is present" do
27
+ it "displays a failed message if client errors" do
28
+ mock_client.should_receive(:update).
29
+ with('token', :ip, '127.0.0.1') { raise IpTracker::Client::TargetError }
30
+ mock_config.should_receive(:host_token).and_return('token')
31
+
32
+ results = capture(:stdout) { update }
33
+ results.should match /Update failed./
34
+ end
35
+
36
+ context "and client will succeed" do
37
+ it "displays success" do
38
+ mock_client.should_receive(:update).
39
+ with('token', :ip, '127.0.0.1')
40
+ mock_config.should_receive(:host_token).and_return('token')
41
+
42
+ results = capture(:stdout) { update }
43
+ results.should match /completed/
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+ require 'ip_tracker'
3
+
4
+ describe IpTracker::Client do
5
+ def stub_register(status)
6
+ fixture_file =
7
+ case status
8
+ when :success
9
+ "register_success.txt"
10
+ when :no_name
11
+ "register_taken.txt"
12
+ when :invalid
13
+ "register_invalid.txt"
14
+ else
15
+ "register_failure.txt"
16
+ end
17
+ stub_request(:post, IpTracker::DEFAULT_LOCAL_TARGET + IpTracker::HOSTS_PATH).
18
+ with(:body => {:name => "foo"}).
19
+ to_return(fixture(fixture_file))
20
+ end
21
+
22
+ def stub_update(status)
23
+ fixture_file =
24
+ case status
25
+ when :success
26
+ "update_success.txt"
27
+ when :invalid
28
+ "update_invalid.txt"
29
+ else
30
+ "update_failed.txt"
31
+ end
32
+
33
+ stub_request(:put, IpTracker::DEFAULT_LOCAL_TARGET + IpTracker::HOSTS_PATH+ '/9').
34
+ with(:body => {:ip => "127.0.0.1"}).
35
+ to_return(fixture(fixture_file))
36
+ end
37
+
38
+ #TODO: Refactor target+hosts to be configurable
39
+ describe "#register" do
40
+
41
+ context "when successful" do
42
+
43
+ it "sets and returns the token" do
44
+ stub_register(:success)
45
+
46
+ host_token = subject.register 'foo'
47
+ subject.host_token.should == host_token
48
+ host_token.should == 6
49
+ end
50
+ end
51
+
52
+ context "raises an exception when" do
53
+
54
+ it "failed" do
55
+ stub_register(:failed)
56
+ expect { subject.register('foo') }.to raise_error IpTracker::Client::TargetError
57
+ end
58
+
59
+
60
+ it "name taken" do
61
+ stub_register(:no_name)
62
+ expect { subject.register('foo') }.to raise_error IpTracker::Client::HostTakenError
63
+ end
64
+
65
+ it "response invalid" do
66
+ stub_register(:invalid)
67
+ expect { subject.register('foo') }.to raise_error IpTracker::Client::TargetError
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "#update" do
73
+ context "when successful" do
74
+ it "returns true" do
75
+ stub_update(:success)
76
+ subject.update(9, :ip, '127.0.0.1').should == true
77
+ end
78
+ end
79
+
80
+ context "raises an exception when" do
81
+ it "response failed " do
82
+ stub_update(:failed)
83
+ expect { subject.update(9, :ip, '127.0.0.1') }.to raise_error IpTracker::Client::TargetError
84
+ end
85
+
86
+ it "response invalid" do
87
+ stub_update(:invalid)
88
+ expect { subject.update(9, :ip, '127.0.0.1') }.to raise_error IpTracker::Client::TargetError
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'ip_tracker'
3
+
4
+ describe IpTracker::Config do
5
+ let(:path) { File.expand_path(IpTracker::DEFAULT_CONFIG_PATH) }
6
+ let(:io) { StringIO.new }
7
+
8
+ # TODO refactor tests to use settings once configurable
9
+
10
+ def stub_config_file(contents=nil)
11
+ File.stub(:exists?).with(path) { contents ? true : false }
12
+ YAML.stub(:load_file).with(path) { contents } if contents
13
+ File.stub(:open).with(path, 'w').and_yield(io)
14
+ end
15
+
16
+ describe "#update" do
17
+ context "when a .ip_tracker file doesn't exist" do
18
+ it "creates the file and can add a new token" do
19
+ expected_token = 'new_token'
20
+
21
+ stub_config_file(nil)
22
+ YAML.should_receive(:dump).with({"host_token" => expected_token}, io)
23
+
24
+ config = IpTracker::Config.new
25
+ config.update(:host_token, 'new_token')
26
+ config.host_token.should == expected_token
27
+ end
28
+ end
29
+
30
+ context "when a .ip_tracker file exists" do
31
+ it "can update tokens in the .ip_tracker file" do
32
+ expected_token = 'new_token'
33
+ old_token = 'old_token'
34
+
35
+ stub_config_file("host_token" => old_token)
36
+ YAML.should_receive(:dump).with({"host_token" => expected_token}, io)
37
+
38
+ config = IpTracker::Config.new
39
+ config.host_token.should == old_token
40
+ config.update(:host_token, 'new_token')
41
+ config.host_token.should == expected_token
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'ip_tracker'
3
+
4
+ describe IpTracker::SyncDaemon do
5
+ describe "#run" do
6
+ let(:run) { IpTracker::SyncDaemon.new( runs: 5, sleep: 1 ).run }
7
+ let(:mock_ip) { "127.0.0.1" }
8
+
9
+ it "should update the ip exactly once" do
10
+ UDPSocket.should_receive(:open).exactly(6).times.and_return(mock_ip)
11
+
12
+ mock_config.should_receive(:host_token).and_return(6)
13
+ mock_client.should_receive(:update).with(6, :ip, mock_ip)
14
+
15
+ results = capture(:stdout) { run }
16
+ results.should match /process started/
17
+ results.should_not match /error/
18
+ end
19
+
20
+ it "should degrade gracefully upon error" do
21
+ UDPSocket.should_receive(:open).exactly(6).times.and_return(mock_ip)
22
+
23
+ mock_config.should_receive(:host_token).at_least(1).times.and_return(6)
24
+ mock_client.should_receive(:update).at_least(1).times.with(6, :ip, mock_ip) { raise IpTracker::Client::TargetError }
25
+
26
+ results = capture(:stdout){ run }
27
+ results.should match /error/
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'ip_tracker'
5
+
6
+ require 'webmock/rspec'
7
+
8
+ RSpec.configure do |config|
9
+
10
+ end
11
+
12
+ def fixture_path
13
+ File.expand_path("../fixtures", __FILE__)
14
+ end
15
+
16
+ def fixture(file)
17
+ File.new(fixture_path + '/' + file)
18
+ end
19
+
20
+ def capture(*streams)
21
+ streams = [*streams]
22
+ begin
23
+ streams.each { |stream|
24
+ stream = stream.to_s
25
+ eval "$#{stream} = StringIO.new"
26
+ }
27
+
28
+ yield
29
+
30
+ result = streams.map { |stream|
31
+ eval("$#{stream}").string
32
+ }.inject(:+)
33
+ ensure
34
+ streams.each { |stream|
35
+ eval("$#{stream} = #{stream.upcase}")
36
+ }
37
+ end
38
+
39
+ result
40
+ end
41
+
42
+
43
+ def mock_client(stubs={})
44
+ client = double(IpTracker::Client, stubs)
45
+ IpTracker::Client.stub(:new) { client }
46
+ client
47
+ end
48
+
49
+ def mock_config(stubs={})
50
+ config = double(IpTracker::Config, stubs)
51
+ IpTracker::Config.stub(:new) { config }
52
+ config
53
+ end
54
+
metadata ADDED
@@ -0,0 +1,203 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ip_tracker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sky Faber
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-04 00:00:00.000000000 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: &16815264 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '2.6'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *16815264
26
+ - !ruby/object:Gem::Dependency
27
+ name: guard
28
+ requirement: &16815012 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *16815012
37
+ - !ruby/object:Gem::Dependency
38
+ name: guard-rspec
39
+ requirement: &16814736 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *16814736
48
+ - !ruby/object:Gem::Dependency
49
+ name: rb-readline
50
+ requirement: &16814484 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *16814484
59
+ - !ruby/object:Gem::Dependency
60
+ name: webmock
61
+ requirement: &16814232 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *16814232
70
+ - !ruby/object:Gem::Dependency
71
+ name: thor
72
+ requirement: &16813980 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: *16813980
81
+ - !ruby/object:Gem::Dependency
82
+ name: faraday_middleware
83
+ requirement: &16813728 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: *16813728
92
+ - !ruby/object:Gem::Dependency
93
+ name: yajl-ruby
94
+ requirement: &22777668 !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ type: :runtime
101
+ prerelease: false
102
+ version_requirements: *22777668
103
+ - !ruby/object:Gem::Dependency
104
+ name: multi_json
105
+ requirement: &22777416 !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ type: :runtime
112
+ prerelease: false
113
+ version_requirements: *22777416
114
+ - !ruby/object:Gem::Dependency
115
+ name: rash
116
+ requirement: &22777164 !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ type: :runtime
123
+ prerelease: false
124
+ version_requirements: *22777164
125
+ - !ruby/object:Gem::Dependency
126
+ name: looper
127
+ requirement: &22776912 !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :runtime
134
+ prerelease: false
135
+ version_requirements: *22776912
136
+ description: Keep a dynamic IP up to date without the use of DNS. Either sync manually
137
+ or automatically to a known location.
138
+ email:
139
+ - skyfaber@gmail.com
140
+ executables:
141
+ - ipme
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - .gitignore
146
+ - Gemfile
147
+ - Guardfile
148
+ - Rakefile
149
+ - bin/ipme
150
+ - ip_tracker.gemspec
151
+ - lib/ip_tracker.rb
152
+ - lib/ip_tracker/cli.rb
153
+ - lib/ip_tracker/cli/methods/register.rb
154
+ - lib/ip_tracker/cli/methods/sync.rb
155
+ - lib/ip_tracker/cli/methods/update.rb
156
+ - lib/ip_tracker/client.rb
157
+ - lib/ip_tracker/client/authentication.rb
158
+ - lib/ip_tracker/client/connection.rb
159
+ - lib/ip_tracker/client/errors.rb
160
+ - lib/ip_tracker/client/request.rb
161
+ - lib/ip_tracker/config.rb
162
+ - lib/ip_tracker/sync_daemon.rb
163
+ - lib/ip_tracker/version.rb
164
+ - spec/fixtures/register_failure.txt
165
+ - spec/fixtures/register_invalid.txt
166
+ - spec/fixtures/register_success.txt
167
+ - spec/fixtures/register_taken.txt
168
+ - spec/fixtures/update_failed.txt
169
+ - spec/fixtures/update_invalid.txt
170
+ - spec/fixtures/update_success.txt
171
+ - spec/ip_tracker/cli/methods/register_spec.rb
172
+ - spec/ip_tracker/cli/methods/sync_spec.rb
173
+ - spec/ip_tracker/cli/methods/update_spec.rb
174
+ - spec/ip_tracker/client_spec.rb
175
+ - spec/ip_tracker/config_spec.rb
176
+ - spec/ip_tracker/sync_daemon_spec.rb
177
+ - spec/spec_helper.rb
178
+ has_rdoc: true
179
+ homepage: https://github.com/diedthreetimes/IpTracker
180
+ licenses: []
181
+ post_install_message:
182
+ rdoc_options: []
183
+ require_paths:
184
+ - lib
185
+ required_ruby_version: !ruby/object:Gem::Requirement
186
+ none: false
187
+ requirements:
188
+ - - ! '>='
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ required_rubygems_version: !ruby/object:Gem::Requirement
192
+ none: false
193
+ requirements:
194
+ - - ! '>='
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ requirements: []
198
+ rubyforge_project: ip_tracker
199
+ rubygems_version: 1.6.2
200
+ signing_key:
201
+ specification_version: 3
202
+ summary: A command line utility to interface with IpMe
203
+ test_files: []