memorable_password 0.0.3 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: