to_pass 0.2.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -24,20 +24,24 @@ rescue LoadError
24
24
  puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
25
25
  end
26
26
 
27
- %w[ rake/rdoctask sdoc ].each { |lib| require lib }
28
- Rake::RDocTask.new do |rdoc|
29
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
30
-
31
- rdoc.rdoc_dir = 'doc/rdoc'
32
- rdoc.title = "to_pass #{version}"
33
- rdoc.options << '--fmt' << 'shtml'
34
- rdoc.template = 'direct'
35
- rdoc.rdoc_files.include('README*')
36
- rdoc.rdoc_files.include('lib/**/*.rb')
27
+ # documentation task
28
+ begin
29
+ %w[ rake/rdoctask sdoc ].each { |lib| require lib }
30
+ Rake::RDocTask.new do |rdoc|
31
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
32
+
33
+ rdoc.rdoc_dir = 'doc/rdoc'
34
+ rdoc.title = "to_pass #{version}"
35
+ rdoc.options << '--fmt' << 'shtml'
36
+ rdoc.template = 'direct'
37
+ rdoc.rdoc_files.include('README*')
38
+ rdoc.rdoc_files.include('lib/**/*.rb')
39
+ end
40
+ rescue LoadError
37
41
  end
38
42
 
39
43
  desc "run tests"
40
- task :test, :needs => [:check_dependencies] do
44
+ task :test do
41
45
  # optional libraries
42
46
  %w[ redgreen ].each do |lib|
43
47
  begin
@@ -50,4 +54,32 @@ task :test, :needs => [:check_dependencies] do
50
54
  end
51
55
  end
52
56
 
57
+ desc "list available algorithms"
58
+ task :algorithms, :needs => [:to_pass] do
59
+ puts ""
60
+ puts " available password algorithms"
61
+ puts " ============================================"
62
+ ToPass::AlgorithmReader.discover.each do |algorithm|
63
+ puts " - #{algorithm}"
64
+ end
65
+ puts " ============================================"
66
+ puts ""
67
+ end
68
+
69
+ desc "list available converters"
70
+ task :converters, :needs => [:to_pass] do
71
+ puts ""
72
+ puts " available converters for password algorithms"
73
+ puts " ============================================"
74
+ ToPass::ConverterReader.new.discover.each do |converter|
75
+ puts " - #{converter}"
76
+ end
77
+ puts " ============================================"
78
+ puts ""
79
+ end
80
+
81
+ task :to_pass do
82
+ require 'lib/to_pass'
83
+ end
84
+
53
85
  task :default => :test
data/TODO CHANGED
@@ -1,9 +1,28 @@
1
1
  UP NEXT
2
2
  ============================================================================
3
- - test and enble class methods of Converter-class
3
+ - make it debian compatible (most of which is common sense anyway)
4
+ http://pkg-ruby-extras.alioth.debian.org/upstream-devs.html
5
+ - Distribute your software as a source code archive
6
+ - Use setup.rb
7
+ - Remove all references to rubygems in the software you ship
8
+ - Don’t make your Rakefile depend on RubyGems
9
+ - Make your tests and examples usable outside of your directory tree
10
+ - Use a shebang that works everywhere
11
+ - Provide man pages for your binaries
12
+ - If possible, maintain a backward-compatible API
13
+
14
+ - Man-Pages http://www.linuxhowtos.org/System/creatingman.htm
15
+ order:
16
+ NAME
17
+ SYNOPSIS
18
+ DESCRIPTION
19
+ OPTIONS
20
+ BUGS
21
+ AUTHOR
22
+ SEE ALSO
23
+
4
24
 
5
25
  SOMEDAY
6
26
  ============================================================================
7
27
  - move decision wether a string is a word or not into algorithm definition.
8
- - make conversion-functions more pluggable
9
28
  - test on windows
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.4.0
@@ -4,7 +4,7 @@
4
4
  require 'pathname'
5
5
  require 'yaml'
6
6
 
7
- # The AlgorithmReader's primary API is the load the rules from a YAML-file
7
+ # The AlgorithmReader's primary API is to load the rules from a YAML-file
8
8
  # into a Hash.
9
9
  #
10
10
  # Algorithms are searched in the following locations
@@ -20,7 +20,7 @@ class ToPass::AlgorithmReader
20
20
  @load_path = []
21
21
  [
22
22
  '~/.to_pass/algorithms',
23
- "#{File.dirname(__FILE__)}/../algorithms"
23
+ "#{File.dirname(__FILE__)}/algorithms"
24
24
  ].each do |dir|
25
25
  dir = Pathname.new(dir).expand_path
26
26
  @load_path << dir if dir.exist?
@@ -32,6 +32,15 @@ class ToPass::AlgorithmReader
32
32
  def load(algorithm)
33
33
  new(algorithm).load_from_file
34
34
  end
35
+
36
+ # searches for available algorithms
37
+ def discover
38
+ new(nil).load_path.collect do |dir|
39
+ Dir["#{dir}/*.yml"]
40
+ end.flatten.compact.map do |fn|
41
+ File.basename(fn).gsub('.yml', '')
42
+ end
43
+ end
35
44
  end
36
45
 
37
46
  def load_from_file # :nodoc:
@@ -1,14 +1,14 @@
1
1
  desc: Basic Algorithm with a german usage in mind
2
2
  name: Basic (german)
3
3
  sentence:
4
- - table: words
4
+ - replace: words
5
5
  - first_chars
6
- - table: symbols
6
+ - replace: symbols
7
7
  - collapse_chars
8
8
  word:
9
- - table: chars
10
- - table: symbols
11
- tables:
9
+ - replace: chars
10
+ - replace: symbols
11
+ replacements:
12
12
  words:
13
13
  ein: 1
14
14
  eine: 1
@@ -1,14 +1,14 @@
1
1
  desc: Basic Algorithm with a english usage in mind
2
2
  name: Basic (english)
3
3
  sentence:
4
- - table: words
4
+ - replace: words
5
5
  - first_chars
6
- - table: symbols
6
+ - replace: symbols
7
7
  - collapse_chars
8
8
  word:
9
- - table: chars
10
- - table: symbols
11
- tables:
9
+ - replace: chars
10
+ - replace: symbols
11
+ replacements:
12
12
  words:
13
13
  one: 1
14
14
  single: 1
data/lib/to_pass/cli.rb CHANGED
@@ -12,7 +12,7 @@ module ToPass
12
12
  end
13
13
 
14
14
  def output
15
- if @options[:pipe_usage]
15
+ if @options[:pipe_out]
16
16
  $stdout << @password
17
17
  else
18
18
  puts @password
@@ -24,7 +24,9 @@ module ToPass
24
24
  # parse the options
25
25
  def parse_options
26
26
  options = {
27
- :algorithm => 'basic_de'
27
+ :algorithm => 'basic_de',
28
+ :pipe_out => false,
29
+ :pipe_in => false
28
30
  }
29
31
 
30
32
  OptionParser.new do |opts|
@@ -35,6 +37,10 @@ module ToPass
35
37
  options[:algorithm] = value
36
38
  end
37
39
 
40
+ opts.on('-p', '--[no-]pipe', "pipe result to stdout (without trailing linebreak)") do |value|
41
+ options[:pipe_out] = value
42
+ end
43
+
38
44
  opts.separator ""
39
45
 
40
46
  opts.on_tail("-h", "--help", "Show this message") do
@@ -43,14 +49,16 @@ module ToPass
43
49
  end
44
50
  end.parse!
45
51
 
46
- options[:pipe_usage] = ARGV[0].nil?
52
+ if ARGV[0].nil?
53
+ options[:pipe_in] = options[:pipe_out] = true
54
+ end
47
55
 
48
56
  options
49
57
  end
50
58
 
51
59
  # get the input string
52
60
  def get_input_string
53
- unless @options[:pipe_usage]
61
+ unless @options[:pipe_in]
54
62
  ARGV[0]
55
63
  else
56
64
  $stdin.read.chomp
@@ -10,9 +10,12 @@ module ToPass
10
10
  # a more complete description of the algorithm capabilities
11
11
  # is still pending.
12
12
  class Converter
13
+ attr_accessor :converters, :rules
13
14
  # create a new converter, based on a set of conversion-rules
14
15
  def initialize( rules )
15
16
  @rules = rules
17
+ @reader = ConverterReader.new
18
+ @converters = @reader.discover
16
19
  end
17
20
 
18
21
  # convert a string into a password
@@ -31,9 +34,16 @@ module ToPass
31
34
  # end
32
35
  # end
33
36
 
34
- protected
35
-
36
- include StringConversions
37
+ # proxy to converters
38
+ def respond_to?(method_name) # :nodoc:
39
+ if [:convert, :rules_for, :process].include? method_name.to_sym
40
+ true
41
+ elsif @converters.include? method_name.to_sym
42
+ true
43
+ else
44
+ super
45
+ end
46
+ end
37
47
 
38
48
  private
39
49
 
@@ -52,13 +62,20 @@ module ToPass
52
62
  # process the string, rule by rule
53
63
  def process(string, rules)
54
64
  rules.inject(string) do |pwd, rule|
55
- if rule.is_a?(String) and respond_to?(rule.to_sym)
56
- send(rule.to_sym, pwd)
57
- elsif rule.is_a?(Hash) and rule.has_key?('table')
58
- replace(pwd, @rules['tables'][rule.fetch('table')])
59
- else
60
- pwd
61
- end
65
+ apply_rule(pwd, rule)
66
+ end
67
+ end
68
+
69
+ # apply a single rule to the password-to-be
70
+ def apply_rule(pwd, rule)
71
+ cmd, args = rule.to_a.flatten
72
+ m = @reader.load(cmd.to_sym).method(cmd.to_sym)
73
+
74
+ case m.arity
75
+ when 1
76
+ m.call(pwd)
77
+ when 3, -2
78
+ m.call(pwd, @rules, *args)
62
79
  end
63
80
  end
64
81
  end
@@ -0,0 +1,107 @@
1
+ # -*- coding: utf-8 -*-
2
+ # vim:ft=ruby:enc=utf-8
3
+
4
+ require 'pathname'
5
+
6
+ # The ConverterReader's primary API is to load the converters from right
7
+ # directories into an Array
8
+ #
9
+ # Converters are searched in the following locations
10
+ #
11
+ # 1. ~/.to_pass/converters
12
+ # 2. bundled converters of gem
13
+ #
14
+ # The bundled converter are, however, lazily loaded with autoload.
15
+ # User-provided converters are always required (for now).
16
+ #
17
+ class ToPass::ConverterReader
18
+ attr_reader :load_path, :loaded
19
+
20
+ def initialize # :nodoc:
21
+ @load_path = []
22
+ @loaded = []
23
+ @discovered = []
24
+ [
25
+ '~/.to_pass/converters',
26
+ "#{File.dirname(__FILE__)}/converters"
27
+ ].each do |dir|
28
+ dir = Pathname.new(dir).expand_path
29
+ @load_path << dir if dir.exist?
30
+ end
31
+ end
32
+
33
+ # loads the converters
34
+ def load(converter)
35
+ unless @loaded.include?(converter.to_sym)
36
+ load_from_file(converter)
37
+ @loaded << converter
38
+ end
39
+
40
+ classname(converter)
41
+ end
42
+
43
+ # discover a list of available converters
44
+ def discover
45
+ search_for_converters
46
+ end
47
+
48
+ private
49
+
50
+ def search_for_converters # :nodoc:
51
+ files = load_path.collect do |directory|
52
+ dir = Pathname.new(directory)
53
+ if dir.exist?
54
+ Dir["#{dir}/*.rb"].collect do |converter|
55
+ fn_re = %r!/([^/]*)\.rb$!i
56
+ name = converter.match(fn_re)
57
+ if name
58
+ name[1].to_sym
59
+ end
60
+ end
61
+ end
62
+ end.flatten.compact
63
+
64
+ raise LoadError, "converters could not be found in #{load_path}" if files.nil?
65
+
66
+ @discovered = files
67
+ end
68
+
69
+ def load_from_file(converter) # :nodoc:
70
+ fn = load_path.collect do |dir|
71
+ path = Pathname.new("#{dir}/#{converter}.rb").expand_path
72
+
73
+ if path.exist?
74
+ path
75
+ else
76
+ next
77
+ end
78
+ end.compact.first
79
+
80
+ raise LoadError, "converter '#{converter}' could not be found in #{load_path}" if fn.nil?
81
+
82
+ if require fn
83
+ classname converter
84
+ end
85
+ end
86
+
87
+ def classname(converter) # :nodoc:
88
+ constantize("ToPass::Converters::#{camel_case(converter)}")
89
+ end
90
+
91
+ def constantize(camel_cased_word) # :nodoc:
92
+ names = camel_cased_word.split('::')
93
+ names.shift if names.empty? || names.first.empty?
94
+
95
+ constant = Object
96
+ names.each do |name|
97
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
98
+ end
99
+ constant
100
+ end
101
+
102
+ def camel_case(underscored_word) # :nodoc:
103
+ camel_cased_word = underscored_word.to_s.split('_').map do |part|
104
+ part.capitalize
105
+ end.join('')
106
+ end
107
+ end
@@ -0,0 +1,12 @@
1
+ module ToPass::Converters
2
+ class CollapseChars
3
+ class << self
4
+ # all the blanks are removed.
5
+ #
6
+ # this is useful if you convert a sentence into a password.
7
+ def collapse_chars(string)
8
+ string = string.split(' ').join('')
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module ToPass::Converters
2
+ class FirstChars
3
+ class << self
4
+ # reduces every word to its first character, preserving case
5
+ def first_chars(string)
6
+ string.split(' ').map do |word|
7
+ word[0].chr
8
+ end.join(' ')
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module ToPass::Converters
2
+ class Replace
3
+ class << self
4
+ # perform replacements on a string, based on a replacment table
5
+ def replace(string, rules, tablename)
6
+ rules['replacements'][tablename].inject(string) do |pwd, map|
7
+ pwd = pwd.gsub(/#{map[0].to_s}/, map[1].to_s)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,25 @@
1
+ module ToPass::Converters
2
+ class Swapcase
3
+ class << self
4
+ # alternate case of letter (not numbers)
5
+ def swapcase(string)
6
+ pwd = ""
7
+ last_upcase = true
8
+
9
+ string.each_char do |char|
10
+ char = if char.between?("0", "9")
11
+ char
12
+ elsif last_upcase
13
+ last_upcase = false
14
+ char.downcase
15
+ else
16
+ last_upcase = true
17
+ char.upcase
18
+ end
19
+ pwd << char
20
+ end
21
+ pwd
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ # -*- coding: utf-8 -*-
2
+ # vim:ft=ruby:enc=utf-8
3
+
4
+ module ToPass
5
+ # The Converters-class is a collection of available
6
+ # Transformations. Every method is given a string as single argument
7
+ # and is expected to return a transformed string. The only exception
8
+ # from this is general rule is the replace method which also takes a
9
+ # replacement table.
10
+ #
11
+ # This enables chaining and eases extending the capabilities.
12
+ module Converters
13
+ end
14
+ end
data/lib/to_pass.rb CHANGED
@@ -13,6 +13,7 @@ module ToPass
13
13
  autoload :Base, 'lib/to_pass/base'
14
14
  autoload :Cli, 'lib/to_pass/cli.rb'
15
15
  autoload :Converter, 'lib/to_pass/converter'
16
+ autoload :ConverterReader, 'lib/to_pass/converter_reader'
17
+ autoload :Converters, 'lib/to_pass/converters'
16
18
  autoload :Integration, 'lib/to_pass/integration'
17
- autoload :StringConversions, 'lib/to_pass/string_conversions'
18
19
  end
@@ -2,13 +2,13 @@ desc: Test Algorithm
2
2
  name: Test
3
3
  sentence:
4
4
  - first_chars
5
- - table: chars
6
- - table: symbols
5
+ - replace: chars
6
+ - replace: symbols
7
7
  - collapse_chars
8
8
  word:
9
- - table: chars
10
- - table: symbols
11
- tables:
9
+ - replace: chars
10
+ - replace: symbols
11
+ replacements:
12
12
  symbols:
13
13
  p: '%'
14
14
  s: '/'
@@ -0,0 +1,17 @@
1
+ # -*- coding: utf-8 -*-
2
+ # vim:ft=ruby:enc=utf-8
3
+
4
+ module ToPass::Converters
5
+ # replace the string "USER" with the current username
6
+ class Userize
7
+ def self.convert(string) # :nodoc:
8
+ username = if `which whoami`
9
+ `whoami`.chomp
10
+ else
11
+ "thecurrentuser"
12
+ end
13
+
14
+ string.gsub("USER",username)
15
+ end
16
+ end
17
+ end
data/test/helper.rb CHANGED
@@ -27,10 +27,16 @@ Test::Unit::TestCase.class_eval do
27
27
 
28
28
 
29
29
  def with_algorithm_in_user_dir
30
- `mkdir -p ~/to_pass/algorithms; cp -f #{File.dirname(__FILE__)}/fixtures/user_alg.yml ~/.to_pass/algorithms/user_alg.yml`
30
+ `mkdir -p ~/.to_pass/algorithms; cp -f #{File.dirname(__FILE__)}/fixtures/user_alg.yml ~/.to_pass/algorithms/user_alg.yml`
31
31
  yield
32
32
  `rm ~/.to_pass/algorithms/user_alg.yml`
33
33
  end
34
+
35
+ def with_converters_in_user_dir
36
+ `mkdir -p ~/.to_pass/converters; cp -f #{File.dirname(__FILE__)}/fixtures/user_converter.rb ~/.to_pass/converters/userize.rb`
37
+ yield
38
+ `rm ~/.to_pass/converters/userize.rb`
39
+ end
34
40
  end
35
41
 
36
42
  require 'lib/to_pass'
@@ -17,6 +17,11 @@ class TestAlgorithmReader < Test::Unit::TestCase
17
17
  assert_kind_of Hash, klass.load(:basic_de)
18
18
  end
19
19
 
20
+ def test_discover
21
+ assert_respond_to klass, :discover
22
+ assert_kind_of Array, klass.discover
23
+ end
24
+
20
25
  def test_has_load_path
21
26
  assert_respond_to reader, :load_path
22
27
  assert_kind_of Array, reader.load_path
@@ -25,7 +30,7 @@ class TestAlgorithmReader < Test::Unit::TestCase
25
30
  def test_load_path_contains_standard_dirs
26
31
  dirs = [
27
32
  '~/.to_pass/algorithms' ,
28
- "#{File.dirname(__FILE__)}/../lib/algorithms"
33
+ "#{File.dirname(__FILE__)}/../lib/to_pass/algorithms"
29
34
  ]
30
35
 
31
36
  Pathname.any_instance.expects(:exist?).times(dirs.size).returns(true)
data/test/test_cli.rb CHANGED
@@ -24,6 +24,18 @@ class TestCli < Test::Unit::TestCase
24
24
  end
25
25
  end
26
26
 
27
+ def test_cli_usage_with_pipe_input
28
+ assert_nothing_raised do
29
+ assert_equal 't35t', `echo "test" | bin/to_pass --no-pipe`.chomp
30
+ end
31
+ end
32
+
33
+ def test_cli_usage_with_pipe_output
34
+ assert_nothing_raised do
35
+ assert_equal 't35t', `bin/to_pass test --pipe`
36
+ end
37
+ end
38
+
27
39
 
28
40
  def test_cli_usage_with_user_algorithm
29
41
  with_algorithm_in_user_dir do
@@ -25,33 +25,34 @@ class TestConverter < Test::Unit::TestCase
25
25
  # mock-tests to ensure presence and calling of protected methods
26
26
  def test_replace
27
27
  assert_respond_to converter, :replace
28
- converter.expects(:replace).once
28
+ converter.expects(:apply_rule).with("test", {'replace'=>'special'}).once
29
29
 
30
30
  converter.convert("test")
31
31
  end
32
32
  def test_swapcase
33
33
  converter({'word' => ['swapcase']})
34
34
  assert_respond_to converter, :swapcase
35
- converter.expects(:swapcase).once
35
+ converter.expects(:apply_rule).with("test", 'swapcase' ).once
36
36
  converter.convert("test")
37
37
  end
38
38
  def test_first_chars
39
39
  converter({'sentence' => ['first_chars']})
40
40
  assert_respond_to converter, :first_chars
41
- converter.expects(:first_chars).once
41
+ converter.expects(:apply_rule).with("test test", 'first_chars').once
42
42
  converter.convert("test test")
43
43
  end
44
44
  def test_collapse_chars
45
45
  converter({'sentence' => ['collapse_chars']})
46
46
  assert_respond_to converter, :collapse_chars
47
- converter.expects(:collapse_chars).once
47
+ converter.expects(:apply_rule).with("test test", 'collapse_chars').once
48
48
  converter.convert("test test")
49
49
  end
50
50
 
51
+
51
52
  # more complex/real-life setups
52
53
  def test_multiple_rules
53
54
  converter(basic_rules.merge({
54
- 'word' => [{'table' => 'special'}, 'swapcase']
55
+ 'word' => [{'replace' => 'special'}, 'swapcase']
55
56
  }))
56
57
 
57
58
  assert_equal( "t35T", converter.convert('test'))
@@ -67,6 +68,34 @@ class TestConverter < Test::Unit::TestCase
67
68
  Pferd auf dem Flur"))
68
69
  end
69
70
 
71
+
72
+ # actual result-testing
73
+ def test_replacement
74
+ rules = {
75
+ 'replacements' => {
76
+ 'numbers' => {
77
+ :e => 3,
78
+ :s => 5
79
+ }
80
+ }
81
+ }
82
+ result = converter(rules).send(:apply_rule, "test", {'replace'=>'numbers'})
83
+ assert_equal "t35t", result
84
+ end
85
+ def test_case_swapping
86
+ assert_equal "tEsT", converter.send(:apply_rule, "test", 'swapcase')
87
+ end
88
+ def test_case_swapping_ignores_numbers
89
+ assert_equal "tEsT4fUn", converter.send(:apply_rule, "test4fun", 'swapcase')
90
+ assert_equal "fUn4TeSt", converter.send(:apply_rule, "fun4test", 'swapcase')
91
+ end
92
+ def test_char_collapsing
93
+ assert_equal "abc", converter.send(:apply_rule, "a b c", 'collapse_chars')
94
+ end
95
+ def test_select_first_chars
96
+ assert_equal "t a t f t", converter.send(:apply_rule, "test all the fucking time", 'first_chars')
97
+ end
98
+
70
99
  protected
71
100
 
72
101
  def converter(rules = basic_rules)
@@ -75,8 +104,8 @@ Pferd auf dem Flur"))
75
104
 
76
105
  def basic_rules
77
106
  {
78
- 'word' => [{ 'table' => 'special' }],
79
- 'tables' => {
107
+ 'word' => [{ 'replace' => 'special' }],
108
+ 'replacements' => {
80
109
  'special' => {
81
110
  'e' => '3',
82
111
  's' => '5'
@@ -86,8 +115,8 @@ Pferd auf dem Flur"))
86
115
  end
87
116
  def complex_rules
88
117
  {
89
- 'sentence' => [{'table'=>'words'},'first_chars','collapse_chars',{'table'=>'symbols'}],
90
- 'tables' => {
118
+ 'sentence' => [{'replace'=>'words'},'first_chars','collapse_chars',{'replace'=>'symbols'}],
119
+ 'replacements' => {
91
120
  'words' => { 'ein' => '1' },
92
121
  'symbols' => { 'a' => '@' }
93
122
  }
@@ -0,0 +1,64 @@
1
+ # -*- coding: utf-8 -*-
2
+ # vim:ft=ruby:enc=utf-8
3
+
4
+ require File.dirname(__FILE__)+'/helper'
5
+
6
+ class TestConverterReader < Test::Unit::TestCase
7
+ test_presence ToPass::ConverterReader
8
+
9
+ def test_load
10
+ assert_respond_to reader, :load
11
+ assert_kind_of Class, reader.load(:replace)
12
+ end
13
+
14
+ def test_discover
15
+ assert_respond_to reader, :discover
16
+ assert_kind_of Array, reader.discover
17
+ end
18
+
19
+ def test_has_load_path
20
+ assert_respond_to reader, :load_path
21
+ assert_kind_of Array, reader.load_path
22
+ end
23
+
24
+ def test_knows_loaded_converters
25
+ assert_respond_to reader, :loaded
26
+ assert_kind_of Array, reader.loaded
27
+ assert_equal 0, reader.loaded.size
28
+
29
+ reader.load(:replace)
30
+ reader.load(:first_chars)
31
+
32
+ assert_equal 2, reader.loaded.size
33
+ assert_equal :replace, reader.loaded.first
34
+ end
35
+
36
+ def test_load_path_contains_standard_dirs
37
+ dirs = [
38
+ '~/.to_pass/converters',
39
+ "#{File.dirname(__FILE__)}/../lib/to_pass/converters"
40
+ ]
41
+
42
+ Pathname.any_instance.expects(:exist?).times(dirs.size).returns(true)
43
+
44
+ dirs.each do |reldir|
45
+ dir = Pathname.new(reldir).expand_path
46
+ assert( reader.load_path.include?(dir), "#{reader.load_path.inspect} should include #{dir.inspect}" )
47
+ end
48
+ end
49
+
50
+ def test_loads_from_user_dir
51
+ with_converters_in_user_dir do
52
+ assert_kind_of Class, reader.load(:userize), "Converter 'Userize' should have been found"
53
+ end
54
+ end
55
+
56
+ protected
57
+
58
+ def klass
59
+ ToPass::ConverterReader
60
+ end
61
+ def reader
62
+ @reader ||= klass.new
63
+ end
64
+ end
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+ # vim:ft=ruby:enc=utf-8
3
+
4
+ require File.dirname(__FILE__)+'/helper'
5
+
6
+ class TestConverters < Test::Unit::TestCase
7
+ def test_presence
8
+ assert_module_defined ToPass::Converters
9
+ end
10
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: to_pass
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
8
  - 4
10
- version: 0.2.4
9
+ - 0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matthias Viehweger
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-24 00:00:00 +02:00
18
+ date: 2010-06-30 00:00:00 +02:00
19
19
  default_executable: to_pass
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -66,23 +66,30 @@ files:
66
66
  - TODO
67
67
  - VERSION
68
68
  - bin/to_pass
69
- - lib/algorithms/basic_de.yml
70
- - lib/algorithms/basic_en.yml
71
69
  - lib/to_pass.rb
72
70
  - lib/to_pass/algorithm_reader.rb
71
+ - lib/to_pass/algorithms/basic_de.yml
72
+ - lib/to_pass/algorithms/basic_en.yml
73
73
  - lib/to_pass/base.rb
74
74
  - lib/to_pass/cli.rb
75
75
  - lib/to_pass/converter.rb
76
+ - lib/to_pass/converter_reader.rb
77
+ - lib/to_pass/converters.rb
78
+ - lib/to_pass/converters/collapse_chars.rb
79
+ - lib/to_pass/converters/first_chars.rb
80
+ - lib/to_pass/converters/replace.rb
81
+ - lib/to_pass/converters/swapcase.rb
76
82
  - lib/to_pass/integration.rb
77
- - lib/to_pass/string_conversions.rb
78
83
  - test/fixtures/user_alg.yml
84
+ - test/fixtures/user_converter.rb
79
85
  - test/helper.rb
80
86
  - test/test_algorithm_reader.rb
81
87
  - test/test_base.rb
82
88
  - test/test_cli.rb
83
89
  - test/test_converter.rb
90
+ - test/test_converter_reader.rb
91
+ - test/test_converters.rb
84
92
  - test/test_integration.rb
85
- - test/test_string_conversions.rb
86
93
  has_rdoc: true
87
94
  homepage: http://github.com/kronn/to_pass
88
95
  licenses: []
@@ -118,10 +125,12 @@ signing_key:
118
125
  specification_version: 3
119
126
  summary: generate password from words or sentences
120
127
  test_files:
128
+ - test/fixtures/user_converter.rb
121
129
  - test/helper.rb
122
130
  - test/test_algorithm_reader.rb
123
131
  - test/test_base.rb
124
132
  - test/test_cli.rb
125
133
  - test/test_converter.rb
134
+ - test/test_converter_reader.rb
135
+ - test/test_converters.rb
126
136
  - test/test_integration.rb
127
- - test/test_string_conversions.rb
@@ -1,54 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # vim:ft=ruby:enc=utf-8
3
-
4
- module ToPass
5
- # The StringConversions-Module is a collection of available
6
- # Transformations. Every method is given a string as single argument
7
- # and is expected to return a transformed string. The only exception
8
- # from this is general rule is the replace method which also takes a
9
- # replacement table.
10
- #
11
- # This enables chaining and eases extending the capabilities.
12
- module StringConversions
13
- # all the blanks are removed.
14
- #
15
- # this is useful if you convert a sentence into a password.
16
- def collapse_chars(string)
17
- string = string.split(' ').join('')
18
- end
19
-
20
- # reduces every word to its first character, preserving case
21
- def first_chars(string)
22
- string.split(' ').map do |word|
23
- word[0].chr
24
- end.join(' ')
25
- end
26
-
27
- # alternate case of letter (not numbers)
28
- def swapcase(string)
29
- pwd = ""
30
- last_upcase = true
31
-
32
- string.each_char do |char|
33
- char = if char.between?("0", "9")
34
- char
35
- elsif last_upcase
36
- last_upcase = false
37
- char.downcase
38
- else
39
- last_upcase = true
40
- char.upcase
41
- end
42
- pwd << char
43
- end
44
- pwd
45
- end
46
-
47
- # perform replacements on a string, based on a replacment table
48
- def replace(string, table)
49
- table.inject(string) do |pwd, map|
50
- pwd = pwd.gsub(/#{map[0].to_s}/, map[1].to_s)
51
- end
52
- end
53
- end
54
- end
@@ -1,40 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # vim:ft=ruby:enc=utf-8
3
-
4
- require File.dirname(__FILE__)+'/helper'
5
-
6
- class TestStringConversion < Test::Unit::TestCase
7
- def test_replacement
8
- result = converter.replace("test", {
9
- :e => 3,
10
- :s => 5
11
- })
12
- assert_equal "t35t", result
13
- end
14
-
15
- def test_case_swapping
16
- assert_equal "tEsT", converter.swapcase("test")
17
- end
18
-
19
- def test_case_swapping_ignores_numbers
20
- assert_equal "tEsT4fUn", converter.swapcase("test4fun")
21
-
22
- assert_equal "fUn4TeSt", converter.swapcase("fun4test")
23
- end
24
-
25
- def test_char_collapsing
26
- assert_equal "abc", converter.collapse_chars("a b c")
27
- end
28
-
29
- def test_select_first_chars
30
- assert_equal "t a t f t", converter.first_chars('test all the fucking time')
31
- end
32
-
33
- protected
34
-
35
- def converter
36
- klass = Class.new
37
- klass.send(:extend, ToPass::StringConversions)
38
- klass
39
- end
40
- end