memorable_password 0.0.3 → 0.1.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 CHANGED
@@ -2,3 +2,7 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ coverage/*
6
+ doc/*
7
+ .yardoc
8
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm ree@memorable_password --create
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in memorable_password.gemspec
4
- gemspec
4
+ gemspec
data/README.markdown CHANGED
@@ -1,7 +1,7 @@
1
1
  # Memorable Password
2
2
 
3
- [Kevin McPhillips](mailto:github@kevinmcphillips.ca)
4
-
3
+ [Kevin McPhillips](mailto:github@kevinmcphillips.ca),
4
+ [Oleksandr Ulianytskyi](mailto:a.ulyanitsky@gmail.com)
5
5
 
6
6
  ## About
7
7
 
@@ -14,29 +14,33 @@ It is, of course, by definition less secure than a truly random password. The in
14
14
 
15
15
  Generates a password with the default length of 8 characters.
16
16
 
17
- MemorablePassword.generate
17
+ MemorablePassword.new.generate
18
18
  => "pad8dune"
19
19
 
20
20
  Generates a password with a specified length.
21
21
 
22
- MemorablePassword.generate :length => 10
22
+ MemorablePassword.new.generate :length => 10
23
23
  => "june3eaten"
24
24
 
25
25
  Generates a password that is at least a certain length.
26
26
 
27
- MemorablePassword.generate :min_length => 8
27
+ MemorablePassword.new.generate :min_length => 8
28
28
  => "gale3covalt"
29
29
 
30
30
  Generates a password that includes special characters.
31
31
 
32
- MemorablePassword.generate :special_characters => true
32
+ MemorablePassword.new.generate :special_characters => true
33
33
  => "grace!pi"
34
34
 
35
35
  Generates a password that mixes upper case in.
36
36
 
37
- MemorablePassword.generate :mixed_case => true
37
+ MemorablePassword.new.generate :mixed_case => true
38
38
  => "was7Room"
39
39
 
40
+ Generates a password that is two 4-char words joined by non-ambiguous digit (not 2 and 4).
41
+
42
+ MemorablePassword.new.generate_simple
43
+ => "sons3pied"
40
44
 
41
45
  ## Feedback
42
46
 
data/Rakefile CHANGED
@@ -1,2 +1,11 @@
1
1
  require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+
2
4
  Bundler::GemHelper.install_tasks
5
+ RSpec::Core::RakeTask.new
6
+
7
+ desc "Run all specs with rcov"
8
+ RSpec::Core::RakeTask.new(:rcov) do |t|
9
+ t.rcov = true
10
+ t.rcov_opts = %w{--rails --exclude osx\/objc,gems\/,spec\/,features\/}
11
+ end
@@ -4,4 +4,4 @@ if RUBY_VERSION.to_f < 1.9
4
4
  self[rand(length)]
5
5
  end
6
6
  end
7
- end
7
+ end
@@ -1,3 +1,3 @@
1
- module MemorablePassword
2
- VERSION = "0.0.3"
1
+ class MemorablePassword
2
+ VERSION = "0.1.1"
3
3
  end
@@ -1,27 +1,87 @@
1
1
  require 'memorable_password/sample'
2
2
 
3
- module MemorablePassword
4
-
3
+ # This class is used for generating memorable passwords. It generates the password
4
+ # from a list of words, proper names, digits and characters.
5
+ #
6
+ # If no options are passed in, the dictionary is built from '/usr/share/dict/words'.
7
+ #
8
+ # Options are:
9
+ # * <tt>ban_list</tt> - an array of words that should added to the blacklist
10
+ # * <tt>dictionary_paths</tt> - an array of paths to files that contain dictionary words
11
+ # * <tt>blacklist_paths</tt> - an array of paths to files that contain blacklisted words
12
+ #
13
+ # Option examples:
14
+ # MemorablePassword.new(:ban_list => ['bad word'])
15
+ # MemorablePassword.new(:dictionary_paths => ['path_to_dictionary/dict.txt'])
16
+ # MemorablePassword.new(:blacklist_paths => ['path_to_blacklist/blacklist.txt'])
17
+ #
18
+ # == Generate password
19
+ # Generate a random password by calling #generate.
20
+ # See #generate for configuration details.
21
+ #
22
+ # MemorablePassword.new.generate
23
+ # # => "fig7joeann"
24
+ #
25
+ # == Generate simple password
26
+ # Generate a simple 9-character password by calling #generate_simple.
27
+ #
28
+ # MemorablePassword.new.generate_simple
29
+ # # => "sons3pied"
30
+ #
31
+ class MemorablePassword
5
32
  MAX_WORD_LENGTH = 7
6
- DIGITS = (0..9).to_a.map{|d| d.to_s}
7
- CHARACTERS = %w[! @ $ ? -]
8
-
9
- DEFAULT_OPTIONS = {
33
+ DIGITS = %w[0 1 2 3 4 5 6 7 8 9].freeze
34
+ NON_WORD_DIGITS = (DIGITS - %w(2 4 8)).freeze
35
+ CHARACTERS = %w[! @ $ ? -].freeze
36
+
37
+ # The default paths to the various dictionary flat files
38
+ DEFAULT_PATH = "#{File.dirname(__FILE__)}/memorable_password"
39
+ DEFAULT_DICTIONARY_PATHS = ['/usr/share/dict/words', "#{DEFAULT_PATH}/names.txt"]
40
+ DEFAULT_BLACKLIST_PATHS = ["#{DEFAULT_PATH}/blacklist.txt"]
41
+
42
+ DEFAULT_GENERATE_OPTIONS = {
10
43
  :mixed_case => false,
11
44
  :special_characters => false,
12
45
  :length => nil,
13
46
  :min_length => nil
14
47
  }
15
-
16
- class << self; attr_accessor :dictionary, :blacklist end
17
- @dictionary = nil
18
- @blacklist = nil
19
-
20
- def self.generate(opts={})
21
- opts = DEFAULT_OPTIONS.merge(opts)
22
-
23
- raise "You cannot specify :length and :min_length at the same time" if opts[:length] && opts[:min_length] # Nonsense!
24
-
48
+
49
+ attr_reader :dictionary, :blacklist, :ban_list,
50
+ :dictionary_paths, :blacklist_paths
51
+
52
+ def initialize(options={})
53
+ # TODO implement these lists as Sets to get Hash lookup and uniqueness for free -- matt.dressel 20120328
54
+ # The dictionary is currently a hash of length to words: {1 => ['w','a'], 2 => ['at']}
55
+ @ban_list = options.fetch(:ban_list, [])
56
+
57
+ # TODO support passing data in as an array -- matt.dressel 20120328
58
+ @dictionary_paths = options.fetch(:dictionary_paths, DEFAULT_DICTIONARY_PATHS)
59
+ @blacklist_paths = options.fetch(:blacklist_paths, DEFAULT_BLACKLIST_PATHS)
60
+
61
+ @dictionary = {}
62
+ @blacklist = []
63
+
64
+ build_dictionary
65
+ end
66
+
67
+ # Generates memorable password as a combination of two 4-letter dictionary
68
+ # words joined by a numeric character excluding 2, 4 and 8.
69
+ def generate_simple
70
+ "#{word(4)}#{non_word_digit}#{word(4)}"
71
+ end
72
+
73
+ # Generates memorable password.
74
+ # +opts+:: hash with options
75
+ # [:mixed_case] +true+ or +false+ - use mixedCase (default: +false+)
76
+ # [:special_characters] +true+ or +false+ - use special characters like ! @ $? - (default: +false+)
77
+ # [:length] Fixnum - generate passoword with specific length
78
+ # [:min_length] Fixnum - generate passoword with length grather or equal of specified
79
+ # Options +:length+ and +:min_length+ are incompatible.
80
+ def generate(opts={})
81
+ opts = DEFAULT_GENERATE_OPTIONS.merge(opts)
82
+
83
+ raise "You cannot specify :length and :min_length at the same time" if opts[:length] && opts[:min_length]
84
+
25
85
  if opts[:length]
26
86
  password = [(opts[:length] >= 8 ? long_word : word), (opts[:special_characters] ? character : digit)]
27
87
  password << word(opts[:length] - password.compact.join.length)
@@ -33,95 +93,141 @@ module MemorablePassword
33
93
  password << word(count)
34
94
  end
35
95
  end
36
-
96
+
97
+ elsif opts[:special_characters]
98
+ password = [word, character, word, digit]
37
99
  else
38
- if opts[:special_characters]
39
- password = [word, character, word, digit]
40
- else
41
- password = [word, digit, long_word]
42
- end
100
+ password = [word, non_word_digit, long_word]
43
101
  end
44
-
102
+
45
103
  if opts[:mixed_case]
46
104
  password.compact.reject{|x| x.length == 1}.sample.capitalize!
47
105
  end
48
-
106
+
49
107
  # If a minimum length is required and this password is too short
50
- if opts[:min_length] && password.compact.join.length < opts[:min_length]
51
- if (count = opts[:min_length] - password.compact.join.length) == 1
108
+ compact_password_length = password.compact.join.length
109
+ if opts[:min_length] && compact_password_length < opts[:min_length]
110
+ if (count = opts[:min_length] - compact_password_length) == 1
52
111
  password << digit
53
112
  else
54
113
  password << word(count)
55
114
  end
56
115
  end
57
-
116
+
117
+
58
118
  # If it is too long, just cut it down to size. This should not happen often unless the :length option is present and is very small.
59
- if opts[:length] && password.compact.join.length > opts[:length]
60
- result = password.compact.join.slice(0, opts[:length])
61
-
119
+ compact_password_string = password.compact.join
120
+ if opts[:length] && compact_password_string.length > opts[:length]
121
+ result = compact_password_string.slice(0, opts[:length])
122
+
62
123
  # If there is no digit then it is probably a short password that by chance is just a dictionary word. Override that because that is bad.
63
124
  if result =~ /^[a-z]+$/
64
125
  password = [(opts[:mixed_case] ? word(opts[:length] - 1).capitalize : word(opts[:length] - 1)), (opts[:special_characters] ? character : digit)]
65
126
  result = password.compact.join
66
127
  end
67
-
128
+
68
129
  result
69
130
  else
70
- password.compact.join
131
+ compact_password_string
71
132
  end
72
133
  end
73
-
134
+
135
+ # Adds the +word+ to the dictionary unless it is invalid or blacklisted
136
+ def add_word(word)
137
+ return unless validate_word(word)
138
+ word = normalize_word(word)
139
+
140
+ unless @blacklist.include?(word)
141
+ length = word.length
142
+ @dictionary[length] = [] unless @dictionary[length]
143
+ @dictionary[length] << word
144
+ end
145
+ end
146
+
147
+ # Adds the +word+ to the blacklist unless it is invalid
148
+ def blacklist_word(word)
149
+ return unless validate_word(word)
150
+ word = normalize_word(word)
151
+
152
+ @blacklist << word
153
+ # Remove the blacklisted word from the dictionary if it exists
154
+ dictionary_array = @dictionary[word.length]
155
+ dictionary_array.delete(word) if dictionary_array && dictionary_array.include?(word)
156
+ end
157
+
74
158
  private
75
-
76
- def self.character
159
+
160
+ # Returns a random character
161
+ def character
77
162
  CHARACTERS.sample
78
163
  end
79
-
80
- def self.digit
164
+
165
+ # Returns a random digit
166
+ def digit
81
167
  DIGITS.sample
82
168
  end
83
-
84
- def self.word(length=nil)
85
- length = self.dictionary.keys.sample if !length || length > self.dictionary.keys.max
86
- self.dictionary[length].sample if self.dictionary.has_key?(length)
169
+
170
+ # Returns a random, non-ambiguous digit (0..9 without 2, 4 and 8)
171
+ def non_word_digit
172
+ NON_WORD_DIGITS.sample
87
173
  end
88
-
89
- def self.long_word
90
- keys = self.dictionary.keys.sort
91
- self.dictionary[keys.partition{|v| v >= keys[keys.size/2] }.first.sample].sample # Magic! It actually just randomly picks from the larger words..
174
+
175
+ # Ensures that the word is valid:
176
+ # is not null
177
+ # is at least 2 letters
178
+ # and does not exceed the MAX_WORD_LENGTH
179
+ #
180
+ # Returns +true+ if the word if it is valid
181
+ # Returns +false+ if the word is invalid
182
+ def validate_word(word)
183
+ return false if word.nil?
184
+
185
+ word = normalize_word(word)
186
+ word.length <= MAX_WORD_LENGTH && word =~ /^[a-z]{2,}$/
92
187
  end
93
-
94
- def self.initialize_dictionary
95
- unless self.dictionary
96
- self.dictionary = {}
97
- self.blacklist = []
98
-
99
- # Load blacklist from text file
100
- File.foreach(File.join(File.dirname(__FILE__), 'memorable_password', 'blacklist.txt'))do |word|
101
- word = word.strip.downcase
102
- self.blacklist << word if word =~ /^[a-z]+$/
103
- end
104
-
105
- # Load system dictionary words
106
- File.foreach("/usr/share/dict/words"){|word| add_word word}
107
-
108
- # Load list of proper names
109
- File.foreach(File.join(File.dirname(__FILE__), 'memorable_password', 'names.txt')){|word| add_word word}
110
- end
111
-
112
- self.dictionary
188
+
189
+ # Strips the word of carriage return characters
190
+ # and converts all characters to lowercase
191
+ def normalize_word(word)
192
+ word.strip.downcase
193
+ end
194
+
195
+ # Returns a random word. If +length+ is given, find a word with this length.
196
+ def word(length=nil)
197
+ length = @dictionary.keys.sample if !length || length > @dictionary.keys.max
198
+ @dictionary[length].sample if @dictionary.has_key?(length)
199
+ end
200
+
201
+ # Returns a random word from most long ones
202
+ def long_word
203
+ keys = @dictionary.keys.sort
204
+ @dictionary[keys.partition{|v| v >= keys[keys.size/2] }.first.sample].sample # Magic! It actually just randomly picks from the larger words..
113
205
  end
114
-
115
- def self.add_word(word)
116
- word = word.strip.downcase
117
- length = word.length
118
-
119
- if length <= MAX_WORD_LENGTH && length > 1 && word =~ /^[a-z]+$/ && !self.blacklist.include?(word)
120
- self.dictionary[length] = [] unless self.dictionary[length]
121
- self.dictionary[length] << word
206
+
207
+ # Builds the blacklist from the blacklist text file
208
+ # Adds user-supplied banned words to the blacklist
209
+ def build_blacklist
210
+ # Load blacklisted words from blacklist files
211
+ blacklist_paths.each do |blacklist_path|
212
+ File.foreach(blacklist_path){ |word| blacklist_word(word) }
122
213
  end
214
+
215
+ # Append custom banned words to the blacklist
216
+ ban_list.each{ |word| blacklist_word(word) }
123
217
  end
124
218
 
125
- initialize_dictionary
126
-
219
+ # Builds the dictionary from flat files located in the dictionary_paths
220
+ #
221
+ # If the dictionary_paths option is not set, the default paths will be used
222
+ # including the system dictionary and a list of proper names
223
+ def build_dictionary
224
+ # Make sure the blacklist is built before building the dictionary
225
+ # add_word will make sure the word is not in the blacklist before adding it
226
+ build_blacklist
227
+
228
+ # Load dictionary words from the dictionary files
229
+ dictionary_paths.each do |dictionary_path|
230
+ File.foreach(dictionary_path){ |word| add_word(word) }
231
+ end
232
+ end
127
233
  end
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
6
6
  s.name = "memorable_password"
7
7
  s.version = MemorablePassword::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Kevin McPhillips"]
10
- s.email = ["github@kevinmcphillips.ca"]
9
+ s.authors = ["Kevin McPhillips", "Oleksandr Ulianytskyi"]
10
+ s.email = ["github@kevinmcphillips.ca", "a.ulyanitsky@gmail.com"]
11
11
  s.homepage = "http://github.com/kimos/memorable_password"
12
12
  s.summary = %q{Generate human readable and easy to remember passwords}
13
13
  s.description = %q{This simple gem generates a random password that is easy to read and remember. It uses dictionary words as well as a list of proper names mixed in with numbers and special characters.}
@@ -18,4 +18,8 @@ Gem::Specification.new do |s|
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
+
22
+ s.add_development_dependency("rspec", ">= 2.6.0")
23
+ s.add_development_dependency("rcov", ">= 0")
24
+ s.add_development_dependency("yard", ">= 0")
21
25
  end
@@ -0,0 +1,3 @@
1
+ list
2
+ black
3
+ blcklst
@@ -0,0 +1,14 @@
1
+ i
2
+ to
3
+ who
4
+ word
5
+ bird
6
+ words
7
+ worlds
8
+ u
9
+ tu
10
+ two
11
+ thou
12
+ tour
13
+ their
14
+ though
@@ -0,0 +1 @@
1
+ foo
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+ require 'open-uri'
3
+
4
+ describe MemorablePassword do
5
+ let(:memorable_password) do
6
+ default_path = "#{File.dirname(__FILE__)}/config"
7
+ @memorable_password ||= MemorablePassword.new(
8
+ :dictionary_paths => ["#{default_path}/custom_dictionary.txt"],
9
+ :blacklist_paths => ["#{default_path}/custom_blacklist.txt"])
10
+ end
11
+
12
+ subject {memorable_password}
13
+
14
+ describe "constructor" do
15
+ it "should initialize ban_list" do
16
+ subject.ban_list.should_not be_nil
17
+ subject.ban_list.should be_empty
18
+ end
19
+
20
+ it "should support a ban_list option" do
21
+ expected_ban_list = ['a','b']
22
+ MemorablePassword.new(:ban_list => expected_ban_list).ban_list.should == expected_ban_list
23
+ end
24
+
25
+ it "should support configurable dictionary paths" do
26
+ subject.dictionary.values.flatten.should include 'word'
27
+ end
28
+
29
+ it "should support configurable blacklist paths" do
30
+ subject.blacklist.should include 'blcklst'
31
+ end
32
+ end
33
+
34
+ describe "#add_word" do
35
+ it "should not add words that are less than 2 characters" do
36
+ subject.add_word('i')
37
+ subject.dictionary.values.flatten.should_not include('i')
38
+ end
39
+
40
+ it "should not add words that are greater than MemorablePassword::MAX_WORD_LENGTH" do
41
+ long_word = (0..MemorablePassword::MAX_WORD_LENGTH+1).map{|a| 'a'}.join
42
+ subject.add_word(long_word)
43
+ subject.dictionary.values.flatten.should_not include(long_word)
44
+ end
45
+
46
+ it "should not add words that have non-letters" do
47
+ subject.add_word('iask3')
48
+ subject.dictionary.values.flatten.should_not include('iask3')
49
+ end
50
+
51
+ it "should not add words that are blacklisted" do
52
+ blacklisted_word = subject.blacklist.first
53
+ subject.add_word(blacklisted_word)
54
+ subject.dictionary.values.flatten.should_not include(blacklisted_word)
55
+ end
56
+
57
+ it "should add valid words to the dictionary" do
58
+ valid_word = 'uhappy'
59
+ subject.add_word(valid_word)
60
+ subject.dictionary.values.flatten.should include(valid_word)
61
+ end
62
+ end
63
+
64
+ describe "#blacklist_word" do
65
+ it "should not add words that are less than 2 characters" do
66
+ subject.blacklist_word('i')
67
+ subject.blacklist.should_not include('i')
68
+ end
69
+
70
+ it "should not add words that are greater than MemorablePassword::MAX_WORD_LENGTH" do
71
+ long_word = (0..MemorablePassword::MAX_WORD_LENGTH+1).map{|a| 'a'}.join
72
+ subject.blacklist_word(long_word)
73
+ subject.blacklist.should_not include(long_word)
74
+ end
75
+
76
+ it "should not add words that have non-letters" do
77
+ subject.blacklist_word('iask3')
78
+ subject.blacklist.should_not include('iask3')
79
+ end
80
+
81
+ it "should add valid words to the blacklist" do
82
+ valid_word = 'uhappy'
83
+ subject.blacklist_word(valid_word)
84
+ subject.blacklist.should include(valid_word)
85
+ end
86
+
87
+ it "should remove blacklisted word from the dictionary" do
88
+ blacklisted_dictionary_word = subject.dictionary.values.flatten.sample
89
+ subject.blacklist_word(blacklisted_dictionary_word)
90
+ subject.dictionary.values.flatten.should_not include(blacklisted_dictionary_word)
91
+ end
92
+
93
+ end
94
+
95
+ describe "#generate_simple" do
96
+ let(:generated_password) { @generated_password ||= subject.generate_simple }
97
+
98
+ it "should return a 9-letter string" do
99
+ generated_password.should be_a(String)
100
+ generated_password.length.should eq(9)
101
+ end
102
+
103
+ it "should be a combination of two 4-letter dictionary words joined by a numeric character" do
104
+ generated_password.should =~ /^[a-z]{4}[0-1,3-7,9][a-z]{4}$/i
105
+ end
106
+
107
+ it "should exclude the numbers 2, 4 and 8" do
108
+ examples = Array.new(10) { memorable_password.generate_simple }
109
+ examples.should_not match_any([/[248]/])
110
+ end
111
+
112
+ it "should not mutate the dictionary" do
113
+ default_path = "#{File.dirname(__FILE__)}/config"
114
+ memorable_password = MemorablePassword.new(:dictionary_paths => ["#{default_path}/short_dictionary.txt"])
115
+
116
+ generated_password = memorable_password.generate_simple
117
+ generated_password.should =~ /foo[0-9]foo$/
118
+
119
+ generated_password = memorable_password.generate_simple
120
+ generated_password.should =~ /foo[0-9]foo$/
121
+ end
122
+
123
+ end
124
+
125
+ describe "#generate" do
126
+ it "should generate a random password" do
127
+ generated_password = memorable_password.generate
128
+ generated_password.should =~ /[a-z]*[0-9][a-z]*$/
129
+ end
130
+
131
+ it "should support the mixed_case option" do
132
+ generated_password = memorable_password.generate(:mixed_case => true)
133
+ generated_password.should =~ /[A-Z]?[a-z]*[0-9][A-Z]?[a-z]*$/
134
+ end
135
+
136
+ it "should support the special_characters option" do
137
+ generated_password = memorable_password.generate(:special_characters => true)
138
+ generated_password.should =~ /[a-z]*[!@$?-][a-z]*[0-9]$/
139
+ end
140
+
141
+ it "should support the length option" do
142
+ generated_password = memorable_password.generate(:length => 5)
143
+ generated_password.length.should == 5
144
+ end
145
+
146
+ it "should support the min_length option" do
147
+ generated_password = memorable_password.generate(:min_length => 12)
148
+ generated_password.length.should >= 12
149
+ end
150
+
151
+ it "should raise an exception if both the length and min_length options are supplied" do
152
+ expect {
153
+ memorable_password.generate(:length => 5, :min_length => 2)
154
+ }.should raise_exception('You cannot specify :length and :min_length at the same time')
155
+ end
156
+ end
157
+
158
+ describe "#dictionary" do
159
+ # TODO determine if we should have a mechanistm to test or update the default blacklist
160
+ # with words from http://www.bannedwordlist.com/lists/swearWords.txt
161
+ let(:default_memorable_password) do
162
+ @default_memorable_password ||= MemorablePassword.new
163
+ end
164
+
165
+ it "should not include any blacklisted words" do
166
+ uniq_dictionary_words = default_memorable_password.dictionary.values.flatten.uniq
167
+ uniq_blacklist_words = default_memorable_password.blacklist.uniq
168
+ (uniq_dictionary_words - uniq_blacklist_words).should == uniq_dictionary_words
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,10 @@
1
+ $: << File.expand_path('../../lib', __FILE__)
2
+ require 'rubygems'
3
+ require 'rspec'
4
+ require 'memorable_password'
5
+
6
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
7
+
8
+ RSpec.configure do |config|
9
+ config.mock_with :rspec
10
+ end
@@ -0,0 +1,23 @@
1
+ # Mather check that enumerable with strings match any regexp in array
2
+ # ["abc", "def", "abb"].should match_any([/ab/, /ss/]) # => passes
3
+ # ["abc", "def", "abb"].should match_any([/yz/, /ss/]) # => fails
4
+ RSpec::Matchers.define :match_any do |patterns|
5
+ match do |actual|
6
+ actual.any? { |str| patterns.any? { |regexp| str =~ regexp } }
7
+ end
8
+
9
+ # TODO: make failure messages more informative
10
+ failure_message_for_should do |actual|
11
+ "expected that
12
+ #{actual.inspect}
13
+ matches any of
14
+ #{patterns.inspect}"
15
+ end
16
+
17
+ failure_message_for_should_not do |actual|
18
+ "expected that
19
+ #{actual.inspect}
20
+ not matches any of
21
+ #{patterns.inspect}"
22
+ end
23
+ end
metadata CHANGED
@@ -2,26 +2,70 @@
2
2
  name: memorable_password
3
3
  version: !ruby/object:Gem::Version
4
4
  hash: 25
5
- prerelease: false
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
- - 0
9
- - 3
10
- version: 0.0.3
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kevin McPhillips
14
+ - Oleksandr Ulianytskyi
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2011-06-05 00:00:00 -05:00
19
- default_executable:
20
- dependencies: []
21
-
19
+ date: 2012-05-03 00:00:00 Z
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 23
30
+ segments:
31
+ - 2
32
+ - 6
33
+ - 0
34
+ version: 2.6.0
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rcov
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: yard
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
22
65
  description: This simple gem generates a random password that is easy to read and remember. It uses dictionary words as well as a list of proper names mixed in with numbers and special characters.
23
66
  email:
24
67
  - github@kevinmcphillips.ca
68
+ - a.ulyanitsky@gmail.com
25
69
  executables: []
26
70
 
27
71
  extensions: []
@@ -30,6 +74,8 @@ extra_rdoc_files: []
30
74
 
31
75
  files:
32
76
  - .gitignore
77
+ - .rspec
78
+ - .rvmrc
33
79
  - Gemfile
34
80
  - README.markdown
35
81
  - Rakefile
@@ -39,7 +85,12 @@ files:
39
85
  - lib/memorable_password/sample.rb
40
86
  - lib/memorable_password/version.rb
41
87
  - memorable_password.gemspec
42
- has_rdoc: true
88
+ - spec/lib/config/custom_blacklist.txt
89
+ - spec/lib/config/custom_dictionary.txt
90
+ - spec/lib/config/short_dictionary.txt
91
+ - spec/lib/memorable_password_spec.rb
92
+ - spec/spec_helper.rb
93
+ - spec/support/match_any_matcher.rb
43
94
  homepage: http://github.com/kimos/memorable_password
44
95
  licenses: []
45
96
 
@@ -69,9 +120,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
120
  requirements: []
70
121
 
71
122
  rubyforge_project: memorable_password
72
- rubygems_version: 1.3.7
123
+ rubygems_version: 1.8.15
73
124
  signing_key:
74
125
  specification_version: 3
75
126
  summary: Generate human readable and easy to remember passwords
76
127
  test_files: []
77
128
 
129
+ has_rdoc: