elambot_secure_password 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5df4870c3b88f99bb82f6266a19d01398f6f656467226e55536e47a600479850
4
+ data.tar.gz: 459d62622440889b85090703218107b9d55e1eca8f6b901e582c422d6173cd16
5
+ SHA512:
6
+ metadata.gz: c7865342f1396814cba6147a77a7d43d9dbc262c4452411738669cf35cf53cf0874edfd97614a5e7bb9b0e3d54be5c8fc178817253901aa5bc0ae3ab4a19c9d7
7
+ data.tar.gz: 89d7c8e722037074f8b2ef9e3b2c5e8e928ca32933745aba74ada32d37c1310b7f54a8c8fd3f2ed0fa5caf6a663eff074a309972c23740aa9c6c890a5eca2f0e
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-03-18
4
+
5
+ - Initial release
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # ElambotSecurePassword
2
+
3
+ ElambotSecurePassword is a simple Ruby gem for generating secure passwords. It can be used within a Rails application
4
+ or as a simple password generation tool from the command line.
5
+
6
+ ## Installation
7
+
8
+ From the command line, install the gem by executing:
9
+
10
+ ```bash
11
+ gem install elambot_secure_password
12
+ ````
13
+ Or add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'elambot_secure_password'
17
+ ```
18
+
19
+ ## Getting started
20
+
21
+ To generate a password simply include the following lines in your application or run these commands in an irb session:
22
+
23
+ ```ruby
24
+ require 'elambot_secure_password'
25
+
26
+ ElambotSecurePassword.generate_password
27
+ ```
28
+
29
+ ## Available options
30
+
31
+ The `generate_password` method accepts a hash of options. The available options are:
32
+
33
+ - `length (Integer)` - (default: 10) - The length of the password to generate
34
+ - `lowercase (Boolean)` - (default: true) - Include one or more lowercase `a-z` characters.
35
+ - `uppercase (Boolean)` - (default: false) - Include one or more uppercase `A-Z` characters.
36
+ - `number` - (default: 0) - The exact number of numeric characters to include.
37
+ - `special` - (default: 0) - The exact number of special characters to include.
38
+
39
+ ## Examples
40
+
41
+ ### Generate a password with a length of 12 characters
42
+
43
+ ```ruby
44
+ ElambotSecurePassword.generate_password(12)
45
+ ```
46
+
47
+ ### Generate a password with a length of 12 characters and advanced options
48
+
49
+ ```ruby
50
+ ElambotSecurePassword.generate_password(12, {
51
+ uppercase: true, # Include uppercase characters
52
+ lowercase: true, # Include lowercase characters
53
+ special: 3, # Include 3 special characters
54
+ number: 5, # Include 5 numeric characters
55
+ })
56
+ ```
57
+
58
+ ## Error messaging
59
+
60
+ If the options provided are invalid, the gem will raise an error with a message indicating the issue.
61
+
62
+ ### Example (Invalid special character option)
63
+
64
+ ```ruby
65
+ ElambotSecurePassword.generate_password(12, {
66
+ special: true, # Invalid special character option
67
+ })
68
+
69
+ special option must be a number(ArgumentError)
70
+ ```
71
+
72
+ ## Notes on the project
73
+
74
+ I found this project to be a great learning experience. I had never created a Ruby gem before, so it was a great opportunity to learn how to do so.
75
+
76
+ I decided to use RubyMine's built in interface to set up the project. Now that I have a better
77
+ understanding of the file structure of a Ruby gem, if I were to do this project again I would like to set up the gem manually.
78
+
79
+ ### Approach
80
+
81
+ The main challenge I faced when developing the logic for the password creation was to ensure that the password would contain the selected
82
+ number of special characters and numbers whilst maintaining the specified length.
83
+
84
+ To solve this, I first subtracted the number of special characters and numbers from the length option and stored the result in a variable called `remaining_length`.
85
+ I then created an empty array to store the password characters and added the required number of special characters and numbers to the array.
86
+
87
+ Next, I used the `remaining_length` variable to add the remaining number of characters to the `characters` array. Either lowercase, uppercase or a mix of both depending on the options selected.
88
+
89
+ Finally, I shuffled the `characters` array and joined the elements to create the password.
90
+
91
+ I have chosen to return a default password of random lowercase characters if no options are provided, or if uppercase and lowercase are set to `false` and `special` and `number` are not set.
92
+ I felt this was a better approach than raising an error as it makes the gem more user-friendly.
93
+
94
+ ### Challenges
95
+
96
+ I didn't encounter any major difficulties developing the gem. RubyMine made setting up the gem very straightforward. And deciding on the logic, whilst challenging, was also very rewarding.
97
+
98
+ The main challenge was keeping the logic as clean and simple as possible. I'm happy with the result but there are definitely improvements I would like to make.
99
+
100
+ ### Improvements
101
+
102
+ If I were to continue working on this project my main area of focus would be the `validate_options` method. I would like to change the `if` statement I have used. I would
103
+ store validation conditions and error messages in a hash. I think setting the error message by selecting from a hash would make the code cleaner and easier to read. I have an idea
104
+ of how I would like to implement this, but I would like to spend more time on it.
105
+
106
+ I like to use a 'make it work, make it better, make it beautiful' approach when developing. I managed to get the gem working and defnitely improved upon my first attempt, but I would like
107
+ to spend more time making the code cleaner and more beautiful.
108
+
109
+ Overall I found the challenge really enjoyable. I am happy with it and learned a lot from it!
110
+
111
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElambotSecurePassword
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "elambot_secure_password/version"
4
+
5
+ module ElambotSecurePassword
6
+ def self.generate_password(length = 10, options = {})
7
+ validate_options(options.merge(length: length))
8
+
9
+ special_length = options[:special] || 0
10
+ number_length = options[:number] || 0
11
+ remaining_length = length - special_length - number_length
12
+
13
+ characters = []
14
+ characters.concat(special_characters(special_length)) if special_length.positive?
15
+ characters.concat([*0..9].sample(number_length)) if number_length.positive?
16
+
17
+ if options[:uppercase] && options[:lowercase]
18
+ characters.concat([*"A".."Z", *"a".."z"].sample(remaining_length))
19
+ elsif options[:uppercase]
20
+ characters.concat([*"A".."Z"].sample(remaining_length))
21
+ else
22
+ characters.concat([*"a".."z"].sample(remaining_length))
23
+ end
24
+
25
+ characters.shuffle.join
26
+ end
27
+
28
+ def self.validate_options(options)
29
+ error_message = if options[:length] < 10
30
+ "length option must be greater than 10"
31
+ elsif options[:special] && !options[:special].is_a?(Integer)
32
+ "special option must be a number"
33
+ elsif options[:number] && !options[:number].is_a?(Integer)
34
+ "number option must be a number"
35
+ elsif options[:number] && options[:number] > options[:length]
36
+ "number option must be less than or equal to length option"
37
+ elsif options[:special] && options[:special] > options[:length]
38
+ "special option must be less than or equal to length option"
39
+ elsif options[:special] && options[:number] && (options[:special].to_i + options[:number].to_i) > options[:length]
40
+ "special and number options combined must be less than or equal to length option"
41
+ elsif options[:uppercase] && !options[:uppercase].is_a?(TrueClass) && !options[:uppercase].is_a?(FalseClass)
42
+ "uppercase option must be a boolean"
43
+ elsif options[:lowercase] && !options[:lowercase].is_a?(TrueClass) && !options[:lowercase].is_a?(FalseClass)
44
+ "lowercase option must be a boolean"
45
+ end
46
+ raise ArgumentError, error_message if error_message
47
+ end
48
+
49
+ def self.special_characters(length)
50
+ %w(! @ # $ % ^ & \( \) { } [ ] - _ < > ?).sample(length)
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elambot_secure_password
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Baz86
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-03-20 00:00:00.000000000 Z
11
+ dependencies: []
12
+ email:
13
+ - thomasebale@gmail.com
14
+ executables: []
15
+ extensions: []
16
+ extra_rdoc_files: []
17
+ files:
18
+ - CHANGELOG.md
19
+ - README.md
20
+ - Rakefile
21
+ - lib/elambot_secure_password.rb
22
+ - lib/elambot_secure_password/version.rb
23
+ homepage: https://github.com/Baz86/elambot_secure_password
24
+ licenses:
25
+ - MIT
26
+ metadata:
27
+ homepage_uri: https://github.com/Baz86/elambot_secure_password
28
+ source_code_uri: https://github.com/Baz86/elambot_secure_password
29
+ changelog_uri: https://github.com/Baz86/elambot_secure_password/blob/main/CHANGELOG.md
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 3.1.0
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubygems_version: 3.6.2
45
+ specification_version: 4
46
+ summary: A simple secure password generator gem
47
+ test_files: []