rhosts 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 044fbb1e205d8759c29fa0381afa740ad7352d99
4
+ data.tar.gz: 2154e8b6d3f7a40365cd381321cf6cc4685e90fc
5
+ SHA512:
6
+ metadata.gz: b99163fd4aa43d5dd534844858092a7cee61825abff17c87a258014508e247c1b7fec709333ddd5757f645c18269b205641ec7fb695bd650ba37ff76a573ec5d
7
+ data.tar.gz: 46b2de6da8141d881deab5e82cff345026a8e3ab8ad0c97e1f5a43872f811941efac12937505cb11b0ab0ec08a1813b4d2c82057b9dd6a7c1d13a156d4e5edce
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Takeshi Takizawa
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ RHosts
2
+ ====
3
+
4
+ hosts file manager
5
+
6
+ # Description
7
+
8
+ rhosts is a command that facilitates the management of the host file.
9
+
10
+ # Usage
11
+ ## show all hosts
12
+ ```
13
+ $ rhosts
14
+ rhosts> all
15
+ ### actives
16
+ 127.0.0.1
17
+ localhost
18
+ dev-www.example.com
19
+
20
+ 127.0.0.2
21
+ stg-www.example.com
22
+
23
+ ### inactives
24
+ 127.0.0.1
25
+ stg-www.example.com
26
+
27
+ ```
28
+
29
+ ## show active hosts
30
+ ```
31
+ rhosts> actives
32
+ ### actives
33
+ 127.0.0.1
34
+ localhost
35
+ dev-www.example.com
36
+
37
+ 127.0.0.2
38
+ stg-www.example.com
39
+
40
+ ```
41
+
42
+ ## show inactive hosts
43
+ ```
44
+ rhosts> inactives
45
+ ### inactives
46
+ 127.0.0.1
47
+ stg-www.example.com
48
+
49
+ ```
50
+
51
+ ## map specific host
52
+ ```
53
+ $ rhosts
54
+ rhosts> actives
55
+ ### actives
56
+ 127.0.0.1
57
+ localhost
58
+ dev-www.example.com
59
+
60
+ 127.0.0.2
61
+ stg-www.example.com
62
+
63
+ rhosts> map "dev-www.example.co.jp" => "127.0.0.1"
64
+ rhosts> actives
65
+ ### actives
66
+ 127.0.0.1
67
+ localhost
68
+ dev-www.example.com
69
+ dev-www.example.co.jp
70
+
71
+ 127.0.0.2
72
+ stg-www.example.com
73
+
74
+ ```
75
+
76
+ ## unmap specific host
77
+ ```
78
+ $ rhosts
79
+ rhosts> actives
80
+ ### actives
81
+ 127.0.0.1
82
+ localhost
83
+ dev-www.example.com
84
+
85
+ 127.0.0.2
86
+ stg-www.example.com
87
+
88
+ rhosts> unmap "dev-www.example.com" => "127.0.0.1"
89
+ rhosts> actives
90
+ ### actives
91
+ 127.0.0.1
92
+ localhost
93
+
94
+ 127.0.0.2
95
+ stg-www.example.com
96
+
97
+ ```
data/bin/rhosts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_path = File.expand_path('../../lib', __FILE__)
4
+ $:.unshift(lib_path) unless $:.include?(lib_path)
5
+
6
+ require "rhosts/cli"
@@ -0,0 +1,41 @@
1
+ module RHosts
2
+ module Alias
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ def alias_ips
8
+ self.class.alias_ips
9
+ end
10
+
11
+ def alias_ip(new_name, old_name)
12
+ self.class.alias_ips[new_name] = old_name
13
+ end
14
+
15
+ def alias_hosts
16
+ self.class.alias_hosts
17
+ end
18
+
19
+ def alias_host(new_name, old_name)
20
+ self.class.alias_hosts[new_name] = old_name
21
+ end
22
+
23
+ module ClassMethods
24
+ def alias_ips
25
+ @alias_ips ||= {}
26
+ end
27
+
28
+ def alias_ip(new_name, old_name)
29
+ alias_ips[new_name] = old_name
30
+ end
31
+
32
+ def alias_hosts
33
+ @alias_hosts ||= {}
34
+ end
35
+
36
+ def alias_host(new_name, old_name)
37
+ alias_hosts[new_name] = old_name
38
+ end
39
+ end
40
+ end
41
+ end
data/lib/rhosts/cli.rb ADDED
@@ -0,0 +1,8 @@
1
+ Signal.trap("INT") { puts; exit(1) }
2
+
3
+ require 'rhosts'
4
+
5
+ # TODO: Runnable not only console but also exec from file, string and so on.
6
+ require 'rhosts/commands/console'
7
+
8
+ RHosts::Console.start
@@ -0,0 +1,57 @@
1
+ require 'readline'
2
+ require 'rhosts/filer'
3
+ require 'rhosts/console/app'
4
+ require 'rhosts/rulable'
5
+ require 'rhosts/alias'
6
+
7
+ module RHosts
8
+ class Console
9
+ include RHosts::ConsoleMethods
10
+ include RHosts::Rulable
11
+ include RHosts::Alias
12
+
13
+ alias_host 'exp', 'www.example.com'
14
+ alias_ip 'localhost', '127.0.0.1'
15
+
16
+ class << self
17
+ def start
18
+ @console = new
19
+
20
+ load_default_rules
21
+ load_run_command
22
+
23
+ unless File.writable?(RHosts.config.hosts_file_path)
24
+ STDERR.puts "Hosts file is not writable. Please check permission"
25
+ exit 1
26
+ end
27
+
28
+ @console.start
29
+ end
30
+
31
+ private
32
+ def load_default_rules
33
+ default_rules = File.read(RHosts.root + '/rhosts/console/default_rules.rb')
34
+ @console.instance_eval(default_rules)
35
+ end
36
+
37
+ def load_run_command
38
+ rhostsrc = File.join(File.expand_path("~"), ".rhostsrc")
39
+ if File.exist?(rhostsrc)
40
+ puts "load: #{rhostsrc}"
41
+ @console.instance_eval(File.read(rhostsrc))
42
+ end
43
+ end
44
+ end
45
+
46
+ def initialize
47
+ @actives, @inactives = RHosts::Filer.load
48
+ end
49
+
50
+ def start
51
+ while command = Readline.readline('rhosts> ', true)
52
+ # call matched rule with captures
53
+ rules.each{ |rule, action| action.call($~.captures) if rule.match command.chomp }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,9 @@
1
+ module RHosts
2
+ class Configuration
3
+ attr_accessor :hosts_file_path, :backup_dir, :make_backup
4
+
5
+ def make_backup?
6
+ make_backup
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,74 @@
1
+ require 'ipaddress'
2
+
3
+ module RHosts
4
+ module ConsoleMethods
5
+ def actives
6
+ @actives ||= Hash.new{ |h, k| h[k] = Set.new }
7
+ end
8
+
9
+ def inactives
10
+ @inactives ||= Hash.new{ |h, k| h[k] = Set.new }
11
+ end
12
+
13
+ def map(target)
14
+ process(target) do |host, ip|
15
+ unless inactives[ip].empty?
16
+ inactives[ip].delete_if{ |h| h == host }
17
+ inactives.delete(ip) if inactives[ip].empty?
18
+ end
19
+
20
+ actives[ip] << host
21
+ puts "map #{host} to #{ip}"
22
+ end
23
+ end
24
+
25
+ def unmap(target)
26
+ process(target) do |host, ip|
27
+ unless actives[ip].empty?
28
+ actives[ip].delete_if{ |h| h == host }
29
+ actives.delete(ip) if actives[ip].empty?
30
+ end
31
+
32
+ inactives[ip] << host
33
+ puts "unmap #{host} from #{ip}"
34
+ end
35
+ end
36
+
37
+ # print mappings
38
+ def display(title, mappings)
39
+ puts "### #{title}"
40
+ mappings.each do |ip, hosts|
41
+ puts ip
42
+ hosts.each{ |host| puts " #{host}" }
43
+ puts ''
44
+ end
45
+ end
46
+
47
+ private
48
+ def process(target, &block)
49
+ raise ArgumentsError.new('mapping target must be Hash') unless target.instance_of? Hash
50
+
51
+ # TODO
52
+ # before_actions.each{ |action| action.call }
53
+
54
+ target.each do |host, ip|
55
+ host = alias_hosts[host] || host
56
+ ip = alias_ips[ip] || ip
57
+
58
+ ip_without_zone_index = ip.split('%')[0]
59
+ unless IPAddress.valid?(ip_without_zone_index)
60
+ STDERR.puts "#{ip} is invalid IP Address!"
61
+ next
62
+ end
63
+
64
+ block.call(host, ip)
65
+ end
66
+
67
+ RHosts::Filer.backup if RHosts.config.make_backup?
68
+ RHosts::Filer.save(actives, inactives)
69
+
70
+ # TODO
71
+ # after_actions.each{ |action| action.call }
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,46 @@
1
+ # print all mappings
2
+ rule /^(A|all)$/ do
3
+ display 'actives', actives
4
+ display 'inactives', inactives
5
+ end
6
+
7
+ # print only active mappings
8
+ rule /^(a|actives)$/ do
9
+ display 'actives', actives
10
+ end
11
+
12
+ # print only inactive mappings
13
+ rule /^(i|inactives)$/ do
14
+ display 'inactives', inactives
15
+ end
16
+
17
+ # add mapping
18
+ rule /^(m|map) +(.*?) +(.*?)$/ do |command, host, ip|
19
+ map host => ip
20
+ end
21
+
22
+ # add unmapping
23
+ rule /^(u|unmap) +(.*?) +(.*?)$/ do |command, host, ip|
24
+ unmap host => ip
25
+ end
26
+
27
+ # print command history
28
+ rule /^(hist|history)$/ do
29
+ puts Readline::HISTORY.to_a.join("\n")
30
+ end
31
+
32
+ # print registored rules
33
+ rule /^rules$/ do
34
+ puts rules.keys.join("\n")
35
+ end
36
+
37
+ # print help
38
+ rule /^(h|help)$/ do
39
+ # TODO: help message
40
+ #help
41
+ end
42
+
43
+ # quit rhosts
44
+ rule /^(q|quit|exit)$/ do
45
+ exit
46
+ end
@@ -0,0 +1,91 @@
1
+ require 'ipaddress'
2
+
3
+ module RHosts
4
+ module Filer
5
+ class << self
6
+ def load
7
+ actives = Hash.new{ |h, k| h[k] = Set.new }
8
+ inactives = Hash.new{ |h, k| h[k] = Set.new }
9
+
10
+ File.open(RHosts.config.hosts_file_path, 'r') do |file|
11
+ file.each do |line|
12
+ storage = Mapping.active?(line) ? actives : inactives
13
+
14
+ Mapping.parse(line) do |ip, hosts|
15
+ next if ip.nil? or hosts.empty?
16
+
17
+ # IPAddress gem can't parse IP with zone index
18
+ #
19
+ # for example
20
+ # fe80::1%lo0
21
+ ip_without_zone_index = ip.split('%')[0]
22
+ next unless IPAddress.valid?(ip_without_zone_index)
23
+
24
+ storage[ip] ||= []
25
+ storage[ip] += hosts
26
+ end
27
+ end
28
+ end
29
+
30
+ [actives, inactives]
31
+ end
32
+
33
+ def backup
34
+ bk_file_path = backup_file_path
35
+ hosts_file_path = RHosts.config.hosts_file_path
36
+
37
+ if File.writable?(RHosts.config.backup_dir)
38
+ FileUtils.cp(hosts_file_path, bk_file_path)
39
+ puts "backup: #{bk_file_path}"
40
+ else
41
+ STDERR.puts "backup file is not writable. #{bk_file_path}"
42
+ STDERR.puts 'So we will backup to tmp dir'
43
+ tmp = tmp_file_path
44
+ FileUtils.cp(hosts_file_path, tmp)
45
+ STDERR.puts "backup: #{tmp}"
46
+ end
47
+ end
48
+
49
+ def save(actives, inactives)
50
+ # TODO: reload hosts file if chnaged after load
51
+ hosts_file_path = RHosts.config.hosts_file_path
52
+ unless File.writable?(hosts_file_path)
53
+ STDERR.puts "Hosts file is not writable. Please check permission"
54
+ exit 1
55
+ end
56
+
57
+ File.open(RHosts.config.hosts_file_path, 'w') do |file|
58
+ actives.each{ |ip, hosts| file.write("#{ip} #{hosts.to_a.join(' ')}\n") }
59
+ inactives.each{ |ip, hosts| file.write("##{ip} #{hosts.to_a.join(' ')}\n") }
60
+ end
61
+ puts "save: #{hosts_file_path}"
62
+ end
63
+
64
+ private
65
+ def backup_file_path
66
+ hosts_file_path = RHosts.config.hosts_file_path
67
+ basename = File.basename(hosts_file_path)
68
+ File.join(RHosts.config.backup_dir, "#{basename}.#{Time.now.to_i}")
69
+ end
70
+
71
+ def tmp_file_path
72
+ hosts_file_path = RHosts.config.hosts_file_path
73
+ basename = File.basename(hosts_file_path)
74
+ File.join('/tmp', "#{basename}.#{Time.now.to_i}")
75
+ end
76
+ end
77
+
78
+ class Mapping
79
+ class << self
80
+ def active?(line)
81
+ line !~ /^#/
82
+ end
83
+
84
+ def parse(line, &block)
85
+ ip, *hosts = line.chomp.sub(/^#+\s*/, '').split(/\s+/)
86
+ block.call(ip, hosts)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,11 @@
1
+ module RHosts
2
+ module Rulable
3
+ def rules
4
+ @rules ||= {}
5
+ end
6
+
7
+ def rule(pattern, &block)
8
+ rules[pattern] = block
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module RHosts # :nodoc:
2
+ module Version # :nodoc:
3
+ STRING = '0.0.1'
4
+ end
5
+ end
data/lib/rhosts.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ lib_path = File.dirname(__FILE__)
5
+ $:.unshift(lib_path) unless $:.include?(lib_path)
6
+
7
+ require 'rhosts/configuration'
8
+
9
+ module RHosts
10
+ def self.root
11
+ @root ||= File.dirname(__FILE__)
12
+ end
13
+
14
+ def self.config
15
+ @config ||= RHosts::Configuration.new
16
+ end
17
+
18
+ def self.configure
19
+ yield config if block_given?
20
+ end
21
+ end
22
+
23
+ # default setting
24
+ RHosts.configure do |c|
25
+ c.hosts_file_path = '/etc/hosts'
26
+ c.backup_dir = '/etc'
27
+ c.make_backup = true
28
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'rhosts/console/app'
3
+
4
+ describe 'ConsoleMethods' do
5
+ include RHosts::ConsoleMethods
6
+
7
+ describe '#map' do
8
+ before do
9
+ map 'example.com' => '127.0.0.1'
10
+ end
11
+
12
+ after do
13
+ @actives.clear
14
+ @inactives.clear
15
+ end
16
+
17
+ it 'map example.com to 127.0.0.1' do
18
+ expect(actives).to eq('127.0.0.1' => ['example.com'])
19
+ expect(inactives).to eq({ })
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../../lib/rhosts', __FILE__)
2
+
3
+ # This file was generated by the `rspec --init` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # Require this file using `require "spec_helper"` to ensure that it is only
6
+ # loaded once.
7
+ #
8
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
9
+ RSpec.configure do |config|
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ config.run_all_when_everything_filtered = true
12
+ config.filter_run :focus
13
+
14
+ # Run specs in random order to surface order dependencies. If you find an
15
+ # order dependency and want to debug it, you can fix the order by providing
16
+ # the seed, which is printed after each run.
17
+ # --seed 1234
18
+ config.order = 'random'
19
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rhosts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Takeshi Takizawa
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ipaddress
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.0
27
+ description: hosts file manager
28
+ email: TakiTake.create@gmail.com
29
+ executables:
30
+ - rhosts
31
+ extensions: []
32
+ extra_rdoc_files:
33
+ - README.md
34
+ files:
35
+ - lib/rhosts.rb
36
+ - lib/rhosts/alias.rb
37
+ - lib/rhosts/cli.rb
38
+ - lib/rhosts/commands/console.rb
39
+ - lib/rhosts/configuration.rb
40
+ - lib/rhosts/console/app.rb
41
+ - lib/rhosts/console/default_rules.rb
42
+ - lib/rhosts/filer.rb
43
+ - lib/rhosts/rulable.rb
44
+ - lib/rhosts/version.rb
45
+ - License.txt
46
+ - README.md
47
+ - spec/console/app_spec.rb
48
+ - spec/spec_helper.rb
49
+ - bin/rhosts
50
+ homepage: http://github.com/TakiTake/rhosts
51
+ licenses:
52
+ - MIT
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --charset=UTF-8
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project: rhosts
71
+ rubygems_version: 2.0.3
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: rhosts-0.0.1
75
+ test_files:
76
+ - spec/console/app_spec.rb
77
+ - spec/spec_helper.rb