electionbuddy-ruby 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4d7bab3a1ccbb73ba611b3f24efe2cb30f46f8971fbdfcc9f7199d4cd90a5d1
4
- data.tar.gz: f7ad3cf9058cf3e87e8b270223b593e8a3a3703a34d0a72f8441ac6a6a1111e1
3
+ metadata.gz: 05e63dbc220f5455343b5a43e4a2fa3754c377f7cb56aceb133acddf3a09fe84
4
+ data.tar.gz: 57afed7e7acaaf77e7ba70a7afae1d1efd56e058d403bf5eed9904d7e546d1d2
5
5
  SHA512:
6
- metadata.gz: fd1e47298c9ede0a03d6e2ff07be9118d7f41ccfacb1acf1635d55e06ddd63ea7309fa965fac97a5dc2ea1b8e20d0eed20714af0761390197d2389661456a734
7
- data.tar.gz: 61e1aac0c642c65f791f4b1f93f7602cc3e34c4c94f3a5d4875e223b483703dc56521a5961199d5675123d5c93b16110107d64a89eed38c93d133c7c221f3ca0
6
+ metadata.gz: 39d9ac1b3891a72787f0c2a80603d2e824e769109c04f977f9a7ef810bd78a34c0dd4403356d7658b67db80462297e59f8040df8f9ee10e4f96658c6f65f8b7c
7
+ data.tar.gz: b6f5c57a45c8075f25c515efeddf23825db14d8c694bf4fa9b6f70b919501d66bd4dffa9639cc940a0a809a8d0739febdd83f06b378e0247591fc9e844ab95fc
data/.rubocop.yml CHANGED
@@ -6,3 +6,19 @@ Style/StringLiterals:
6
6
 
7
7
  Style/StringLiteralsInInterpolation:
8
8
  EnforcedStyle: double_quotes
9
+
10
+ Style/Documentation:
11
+ Enabled: false
12
+
13
+ Naming/FileName:
14
+ Exclude:
15
+ - 'lib/electionbuddy-ruby.rb'
16
+
17
+ Metrics/MethodLength:
18
+ Max: 30
19
+
20
+ Metrics/ClassLength:
21
+ Max: 200
22
+
23
+ Layout/LineLength:
24
+ Max: 150
data/.yardopts ADDED
@@ -0,0 +1,10 @@
1
+ --markup markdown
2
+ --markup-provider redcarpet
3
+ --readme README.md
4
+ --title 'ElectionBuddy Ruby'
5
+ --protected
6
+ --private
7
+ lib/**/*.rb
8
+ -
9
+ LICENSE.txt
10
+ CHANGELOG.md
data/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
1
8
  ## [Unreleased]
2
9
 
10
+ ## [0.3.0] - 2024-11-18
11
+
12
+ ### Added
13
+
14
+ - Added Global configuration for setting the API key using `ElectionBuddy.configure` method
15
+ - Ability to retrieve voter list's validation results
16
+ - Automated gem documentation using YARD and GitHub Pages
17
+
18
+ ### Changed
19
+
20
+ - Update README.md with get validtion results usage instructions
21
+ - Update .rubocop.yml with new rules
22
+ - Enhance code documentation with detailed YARD comments
23
+
24
+ ### Fixed
25
+
26
+ - Fix changelog version ordering
27
+
28
+ ## [0.2.0] - 2024-11-07
29
+
30
+ ### Added
31
+
32
+ - Faraday as a runtime dependency
33
+ - Debug as a development dependency
34
+ - API call framework: resource-oriented design to structure and support API calls
35
+ - Ability to validate the voter list of a given vote
36
+
37
+ ### Changed
38
+
39
+ - Update README.md with usage instructions
40
+ - Update .rubocop.yml with new rules
41
+
42
+ ### Fixed
43
+
44
+ - Fix console startup issue caused by incorrect code reference
45
+
3
46
  ## [0.1.0] - 2024-10-29
4
47
 
5
48
  - Initial release
49
+
50
+ [unreleased]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.3.0...HEAD
51
+ [0.3.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.2.0...v0.3.0
52
+ [0.2.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.1.0...v0.2.0
53
+ [0.1.0]: https://github.com/electionbuddy/electionbuddy-ruby/releases/tag/v0.1.0
54
+
data/README.md CHANGED
@@ -1,24 +1,158 @@
1
- # Electionbuddy::Ruby
1
+ # ElectionBuddy Ruby
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
4
-
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/electionbuddy/ruby`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ A Ruby client for interacting with the [ElectionBuddy API](https://api.electionbuddy.com).
4
+ Explore the complete documentation at [https://electionbuddy.github.io/electionbuddy-ruby](https://electionbuddy.github.io/electionbuddy-ruby).
6
5
 
7
6
  ## Installation
8
7
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
10
-
11
- Install the gem and add to the application's Gemfile by executing:
8
+ Install the gem and add it to the application's Gemfile by executing:
12
9
 
13
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
10
+ bundle add electionbuddy-ruby
14
11
 
15
12
  If bundler is not being used to manage dependencies, install the gem by executing:
16
13
 
17
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
14
+ gem install electionbuddy-ruby
15
+
16
+ Alternatively, add the gem to the Gemfile:
17
+
18
+ ```ruby
19
+ gem 'electionbuddy-ruby'
20
+ ```
18
21
 
19
22
  ## Usage
20
23
 
21
- TODO: Write usage instructions here
24
+ Ensure you have an API key from Electionbuddy. You can configure the API key globally or initialize the client directly with the API key.
25
+
26
+ ### Global Configuration
27
+
28
+ #### Example in a Rails application
29
+
30
+ Create an initializer file in `config/initializers/electionbuddy.rb` and add the following code:
31
+
32
+ ```ruby
33
+ ElectionBuddy.configure do |config|
34
+ config.api_key = Rails.application.credentials.electionbuddy[:api_key]
35
+ end
36
+ ```
37
+
38
+ #### Client Initialization
39
+
40
+ You can now initialize the client without passing the API key if it has been configured globally:
41
+
42
+ ```ruby
43
+ client = ElectionBuddy::Client.new
44
+ ```
45
+
46
+ Alternatively, you can still pass the API key directly during initialization. If the API key is passed during initialization, it will take precedence over the global configuration.
47
+
48
+ ```ruby
49
+ client = ElectionBuddy::Client.new(api_key: 'your-api-key')
50
+ ```
51
+
52
+ ### Voter List Validation
53
+
54
+ To validate a voter list, use the `voter_list.validate(vote_id)` method:
55
+
56
+ ```ruby
57
+ validation = client.voter_list.validate(1)
58
+
59
+ if validation.done?
60
+ puts "Validation completed successfully! Identifier: #{validation.identifier}"
61
+ else
62
+ puts "Validation failed: #{validation.error}"
63
+ end
64
+ ```
65
+
66
+ ### Get the Validation Result
67
+
68
+ Once you have a validation identifier, you can check the validation results.
69
+
70
+ ```ruby
71
+ begin
72
+ validation_result = client.voter_list.validation_result('ae0a1724-9791-4bb2-8331-6d4e55a9b7c8')
73
+ if validation_result.valid?
74
+ puts "The voter list is valid!"
75
+ else
76
+ puts "The voter list is invalid!"
77
+ puts "Total errors count: #{validation_result.total_errors_count}"
78
+ end
79
+ rescue ElectionBuddy::Error => e
80
+ puts "Something went wrong: #{e.message}"
81
+ end
82
+ ```
83
+
84
+ The voter list validation can have two types of errors:
85
+
86
+ 1. **List-level errors**: Affect the entire voter list (e.g., missing required columns)
87
+ 2. **Line-level errors**: Affect specific lines in the voter list (e.g., invalid email format)
88
+
89
+ #### List-Level Errors
90
+
91
+ List-level errors can be retrieved using the `list_errors` method.
92
+
93
+ ```ruby
94
+ if validation_result.list_errors.any?
95
+ puts "List-level errors:"
96
+ validation_result.list_errors.each do |list_error|
97
+ puts "Error message: #{list_error.error_message}"
98
+ end
99
+ end
100
+ ```
101
+
102
+ #### Line-Level Errors
103
+
104
+ Line-level errors are paginated. You can specify the page number and the number of errors per page.
105
+ If you don't specify the page number and the number of errors per page, the default values are 1 and 10, respectively.
106
+
107
+ ```ruby
108
+ validation_result = client.voter_list.validation_result('ae0a1724-9791-4bb2-8331-6d4e55a9b7c8', page: 2, per_page: 10)
109
+ line_errors = validation_result.line_errors
110
+ puts "There is a total of #{line_errors.total} line-level errors."
111
+ puts "The current page is #{line_errors.page}."
112
+ puts "There are #{line_errors.per_page} errors per page."
113
+ puts "There are #{line_errors.total_pages} pages in total."
114
+ ```
115
+
116
+ PS: The `list_errors` object is always available, regardless of the line-level errors pagination.
117
+
118
+ To iterate over the line-level errors, you can use the following code:
119
+
120
+ ```ruby
121
+ if line_errors.any?
122
+ puts "Line-level errors for page #{line_errors.page}:"
123
+ line_errors.each do |line_error|
124
+ puts "Line identifier: #{line_error.voter_information_line_id}"
125
+ puts "Error messages:"
126
+ line_error.each do |line_error|
127
+ puts "Error messages #{line_error.error_messages}"
128
+ end
129
+ end
130
+ end
131
+ ```
132
+
133
+ ### Possible API Errors
134
+
135
+ The following errors may be raised by the API:
136
+
137
+ - **400**: Malformed request.
138
+ - **401**: Invalid authentication credentials.
139
+ - **403**: Unauthorized.
140
+ - **404**: Resource not found.
141
+ - **429**: Your request exceeded the API rate limit.
142
+ - **500**: We were unable to perform the request due to server-side problems.
143
+
144
+ Each error will raise an `Error` exception with a message detailing the status code and the error message returned by the API.
145
+
146
+ ## Documentation
147
+
148
+ The complete documentation for this gem is available at: https://electionbuddy.github.io/electionbuddy-ruby
149
+
150
+ You can also generate the documentation locally using:
151
+
152
+ ```bash
153
+ yard doc
154
+ yard server
155
+ ```
22
156
 
23
157
  ## Development
24
158
 
@@ -28,7 +162,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
28
162
 
29
163
  ## Contributing
30
164
 
31
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/electionbuddy-ruby.
165
+ Bug reports and pull requests are welcome on GitHub at https://github.com/electionbuddy/electionbuddy-ruby.
32
166
 
33
167
  ## License
34
168
 
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ # HTTP client for the ElectionBuddy API
5
+ #
6
+ # @example
7
+ # client = Client.new(api_key: "your-api-key")
8
+ # client.voter_list.validate(123)
9
+ #
10
+ class Client
11
+ BASE_URL = "https://secure.electionbuddy.com/api/v2"
12
+
13
+ # @param api_key [String, nil] The API key for authentication (optional if configured globally)
14
+ # @param adapter [Object, nil] The HTTP client adapter to use (optional, defaults to Faraday's default adapter)
15
+ # @param stubs [Object, nil] Test stubs for the adapter (optional)
16
+ def initialize(api_key: nil, adapter: Faraday.default_adapter, stubs: nil)
17
+ @api_key = api_key || ElectionBuddy.configuration.api_key
18
+ @adapter = adapter
19
+ @stubs = stubs
20
+ end
21
+
22
+ # Returns the voter list resource for handling voter-related operations
23
+ #
24
+ # @return [VoterListResource] The voter list resource
25
+ def voter_list
26
+ VoterListResource.new(connection)
27
+ end
28
+
29
+ private
30
+
31
+ def connection
32
+ @connection ||= Faraday.new(BASE_URL) do |conn|
33
+ conn.headers["authorization"] = @api_key
34
+ conn.request :json
35
+ conn.response :json, content_type: /\bjson$/
36
+ conn.adapter @adapter, @stubs
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ # Configuration options for the ElectionBuddy client
5
+ class Configuration
6
+ # @return [String, nil] The API key used for authentication
7
+ attr_accessor :api_key
8
+ end
9
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ class Validation
5
+ # Represents a single line validation error
6
+ #
7
+ # @example
8
+ # line_error = LineError.new({ "voter_information_line_id" => 123, "errors" => { "email" => ["is invalid"] } })
9
+ # line_error.voter_information_line_id #=> 123
10
+ # line_error.error_messages #=> ["email: is invalid"]
11
+ #
12
+ class LineError
13
+ # @return [Integer] The ID of the voter information line
14
+ attr_reader :voter_information_line_id
15
+
16
+ # @return [Hash] The errors hash containing categories and messages
17
+ attr_reader :errors
18
+
19
+ # @param line_error [Hash] The error hash containing line ID and errors
20
+ def initialize(line_error)
21
+ @voter_information_line_id = line_error["voter_information_line_id"]
22
+ @errors = line_error["errors"]
23
+ end
24
+
25
+ # Returns formatted error messages for each category
26
+ #
27
+ # @return [Array<String>] Array of formatted error messages
28
+ def error_messages
29
+ @errors.map do |(category, messages)|
30
+ "#{category}: #{messages.join(", ")}"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ class Validation
5
+ # Represents a collection of line-by-line validation errors
6
+ #
7
+ # @example
8
+ # line_errors = LineErrors.new(response)
9
+ # line_errors.total #=> 5
10
+ # line_errors.total_pages #=> 1
11
+ # line_errors.empty? #=> false
12
+ #
13
+ class LineErrors
14
+ include Enumerable
15
+
16
+ # @return [Integer] Total number of validation errors
17
+ attr_reader :total
18
+
19
+ # @return [Integer] Current page number
20
+ attr_reader :page
21
+
22
+ # @return [Integer] Number of items per page
23
+ attr_reader :per_page
24
+
25
+ # @param response [Hash] API response containing validation errors
26
+ def initialize(response)
27
+ @line_errors = response["voter_line_errors"]
28
+ @total = response["meta"]["total"]
29
+ @page = response["meta"]["page"]
30
+ @per_page = response["meta"]["per_page"]
31
+ end
32
+
33
+ # Calculates the total number of pages
34
+ #
35
+ # @return [Integer] Total number of pages
36
+ def total_pages
37
+ (total.to_f / per_page).ceil
38
+ end
39
+
40
+ # Iterates through each line error
41
+ #
42
+ # @return [Enumerator] Collection of line errors
43
+ def each(&block)
44
+ collection.each(&block)
45
+ end
46
+
47
+ # Checks if there are any line errors
48
+ #
49
+ # @return [Boolean] true if no errors exist, false otherwise
50
+ def empty?
51
+ collection.empty?
52
+ end
53
+
54
+ private
55
+
56
+ def collection
57
+ @collection ||= @line_errors.map { |line_error| LineError.new(line_error) }
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ class Validation
5
+ # Represents a single voter list validation error
6
+ #
7
+ # @example
8
+ # error = ListError.new({ "email" => ["is invalid", "is required"] })
9
+ # error.category #=> "email"
10
+ # error.messages #=> ["is invalid", "is required"]
11
+ # error.error_message #=> "email: is invalid, is required"
12
+ #
13
+ class ListError
14
+ # @return [Hash] The raw error data
15
+ attr_reader :error
16
+
17
+ # @return [String] The category of the error
18
+ attr_reader :category
19
+
20
+ # @return [Array<String>] The error messages
21
+ attr_reader :messages
22
+
23
+ # @param error [Hash] The error hash containing category and messages
24
+ def initialize(error)
25
+ @error = error
26
+ @category, @messages = error.first
27
+ end
28
+
29
+ # Returns a formatted error message
30
+ #
31
+ # @return [String] The formatted error message
32
+ def error_message
33
+ "#{category}: #{messages.join(", ")}"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ class Validation
5
+ # Represents a collection of voter list validation errors
6
+ #
7
+ # @example
8
+ # list_errors = ListErrors.new(response)
9
+ # list_errors.total #=> 2
10
+ # list_errors.empty? #=> false
11
+ #
12
+ class ListErrors
13
+ include Enumerable
14
+
15
+ # @return [Integer] Total number of validation errors
16
+ attr_reader :total
17
+
18
+ # @param response [Hash] API response containing validation errors
19
+ def initialize(response)
20
+ @list_errors = response["voter_list_errors"]
21
+ @total = response["meta"]["total"]
22
+ end
23
+
24
+ # Iterates through each list error
25
+ #
26
+ # @return [Enumerator] Collection of list errors
27
+ def each(&block)
28
+ collection.each(&block)
29
+ end
30
+
31
+ # Checks if there are any list errors
32
+ #
33
+ # @return [Boolean] true if no errors exist, false otherwise
34
+ def empty?
35
+ collection.empty?
36
+ end
37
+
38
+ private
39
+
40
+ def collection
41
+ @collection ||= @list_errors
42
+ .map { |category, messages| { category => messages } }
43
+ .map { |list_error| ListError.new(list_error) }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ class Validation
5
+ class UnavailableTotalErrorsCount < ElectionBuddy::Error; end
6
+ class UnavailableValidStatus < ElectionBuddy::Error; end
7
+
8
+ Success = ->(data) { { success?: true, data: data } }
9
+ Failure = ->(error) { { success?: false, error: error } }
10
+ private_constant :Success, :Failure
11
+
12
+ # Represents the result of a validation operation
13
+ #
14
+ # @example Check if validation passed
15
+ # result = Result.new(response)
16
+ # result.valid? #=> true
17
+ # result.total_errors_count #=> 0
18
+ #
19
+ # @example Handle validation errors
20
+ # result = Result.new(response)
21
+ # result.valid? #=> false
22
+ # result.total_errors_count #=> 3
23
+ # result.list_errors #=> #<ListErrors @total=1>
24
+ # result.line_errors #=> #<LineErrors @total=2>
25
+ #
26
+ # @example Handle API failures
27
+ # result = Result.new(response)
28
+ # result.valid? #=> raises UnavailableValidStatus
29
+ # result.total_errors_count #=> raises UnavailableTotalErrorsCount
30
+ # result.failure_message #=> "API call has failed - Error: Validation: not found"
31
+ class Result
32
+ # Initializes a new validation result
33
+ #
34
+ # @param response [Hash] The raw response from the validation API
35
+ def initialize(response)
36
+ @result = process_response(response)
37
+ end
38
+
39
+ # Returns the list of validation errors related to the voter list
40
+ # @return [ListErrors, Array] List validation errors or empty array if validation failed
41
+ def list_errors
42
+ return [] unless success?
43
+
44
+ @list_errors ||= ListErrors.new(@result[:data].dig("results", "voter_list_validations"))
45
+ end
46
+
47
+ # Returns the line-by-line validation errors
48
+ # @return [LineErrors, Array] Line validation errors or empty array if validation failed
49
+ def line_errors
50
+ return [] unless success?
51
+
52
+ @line_errors ||= LineErrors.new(@result[:data].dig("results", "voter_lines_validation"))
53
+ end
54
+
55
+ # Returns the total count of all validation errors
56
+ #
57
+ # @return [Integer] Total number of validation errors
58
+ # @raise [UnavailableTotalErrorsCount] if the API call failed
59
+ def total_errors_count
60
+ raise UnavailableTotalErrorsCount, failure_message if failure?
61
+
62
+ list_errors.total + line_errors.total
63
+ end
64
+
65
+ # Indicates if the validation passed without errors
66
+ #
67
+ # @return [Boolean] true if validation passed, false otherwise
68
+ # @raise [UnavailableValidStatus] if the API call failed
69
+ def valid?
70
+ raise UnavailableValidStatus, failure_message if failure?
71
+
72
+ total_errors_count.zero?
73
+ end
74
+
75
+ # Returns the error message if validation failed
76
+ #
77
+ # @return [String, nil] The formatted error message or nil if successful
78
+ def failure_error
79
+ return if success?
80
+
81
+ ::ElectionBuddy::ErrorFormatter.format(@result[:error])
82
+ end
83
+
84
+ # Returns a user-friendly failure message
85
+ #
86
+ # @return [String, nil] The failure message or nil if successful
87
+ def failure_message
88
+ return if success?
89
+
90
+ "API call has failed - Error: #{failure_error}"
91
+ end
92
+
93
+ private
94
+
95
+ def success?
96
+ @result[:success?]
97
+ end
98
+
99
+ def failure?
100
+ !success?
101
+ end
102
+
103
+ def process_response(response)
104
+ if response["error"]
105
+ Failure.call(response["error"])
106
+ else
107
+ Success.call(response)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ # Represents a validation operation response from the ElectionBuddy API
5
+ # This class handles both successful and failed validation responses
6
+ #
7
+ # @example
8
+ # validation = Validation.new(response_hash)
9
+ # validation.done? #=> true/false
10
+ #
11
+ class Validation
12
+ # @param response [Hash] The raw API response containing validation details
13
+ def initialize(response)
14
+ @response = response
15
+ end
16
+
17
+ # Returns the unique identifier for this validation
18
+ #
19
+ # @return [String] The validation identifier or "Not available" if not present
20
+ def identifier
21
+ @response["validation_identifier"] || "Not available"
22
+ end
23
+
24
+ # Checks if the validation completed successfully without errors
25
+ #
26
+ # @return [Boolean] true if validation completed without errors, false otherwise
27
+ def done?
28
+ @response["error"].nil?
29
+ end
30
+
31
+ # Returns formatted error message if validation failed
32
+ #
33
+ # @return [String, nil] Formatted error message or nil if validation was successful
34
+ def error
35
+ return nil if done?
36
+
37
+ ErrorFormatter.format(@response["error"])
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ # @api public
5
+ # Base error class for ElectionBuddy exceptions
6
+ #
7
+ # @example Raising a custom error
8
+ # raise ElectionBuddy::Error, "Something went wrong"
9
+ #
10
+ # @since 0.1.0
11
+ class Error < StandardError
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ # Formats error messages from hash responses
5
+ #
6
+ # @example
7
+ # ErrorFormatter.format({ "email" => ["is invalid", "is required"] })
8
+ # #=> "Email: is invalid, is required"
9
+ #
10
+ class ErrorFormatter
11
+ # Formats error messages from a hash into a human-readable string
12
+ #
13
+ # @param error_hash [Hash, nil] Hash containing error keys and messages
14
+ # @return [String, nil] Formatted error message or nil if no errors
15
+ def self.format(error_hash)
16
+ return if error_hash.nil? || error_hash.empty?
17
+
18
+ error_hash.map do |key, value|
19
+ formatted_key = key.gsub("_", " ").split.map(&:capitalize).join(" ")
20
+ formatted_value = value.is_a?(Array) ? value.join(", ") : value
21
+ "#{formatted_key}: #{formatted_value}"
22
+ end.join(", ")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ # Base class for API resources
5
+ #
6
+ # @example
7
+ # class VoterListResource < Resource
8
+ # def validate(vote_id)
9
+ # post_request("/api/v2/votes/voters/validations", vote_id: vote_id)
10
+ # end
11
+ # end
12
+ #
13
+ class Resource
14
+ # @param connection [Object] The HTTP client connection
15
+ def initialize(connection)
16
+ @connection = connection
17
+ end
18
+
19
+ # Makes a GET request to the API
20
+ #
21
+ # @param url [String] The API endpoint URL
22
+ # @param params [Hash] Query parameters
23
+ # @param headers [Hash] Request headers
24
+ # @return [Hash] The API response
25
+ def get_request(url, params = {}, headers = {}, &block)
26
+ handle_response(@connection.get(url, params, headers, &block))
27
+ end
28
+
29
+ # Makes a POST request to the API
30
+ #
31
+ # @param url [String] The API endpoint URL
32
+ # @param body [Hash] Request body parameters
33
+ # @param headers [Hash] Request headers
34
+ # @return [Hash] The API response
35
+ def post_request(url, body = {}, headers = {}, &block)
36
+ handle_response(@connection.post(url, body, headers, &block))
37
+ end
38
+
39
+ private
40
+
41
+ def handle_response(response)
42
+ return response.body if response.success? || [422, 423].include?(response.status)
43
+
44
+ raise_error(response.status, ErrorFormatter.format(response.body))
45
+ end
46
+
47
+ def raise_error(status, formatted_error)
48
+ base_message = error_messages(status)
49
+ message = if formatted_error
50
+ "Error #{status}: #{base_message} - #{formatted_error}."
51
+ else
52
+ "Error #{status}: #{base_message}."
53
+ end
54
+
55
+ raise Error, message
56
+ end
57
+
58
+ def error_messages(status)
59
+ messages = {
60
+ 400 => "Malformed request",
61
+ 401 => "Invalid authentication credentials",
62
+ 403 => "Unauthorized",
63
+ 404 => "Resource not found",
64
+ 429 => "Your request exceeded the API rate limit",
65
+ 500 => "We were unable to perform the request due to server-side problems"
66
+ }
67
+ messages.fetch(status, "Unexpected status code.")
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ # Handles voter list validation operations
5
+ #
6
+ # @example
7
+ # resource = VoterListResource.new(client)
8
+ # validation = resource.validate(123)
9
+ # result = resource.get_validation_result(validation.identifier)
10
+ #
11
+ class VoterListResource < Resource
12
+ # Initiates a validation for a given vote
13
+ #
14
+ # @param vote_id [Integer] The ID of the vote to validate
15
+ # @return [Validation] The validation response
16
+ def validate(vote_id)
17
+ response = post_request("/api/v2/votes/voters/validations", vote_id: vote_id.to_i)
18
+
19
+ Validation.new(response)
20
+ end
21
+
22
+ # Retrieves the validation results
23
+ #
24
+ # @param identifier [String] The validation identifier
25
+ # @param page [Integer, nil] The page number for paginated results (optional, default: 1)
26
+ # @param per_page [Integer, nil] The number of items per page (optional, default: 10)
27
+ # @return [Validation::Result] The validation results
28
+ def get_validation_result(identifier, page: 1, per_page: 10)
29
+ params = { "identifier" => identifier, "page" => page, "per_page" => per_page }
30
+ response = get_request("/api/v2/votes/voters/validations", params)
31
+
32
+ Validation::Result.new(response)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ # Current version of ElectionBuddy gem
5
+ #
6
+ # @api public
7
+ # @return [String] the current version of the gem
8
+ # @example Get current version
9
+ # ElectionBuddy::VERSION #=> "0.3.0"
10
+ VERSION = "0.3.0"
11
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "faraday/middleware"
5
+ require "json"
6
+
7
+ require "election_buddy/version"
8
+ require "election_buddy/client"
9
+ require "election_buddy/error"
10
+ require "election_buddy/resource"
11
+ require "election_buddy/resources/voter_list_resource"
12
+ require "election_buddy/entities/validation"
13
+ require "election_buddy/error_formatter"
14
+ require "election_buddy/configuration"
15
+ require "election_buddy/entities/validation/result"
16
+ require "election_buddy/entities/validation/line_error"
17
+ require "election_buddy/entities/validation/line_errors"
18
+ require "election_buddy/entities/validation/list_error"
19
+ require "election_buddy/entities/validation/list_errors"
20
+
21
+ # ElectionBuddy API client library
22
+ #
23
+ # @api public
24
+ # @example Configure the client
25
+ # ElectionBuddy.configure do |config|
26
+ # config.api_key = 'your-api-key'
27
+ # end
28
+ module ElectionBuddy
29
+ class << self
30
+ # @return [Configuration] Current configuration
31
+ attr_accessor :configuration
32
+
33
+ # Configures the ElectionBuddy client
34
+ #
35
+ # @yield [config] Configuration instance to be modified
36
+ # @yieldparam [Configuration] config The configuration instance
37
+ def configure
38
+ self.configuration ||= Configuration.new
39
+ yield(configuration)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "election_buddy"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: electionbuddy-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alberto Rocha
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2024-10-29 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2024-11-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faraday
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.12'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.12'
14
28
  description: This gem provides a Ruby SDK for easy interaction with the ElectionBuddy
15
29
  API
16
30
  email:
@@ -21,12 +35,26 @@ extra_rdoc_files: []
21
35
  files:
22
36
  - ".devcontainer/devcontainer.json"
23
37
  - ".rubocop.yml"
38
+ - ".yardopts"
24
39
  - CHANGELOG.md
25
40
  - LICENSE.txt
26
41
  - README.md
27
42
  - Rakefile
28
- - lib/electionbuddy/ruby.rb
29
- - lib/electionbuddy/ruby/version.rb
43
+ - lib/election_buddy.rb
44
+ - lib/election_buddy/client.rb
45
+ - lib/election_buddy/configuration.rb
46
+ - lib/election_buddy/entities/validation.rb
47
+ - lib/election_buddy/entities/validation/line_error.rb
48
+ - lib/election_buddy/entities/validation/line_errors.rb
49
+ - lib/election_buddy/entities/validation/list_error.rb
50
+ - lib/election_buddy/entities/validation/list_errors.rb
51
+ - lib/election_buddy/entities/validation/result.rb
52
+ - lib/election_buddy/error.rb
53
+ - lib/election_buddy/error_formatter.rb
54
+ - lib/election_buddy/resource.rb
55
+ - lib/election_buddy/resources/voter_list_resource.rb
56
+ - lib/election_buddy/version.rb
57
+ - lib/electionbuddy-ruby.rb
30
58
  - sig/electionbuddy/ruby.rbs
31
59
  homepage: https://github.com/electionbuddy/electionbuddy-ruby
32
60
  licenses:
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Electionbuddy
4
- module Ruby
5
- VERSION = "0.1.0"
6
- end
7
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "ruby/version"
4
-
5
- module Electionbuddy
6
- module Ruby
7
- class Error < StandardError; end
8
- # Your code goes here...
9
- end
10
- end