xkpassword 0.4.0 → 0.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a85ce5f07e033d7e20eb30071b644c118ac1e67426c321df01ec2574590eb06f
4
- data.tar.gz: f5505a6dddc828b5cb673d32c90b29dd073a0b7143017b2766e54274eba61315
3
+ metadata.gz: a8679b970d4279654ff7aa2f1212267bd2874466af8adfb197d4005dd94fb62a
4
+ data.tar.gz: 46877ecf578b62ced17e1ba35c8463387c34676d15cf301e8de5188af7619a79
5
5
  SHA512:
6
- metadata.gz: 37b272ecbb57d3e652fa01e6ddd1c047241640e957dd733d9323e57c9daa0e92e5fbb8dbbcf3c82eb5eb5f1c72ca51bac2188f60464d6e4c57dfb6453b07ec4a
7
- data.tar.gz: 47778296384d3398000a0b4cf3f890d847e11461da40f0f8a269ebe4bc6a718a8194b4ea3e3369f8a81c5fb056bcc85e3ee5c89b8568819b72286ce423ce88c2
6
+ metadata.gz: 512d12534d4a4d0072dfb2581f98c7b9e1464647739b7b508cea5e3fb4e9ab972158d3b4ffa58fafc12f1a904ddc369b308ec33d57a32355b29028e2424d5476
7
+ data.tar.gz: '0828510166c54f05316e349a0187aee1d9fd49acf19ee4d8fd970309c938e9a0cb2a06390436f32788a87dc2e71a3cbc23aaf31d82dff868340b45dbd3244a38'
data/.gitignore CHANGED
@@ -3,7 +3,6 @@
3
3
  /Gemfile.lock
4
4
  /_yardoc/
5
5
  /coverage/
6
- /doc/
7
6
  /pkg/
8
7
  /spec/reports/
9
8
  /tmp/
data/README.md CHANGED
@@ -29,7 +29,7 @@ it.
29
29
 
30
30
  ## Installation
31
31
 
32
- Developed in Linux/Ubuntu, this should work fine in a Linux machine. How ever I can't say
32
+ Developed in Linux/Ubuntu, this should work fine on Linux machines. I cannot say
33
33
  the same is true with Mac or Windows systems.
34
34
 
35
35
  Add this line to your application's Gemfile:
@@ -50,69 +50,108 @@ Or install it yourself as:
50
50
  ~# gem install xkpassword
51
51
  ```
52
52
 
53
+ Installing the gem also installs the `xkpassword` executable.
54
+
53
55
  ## Usage
54
56
 
55
- You can use this app stand-alone in the command line or include it in any of your Ruby
56
- applications.
57
+ You can use this app as a Ruby gem in your application or as a command line utility.
58
+ For a fuller guide to presets and examples, see [doc/README.md](doc/README.md).
59
+
60
+ ### Command Line
57
61
 
58
- ### Comamnd Line
59
- The commandline application accepts the same collection of configuration options as would
60
- the `XKPassword` module would. For more information use `xkpassword --help` to obtain a
61
- full list of options.
62
+ The command line app accepts the same generation options as `XKPassword.generate`.
63
+ If you installed the gem, use the executable directly. If you are working from a
64
+ checkout, run the executable through Bundler.
62
65
 
63
66
  ```bash
64
67
  ~# xkpassword
65
68
  ~# xkpassword --help
69
+ ~# bundle exec exe/xkpassword
70
+ ```
71
+
72
+ #### Global CLI config
73
+
74
+ The CLI reads optional defaults from `~/.xkpassword`. The file uses YAML, so it can
75
+ include comments while you experiment with different settings. Use `xkpassword --init`
76
+ to create a commented starter file.
77
+
78
+ ```bash
79
+ ~# xkpassword --init
80
+ ~# xkpassword
81
+ ~# xkpassword --separator .
66
82
  ```
67
83
 
84
+ ```yaml
85
+ # ~/.xkpassword
86
+ # preset: wifi
87
+ words: 5
88
+ min_length: 4
89
+ max_length: 8
90
+ separator: "-"
91
+ # case_transform: capitalize
92
+ ```
93
+
94
+ CLI flags always override values from `~/.xkpassword`, so you can keep preferred defaults
95
+ in the file and still override them per command.
96
+
68
97
  ### Ruby Apps
69
98
 
99
+ Use the library API inside your Ruby application:
100
+
70
101
  ```ruby
71
102
  require 'xkpassword'
72
103
 
73
104
  options = {
105
+ words: 5,
106
+ min_length: 5,
74
107
  max_length: 8,
75
- min_length: 4,
108
+ separator: '.',
76
109
  case_transform: :capitalize,
77
- separator: '-',
78
- words: 4,
79
110
  }
80
111
 
81
112
  XKPassword.generate(options)
82
113
  ```
83
114
 
84
- If you are generating multiple passwords at once, I recommend you use
85
- the following as then it will only load and parse the databse once.
115
+ You can still use presets when they fit your use case:
116
+
117
+ ```ruby
118
+ require 'xkpassword'
119
+
120
+ XKPassword.generate(preset: :security, separator: '.')
121
+ ```
122
+
123
+ If you are generating multiple passwords at once, use a single generator instance so
124
+ the dictionary only needs to be loaded once:
86
125
 
87
126
  ```ruby
88
127
  require 'xkpassword/generator'
89
128
 
90
129
  options = {
91
- max_length: 8,
130
+ words: 4,
92
131
  min_length: 4,
93
- case_transform: :capitalize,
132
+ max_length: 6,
94
133
  separator: '-',
95
- words: 4,
134
+ case_transform: :downcase,
96
135
  }
97
-
136
+
98
137
  generator = XKPassword::Generator.new
99
138
  generator.generate(options)
100
139
 
101
140
  # 10.times { generator.generate(options) }
102
141
  ```
103
142
 
143
+ `preset` supports `:xkcd` (default), `:web32`, `:wifi`, `:security`, and `:apple_id`.
144
+ You can still override any individual option in the selected preset.
145
+
146
+ The default `:xkcd` preset keeps the gem's original behavior of generating 4 words.
147
+
104
148
  `case_transform` supports `:upcase`, `:downcase`, and `:capitalize`, and applies the
105
149
  selected transform to every generated word in the password.
106
150
 
107
151
  ## The GEM
108
152
 
109
- I run a policy of tagging any improvement done for the code base. For example, I just
110
- bumped a minor (resulting 0.2.3 ~> 0.3.0) for adding Travis CI which doesn't change any
111
- functional aspect of the app.
112
-
113
- I am thinking of releasing gems only on functional updates (real code changes) that
114
- happen with new features and bug fixes. So for now, there will be no 0.3.0v available
115
- in Ruby GEMs and the latest would be 0.2.3.
153
+ Releases are intended for functional updates such as new features and bug fixes.
154
+ Check the Git tags or RubyGems release history for the latest published version.
116
155
 
117
156
  ## Development
118
157
 
@@ -124,16 +163,6 @@ a new version, update the version number in `version.rb`, and then run `bundle e
124
163
  release`, which will create a git tag for the version, push git commits and tags, and push
125
164
  the `.gem` file to [rubygems.org](https://rubygems.org).
126
165
 
127
- ## TODO
128
-
129
- Some of the things I am interested in doing in the near future.
130
-
131
- - More tests, didn't have the time to write it all
132
- - Local configuration file -> ex: `~/.xkpassword`
133
- - Check for a better dictionary
134
- - Ability to provide a dictionary (this should help languages other than English)
135
- - A black-list - words that will not show up in the generation
136
-
137
166
  ## Contributing
138
167
 
139
168
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/xkpassword.
data/doc/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # XKPassword Docs
2
+
3
+ Additional documentation for this gem lives in this folder.
4
+
5
+ - [Preset guide](presets.md)
6
+ - [Project README](../README.md) for installation, Ruby gem usage, CLI usage, and `~/.xkpassword` defaults
7
+ - [GitHub repository](https://github.com/jdeen/xkpassword)
8
+
9
+ Use these docs for feature-specific details, then jump back to the main README or repository as needed.
data/doc/presets.md ADDED
@@ -0,0 +1,37 @@
1
+ # Presets
2
+
3
+ Back to the [docs index](README.md) or the [project README](../README.md).
4
+
5
+ `XKPassword.generate` and `XKPassword::Generator#generate` accept a `:preset` option.
6
+ If omitted, `:preset` defaults to `:xkcd`.
7
+
8
+ ## Supported presets
9
+
10
+ | Preset | Defaults |
11
+ | --- | --- |
12
+ | `:xkcd` | 4 words, 4-8 letters, `-` separator, random per-word uppercasing |
13
+ | `:web32` | 4 words, 4-5 letters, `-` separator, random per-word uppercasing |
14
+ | `:wifi` | 6 words, 4-8 letters, `-` separator, random per-word uppercasing |
15
+ | `:security` | 6 words, 4-8 letters, space separator, lowercase words |
16
+ | `:apple_id` | 3 words, 4-7 letters, `-` separator, random per-word uppercasing |
17
+
18
+ ## Examples
19
+
20
+ ```ruby
21
+ require 'xkpassword'
22
+
23
+ XKPassword.generate
24
+ XKPassword.generate(preset: :wifi)
25
+ XKPassword.generate(preset: :security, separator: '.')
26
+ ```
27
+
28
+ You can pass a preset as a symbol or string. String values like `"apple_id"` and `"apple-id"`
29
+ are normalized to the matching preset.
30
+
31
+ Explicit options always win over the preset defaults, so this is valid:
32
+
33
+ ```ruby
34
+ XKPassword.generate(preset: :xkcd, words: 6, case_transform: :capitalize)
35
+ ```
36
+
37
+ For the full source and latest updates, see the [GitHub repository](https://github.com/jdeen/xkpassword).
data/exe/xkpassword CHANGED
@@ -1,48 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require 'optparse'
4
- require 'artii'
5
-
6
- require 'bundler/setup'
7
- require 'xkpassword'
8
-
9
- artii = Artii::Base.new font: 'standard'
10
- message = """
11
- #{ artii.asciify('XKPassword') }
12
- by Ziyan Junaideen
13
-
14
- How many times have you changed your password just because you forgot it?
15
- Well, you are not alone. In todays security requirements, passwords need
16
- to be secure and difficult to break. Passwords need to be secure, sure,
17
- but they can also be easy to remember. Follow up this XKCD article for more
18
- information - http://xkcd.com/936/
19
-
20
- This does exactly what the picture predicts. You can use this in your Ruby
21
- applications (Ex: Rails, Sinatra) or standalone if you install the gem (as
22
- you have done here).
23
-
24
- Wish you all the best keeping things secure.
25
-
26
- Ziyan Junaideen
27
- ziyan@jdeen.com
28
-
29
- """
30
-
31
- options = {}
32
- OptionParser.new do |opts|
33
- opts.banner = "Usage: ./exe/xkpassword [options]"
34
-
35
- opts.on('-v', '--version', 'Gem version') { options[:version] = true }
36
- opts.on('-i', '--info', 'Gem info') { options[:info] = true }
37
-
38
- opts.on('--words [INTEGER]', 'Number of wrods to be used in the generated password') { |words| options[:words] = words.to_i }
39
- opts.on('--min-length [INTEGER]', 'Minimum length of a word') { |min| options[:min_length] = min.to_i }
40
- opts.on('--max-length [INTEGER]', 'Maximum length of a word') { |max| options[:max_length] = max.to_i }
41
- opts.on('--separator [STRING]', 'The separator to separate password') { |separator| options[:separator] = separator }
42
- opts.on('--case-transform [STRING]', 'Transform each word using upcase, downcase, or capitalize') { |case_transform| options[:case_transform] = case_transform }
43
- end.parse!
44
-
45
- puts message if options[:info]
46
- puts XKPassword::VERSION if options[:version]
47
- puts XKPassword.generate(options) if !options[:info] && !options[:version]
4
+ require 'xkpassword/cli'
48
5
 
6
+ exit XKPassword::CLI.new.run
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'artii'
5
+
6
+ require 'xkpassword'
7
+ require 'xkpassword/config_file'
8
+
9
+ module XKPassword
10
+ # Command-line entrypoint for the xkpassword executable.
11
+ class CLI
12
+ INFO_MESSAGE = <<~TEXT.freeze
13
+ #{Artii::Base.new(font: 'standard').asciify('XKPassword')}
14
+ by Ziyan Junaideen
15
+
16
+ How many times have you changed your password just because you forgot it?
17
+ Well, you are not alone. In todays security requirements, passwords need
18
+ to be secure and difficult to break. Passwords need to be secure, sure,
19
+ but they can also be easy to remember. Follow up this XKCD article for more
20
+ information - http://xkcd.com/936/
21
+
22
+ This does exactly what the picture predicts. You can use this in your Ruby
23
+ applications (Ex: Rails, Sinatra) or standalone if you install the gem (as
24
+ you have done here).
25
+
26
+ Wish you all the best keeping things secure.
27
+
28
+ Ziyan Junaideen
29
+ ziyan@jdeen.com
30
+ TEXT
31
+
32
+ def initialize(argv = ARGV, stdout: $stdout, stderr: $stderr, env: ENV)
33
+ @argv = argv.dup
34
+ @stdout = stdout
35
+ @stderr = stderr
36
+ @config_file = XKPassword::ConfigFile.new(XKPassword::ConfigFile.default_path(env))
37
+ @options = {}
38
+ end
39
+
40
+ def run
41
+ option_parser.parse!(@argv)
42
+
43
+ return initialize_config if @options[:init]
44
+ return write_special_output if special_output?
45
+
46
+ options = @config_file.load.merge(@options)
47
+ write_line(XKPassword.generate(options))
48
+ rescue OptionParser::ParseError, XKPassword::ConfigFile::Error, ArgumentError => e
49
+ @stderr.puts(e.message)
50
+ 1
51
+ end
52
+
53
+ private
54
+
55
+ def option_parser
56
+ @option_parser ||= OptionParser.new do |opts|
57
+ opts.banner = 'Usage: xkpassword [options]'
58
+
59
+ add_command_options(opts)
60
+ add_generation_options(opts)
61
+ end
62
+ end
63
+
64
+ def add_command_options(opts)
65
+ opts.on('-v', '--version', 'Gem version') { @options[:version] = true }
66
+ opts.on('-i', '--info', 'Gem info') { @options[:info] = true }
67
+ opts.on('--init', "Create #{@config_file.path} with commented defaults") { @options[:init] = true }
68
+ opts.on('-h', '--help', 'Show help') { @options[:help] = true }
69
+ end
70
+
71
+ def add_generation_options(opts)
72
+ add_numeric_generation_options(opts)
73
+ add_text_generation_options(opts)
74
+ end
75
+
76
+ def add_numeric_generation_options(opts)
77
+ opts.on(
78
+ '--words [INTEGER]',
79
+ 'Number of words to use in the generated password'
80
+ ) { |words| @options[:words] = words.to_i }
81
+ opts.on('--min-length [INTEGER]', 'Minimum length of a word') { |min| @options[:min_length] = min.to_i }
82
+ opts.on('--max-length [INTEGER]', 'Maximum length of a word') { |max| @options[:max_length] = max.to_i }
83
+ end
84
+
85
+ def add_text_generation_options(opts)
86
+ opts.on(
87
+ '--separator [STRING]',
88
+ 'The separator between generated words'
89
+ ) { |separator| @options[:separator] = separator }
90
+ opts.on(
91
+ '--case-transform [STRING]',
92
+ 'Transform each word using upcase, downcase, or capitalize'
93
+ ) { |case_transform| @options[:case_transform] = case_transform }
94
+ add_preset_option(opts)
95
+ end
96
+
97
+ def add_preset_option(opts)
98
+ opts.on(
99
+ '--preset [STRING]',
100
+ 'Preset to use: xkcd, web32, wifi, security, or apple_id'
101
+ ) { |preset| @options[:preset] = preset }
102
+ end
103
+
104
+ def special_output?
105
+ @options[:help] || @options[:info] || @options[:version]
106
+ end
107
+
108
+ def write_special_output
109
+ write_line(option_parser) if @options[:help]
110
+ write_line(INFO_MESSAGE) if @options[:info]
111
+ write_line(XKPassword::VERSION) if @options[:version]
112
+ 0
113
+ end
114
+
115
+ def initialize_config
116
+ path = @config_file.init!
117
+ write_line("Created #{path}")
118
+ end
119
+
120
+ def write_line(message)
121
+ @stdout.puts(message)
122
+ 0
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'psych'
4
+
5
+ module XKPassword
6
+ # Loads and initializes the CLI config file stored at ~/.xkpassword.
7
+ class ConfigFile
8
+ Error = Class.new(StandardError)
9
+
10
+ ALLOWED_OPTIONS = %i[
11
+ preset
12
+ words
13
+ min_length
14
+ max_length
15
+ separator
16
+ case_transform
17
+ ].freeze
18
+ INTEGER_OPTIONS = %i[words min_length max_length].freeze
19
+ TEMPLATE = <<~YAML
20
+ # Global defaults for the xkpassword CLI.
21
+ # Remove the leading "#" from any setting you want to enable.
22
+ #
23
+ # Supported keys:
24
+ # preset: xkcd, web32, wifi, security, apple_id
25
+ # words: integer
26
+ # min_length: integer
27
+ # max_length: integer
28
+ # separator: string
29
+ # case_transform: upcase, downcase, capitalize
30
+ #
31
+ # Example:
32
+ # preset: wifi
33
+ # words: 6
34
+ # min_length: 4
35
+ # max_length: 8
36
+ # separator: "-"
37
+ # case_transform: capitalize
38
+ YAML
39
+
40
+ attr_reader :path
41
+
42
+ def initialize(path = self.class.default_path)
43
+ @path = path
44
+ end
45
+
46
+ def self.default_path(env = ENV)
47
+ home = env['HOME'] || Dir.home
48
+ File.expand_path('.xkpassword', home)
49
+ end
50
+
51
+ def load
52
+ return {} unless File.exist?(path)
53
+
54
+ data = Psych.safe_load(File.read(path), aliases: false)
55
+ return {} if data.nil?
56
+ raise Error, "#{path} must contain a YAML mapping of options" unless data.is_a?(Hash)
57
+
58
+ normalize_options(data)
59
+ rescue Psych::SyntaxError => e
60
+ raise Error, "Could not parse #{path}: #{e.message}"
61
+ end
62
+
63
+ def init!
64
+ raise Error, "#{path} already exists" if File.exist?(path)
65
+
66
+ File.write(path, TEMPLATE)
67
+ path
68
+ end
69
+
70
+ private
71
+
72
+ def normalize_options(data)
73
+ data.each_with_object({}) do |(key, value), normalized|
74
+ normalized_key = normalize_key(key)
75
+
76
+ unless ALLOWED_OPTIONS.include?(normalized_key)
77
+ raise Error, "Unsupported config option #{key.inspect} in #{path}"
78
+ end
79
+
80
+ normalized[normalized_key] = normalize_value(normalized_key, value)
81
+ end
82
+ end
83
+
84
+ def normalize_key(key)
85
+ key.to_s.strip.downcase.tr(' -', '_').to_sym
86
+ end
87
+
88
+ def normalize_value(key, value)
89
+ return normalize_integer(key, value) if INTEGER_OPTIONS.include?(key)
90
+
91
+ return value if value.nil? || value.is_a?(String) || value.is_a?(Symbol)
92
+
93
+ raise Error, "#{key} in #{path} should be a string or left unset"
94
+ end
95
+
96
+ def normalize_integer(key, value)
97
+ integer_value = value.is_a?(String) ? Integer(value, 10) : value
98
+ return integer_value if integer_value.is_a?(Integer)
99
+
100
+ raise Error, "#{key} in #{path} should be an integer"
101
+ rescue ArgumentError, TypeError
102
+ raise Error, "#{key} in #{path} should be an integer"
103
+ end
104
+ end
105
+ end
@@ -2,16 +2,60 @@ require 'xkpassword/words'
2
2
 
3
3
  # The Generator class which finds words based on the requirement and using the provided options to build a
4
4
  # new random passowrd.
5
+ # Presets provide convenient defaults for common password styles, and callers can override any
6
+ # preset-derived value by passing explicit generation options.
5
7
  #
6
8
  # @attr_reader [XKPassword::Words] words A word database that gen provide you words for the length required
7
9
  class XKPassword::Generator
8
- DEFAULTS = {
9
- case_transform: nil,
10
- max_length: 8,
11
- min_length: 4,
12
- separator: '-',
13
- words: 4,
10
+ # The preset used when `:preset` is omitted.
11
+ DEFAULT_PRESET = :xkcd
12
+
13
+ # Built-in password presets.
14
+ #
15
+ # - `:xkcd` generates 4 words between 4 and 8 letters separated by `-`, preserving the gem's
16
+ # original default behavior.
17
+ # - `:web32` generates 4 shorter words between 4 and 5 letters separated by `-`.
18
+ # - `:wifi` generates 6 words between 4 and 8 letters separated by `-`.
19
+ # - `:security` generates 6 lowercase words between 4 and 8 letters separated by spaces.
20
+ # - `:apple_id` generates 3 words between 4 and 7 letters separated by `-`.
21
+ PRESETS = {
22
+ xkcd: {
23
+ case_transform: nil,
24
+ max_length: 8,
25
+ min_length: 4,
26
+ separator: '-',
27
+ words: 4,
28
+ },
29
+ web32: {
30
+ case_transform: nil,
31
+ max_length: 5,
32
+ min_length: 4,
33
+ separator: '-',
34
+ words: 4,
35
+ },
36
+ wifi: {
37
+ case_transform: nil,
38
+ max_length: 8,
39
+ min_length: 4,
40
+ separator: '-',
41
+ words: 6,
42
+ },
43
+ security: {
44
+ case_transform: :downcase,
45
+ max_length: 8,
46
+ min_length: 4,
47
+ separator: ' ',
48
+ words: 6,
49
+ },
50
+ apple_id: {
51
+ case_transform: nil,
52
+ max_length: 7,
53
+ min_length: 4,
54
+ separator: '-',
55
+ words: 3,
56
+ },
14
57
  }
58
+ VALID_CASE_TRANSFORMS = [:upcase, :downcase, :capitalize]
15
59
 
16
60
  attr_reader :words
17
61
 
@@ -20,8 +64,14 @@ class XKPassword::Generator
20
64
  end
21
65
 
22
66
  # Generates a password absed on the configuration provided.
67
+ # A preset supplies a base configuration and any explicit options passed in `options` override
68
+ # those preset defaults.
23
69
  #
24
70
  # @param [Hash] options The options to populate a generator
71
+ # @option options [String, Symbol] :preset The preset to use. Supports `:xkcd`, `:web32`,
72
+ # `:wifi`, `:security`, and `:apple_id`.
73
+ # String values like `"apple_id"` and `"apple-id"`
74
+ # are normalized to `:apple_id`. Defaults to `:xkcd`.
25
75
  # @option options [Integer] :words The number of words to include in the generated password
26
76
  # @option options [String] :separator The separator symbol to use joining words used in password
27
77
  # @option options [Integer] :min_length The minimum length of a word to be used in the process
@@ -30,20 +80,37 @@ class XKPassword::Generator
30
80
  #
31
81
  # @return [String] The generated password
32
82
  #
33
- # @example Populating the method with all options (current default)
83
+ # @example Using the default xkcd preset
84
+ # generator = XKPassword::Generator.new
85
+ # generator.generate
86
+ #
87
+ # @example Using the security preset
88
+ # generator = XKPassword::Generator.new
89
+ # generator.generate(preset: :security)
90
+ #
91
+ # @example Populating the method with a preset and explicit overrides
34
92
  # options = {
93
+ # preset: :security,
35
94
  # separator: ' ',
36
- # words: 4,
95
+ # words: 6,
37
96
  # min_length: 4,
38
97
  # max_length: 8,
39
- # case_transform: :capitalize
98
+ # case_transform: :downcase
40
99
  # }
41
100
  #
42
101
  # generator = XKPassword::Generator.new
43
102
  # generator.generate(options)
103
+ #
104
+ # @example Built-in preset defaults
105
+ # :xkcd # 4 words, 4..8 letters, "-" separator, random per-word uppercasing
106
+ # :web32 # 4 words, 4..5 letters, "-" separator, random per-word uppercasing
107
+ # :wifi # 6 words, 4..8 letters, "-" separator, random per-word uppercasing
108
+ # :security # 6 words, 4..8 letters, " " separator, lowercase
109
+ # :apple_id # 3 words, 4..7 letters, "-" separator, random per-word uppercasing
44
110
  def generate(options = nil)
45
- options ||= {}
46
- options = DEFAULTS.merge(options)
111
+ options = (options || {}).dup
112
+ preset = normalize_preset(options.delete(:preset))
113
+ options = PRESETS.fetch(preset).merge(options)
47
114
  case_transform = normalize_case_transform(options[:case_transform])
48
115
  length_vals = (options[:min_length]..options[:max_length]).to_a
49
116
 
@@ -73,11 +140,20 @@ class XKPassword::Generator
73
140
  return nil if case_transform.nil?
74
141
 
75
142
  case_transform = case_transform.to_sym if case_transform.is_a?(String)
76
- valid_case_transforms = [:upcase, :downcase, :capitalize]
77
143
 
78
- return case_transform if valid_case_transforms.include?(case_transform)
144
+ return case_transform if VALID_CASE_TRANSFORMS.include?(case_transform)
145
+
146
+ fail ArgumentError, "case_transform should be one of: #{ VALID_CASE_TRANSFORMS.join(', ') }"
147
+ end
148
+
149
+ def normalize_preset(preset)
150
+ return DEFAULT_PRESET if preset.nil?
151
+
152
+ preset = preset.strip.downcase.tr(' -', '_').to_sym if preset.is_a?(String)
153
+
154
+ return preset if PRESETS.key?(preset)
79
155
 
80
- fail ArgumentError, "case_transform should be one of: #{ valid_case_transforms.join(', ') }"
156
+ fail ArgumentError, "preset should be one of: #{ PRESETS.keys.join(', ') }"
81
157
  end
82
158
 
83
159
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module XKPassword
2
- VERSION = '0.4.0'
4
+ VERSION = '0.6.0'
3
5
  end
data/lib/xkpassword.rb CHANGED
@@ -7,12 +7,29 @@ module XKPassword
7
7
  # use the `XKPassword::Generator` class as it will be faster since it will only need to load
8
8
  # the dictionary once.
9
9
  #
10
+ # Presets provide named defaults for common password styles, and any explicit options passed to
11
+ # this method override the selected preset.
12
+ #
10
13
  # @param [Hash] options The options to populate a generator
14
+ # @option options [String, Symbol] :preset The preset to use. Supports `:xkcd`, `:web32`,
15
+ # `:wifi`, `:security`, and `:apple_id`.
16
+ # Defaults to `:xkcd`.
17
+ # - `:xkcd` uses 4 words of 4..8 letters joined by `-`
18
+ # - `:web32` uses 4 words of 4..5 letters joined by `-`
19
+ # - `:wifi` uses 6 words of 4..8 letters joined by `-`
20
+ # - `:security` uses 6 lowercase words of 4..8 letters joined by spaces
21
+ # - `:apple_id` uses 3 words of 4..7 letters joined by `-`
11
22
  # @option options [Integer] :words The number of words to include in the generated password
12
23
  # @option options [String] :separator The separator symbol to use joining words used in password
13
24
  # @option options [Integer] :min_length The minimum length of a word to be used in the process
14
25
  # @option options [Integer] :max_length The maximum length of a word to be used in the process
15
26
  # @option options [String, Symbol] :case_transform The transform to apply to every generated word
27
+ #
28
+ # @example Generate with the default preset
29
+ # XKPassword.generate
30
+ #
31
+ # @example Generate with a preset and override one of its defaults
32
+ # XKPassword.generate(preset: :apple_id, separator: '.')
16
33
  def self.generate(options = nil)
17
34
  generator = XKPassword::Generator.new
18
35
  generator.generate(options)
@@ -22,4 +39,3 @@ end
22
39
 
23
40
  require 'xkpassword/version'
24
41
  require 'xkpassword/generator'
25
-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xkpassword
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ziyan Junaideen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-10 00:00:00.000000000 Z
11
+ date: 2026-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: artii
@@ -90,8 +90,12 @@ files:
90
90
  - bin/console
91
91
  - bin/setup
92
92
  - bin/xkpassword
93
+ - doc/README.md
94
+ - doc/presets.md
93
95
  - exe/xkpassword
94
96
  - lib/xkpassword.rb
97
+ - lib/xkpassword/cli.rb
98
+ - lib/xkpassword/config_file.rb
95
99
  - lib/xkpassword/data/google-10000-english-no-swears.txt
96
100
  - lib/xkpassword/generator.rb
97
101
  - lib/xkpassword/store.rb