electionbuddy-ruby 0.2.0 → 0.4.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: 609fbcd8ec13547ffc65048afb495236d9eecbe0a787bea74bee39315b3e92c1
4
- data.tar.gz: 35682f5a36ea65fe229f9239cf14335b132e6ef94e7befc79d9e601338262e81
3
+ metadata.gz: 64c0f48e8da4cd5811d86506d945b853e4e284afd8a8041878d680bf46117d4d
4
+ data.tar.gz: b635df1e9e3f7f013c769818e2722f6224de5cea279bd5ad36364a80c6d9d15b
5
5
  SHA512:
6
- metadata.gz: 7cbf3bc35b59210fcf1a8a0e906dafa5112ea801568edc5aee762019edc3dcdc49936087e7c58613657b3d732af42400ea735da644f717e38d89a2e1eb8b074b
7
- data.tar.gz: 7fadf9a933f95c94b949dbec9533bf6c775cc25c16252e30f80d3895ab7c49408e29d01518e034d9d71148e6e258634ce01df6f7d4710625d21cfee4ff9ba431
6
+ metadata.gz: b0dc2ca445cb784a1d72b27c6bcd4a5f0221ea50ca87e577dd301e0603189a11623eb85f9d794005350486efd82ce62bdb40d1492afe5ef536cde58a39fdb060
7
+ data.tar.gz: 4438b38fefa9ebaf8d22d7b57bc71272042bf89eda5dfe459fe29d647fd1c7d0d6141e31c5befe25e7267c5010cf5fad67b7f8ab729ef067b155b68ceed07fea
data/.rubocop.yml CHANGED
@@ -15,4 +15,10 @@ Naming/FileName:
15
15
  - 'lib/electionbuddy-ruby.rb'
16
16
 
17
17
  Metrics/MethodLength:
18
- Max: 15
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
@@ -7,9 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [0.1.0] - 2024-10-29
10
+ ## [0.4.0] - 2024-11-20
11
11
 
12
- - Initial release
12
+ ### Added
13
+
14
+ - Ability to import voters to a vote
15
+ - Automated gem release using GitHub Actions
16
+
17
+ ## [0.3.0] - 2024-11-18
18
+
19
+ ### Added
20
+
21
+ - Added Global configuration for setting the API key using `ElectionBuddy.configure` method
22
+ - Ability to retrieve voter list's validation results
23
+ - Automated gem documentation using YARD and GitHub Pages
24
+
25
+ ### Changed
26
+
27
+ - Update README.md with get validtion results usage instructions
28
+ - Update .rubocop.yml with new rules
29
+ - Enhance code documentation with detailed YARD comments
30
+
31
+ ### Fixed
32
+
33
+ - Fix changelog version ordering
13
34
 
14
35
  ## [0.2.0] - 2024-11-07
15
36
 
@@ -29,7 +50,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
29
50
 
30
51
  - Fix console startup issue caused by incorrect code reference
31
52
 
32
- [unreleased]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.2.0...HEAD
53
+ ## [0.1.0] - 2024-10-29
54
+
55
+ - Initial release
56
+
57
+ [unreleased]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.4.0...HEAD
58
+ [0.4.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.3.0...v0.4.0
59
+ [0.3.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.2.0...v0.3.0
33
60
  [0.2.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.1.0...v0.2.0
34
61
  [0.1.0]: https://github.com/electionbuddy/electionbuddy-ruby/releases/tag/v0.1.0
35
62
 
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # ElectionBuddy Ruby
2
2
 
3
- A Ruby client for interacting with the ElectionBuddy API.
4
- You can find the API documentation [here](https://api.electionbuddy.com).
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).
5
5
 
6
6
  ## Installation
7
7
 
@@ -21,12 +21,55 @@ gem 'electionbuddy-ruby'
21
21
 
22
22
  ## Usage
23
23
 
24
- Ensure you have an API key from Electionbuddy, and initialize the client as follows:
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:
25
41
 
26
42
  ```ruby
27
- client = ElectionBuddy::Client.new('your-api-key')
43
+ client = ElectionBuddy::Client.new
28
44
  ```
29
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 Importation
53
+
54
+ To import a voter list for an election, use the `voter_list.import(vote_id, voters, append_mode: false)` method:
55
+
56
+ ```ruby
57
+ voters = [
58
+ { email: 'voter1@example.com', label: 'Voter One' },
59
+ { email: 'voter2@example.com', label: 'Voter Two' }
60
+ ]
61
+
62
+ import = client.voter_list.import(1, voters, append_mode: false)
63
+
64
+ if import.done?
65
+ puts "Import completed successfully! Identifier: #{import.identifier}"
66
+ else
67
+ puts "Import failed: #{import.error}"
68
+ end
69
+ ```
70
+
71
+ The `append_mode` parameter determines whether the voters should be appended to the existing list (`true`) or replace the existing list (`false`). The default value is `false`.
72
+
30
73
  ### Voter List Validation
31
74
 
32
75
  To validate a voter list, use the `voter_list.validate(vote_id)` method:
@@ -36,14 +79,79 @@ validation = client.voter_list.validate(1)
36
79
 
37
80
  if validation.done?
38
81
  puts "Validation completed successfully! Identifier: #{validation.identifier}"
39
- # "Validation completed successfully! Identifier: ae0a1724-9791-4bb2-8331-6d4e55a9b7c8"
40
82
  else
41
83
  puts "Validation failed: #{validation.error}"
42
- # "Validation failed: Vote: not found"
43
84
  end
44
85
  ```
45
86
 
46
- ### Possible Errors
87
+ ### Get the Validation Result
88
+
89
+ Once you have a validation identifier, you can check the validation results.
90
+
91
+ ```ruby
92
+ begin
93
+ validation_result = client.voter_list.validation_result('ae0a1724-9791-4bb2-8331-6d4e55a9b7c8')
94
+ if validation_result.valid?
95
+ puts "The voter list is valid!"
96
+ else
97
+ puts "The voter list is invalid!"
98
+ puts "Total errors count: #{validation_result.total_errors_count}"
99
+ end
100
+ rescue ElectionBuddy::Error => e
101
+ puts "Something went wrong: #{e.message}"
102
+ end
103
+ ```
104
+
105
+ The voter list validation can have two types of errors:
106
+
107
+ 1. **List-level errors**: Affect the entire voter list (e.g., missing required columns)
108
+ 2. **Line-level errors**: Affect specific lines in the voter list (e.g., invalid email format)
109
+
110
+ #### List-Level Errors
111
+
112
+ List-level errors can be retrieved using the `list_errors` method.
113
+
114
+ ```ruby
115
+ if validation_result.list_errors.any?
116
+ puts "List-level errors:"
117
+ validation_result.list_errors.each do |list_error|
118
+ puts "Error message: #{list_error.error_message}"
119
+ end
120
+ end
121
+ ```
122
+
123
+ #### Line-Level Errors
124
+
125
+ Line-level errors are paginated. You can specify the page number and the number of errors per page.
126
+ If you don't specify the page number and the number of errors per page, the default values are 1 and 10, respectively.
127
+
128
+ ```ruby
129
+ validation_result = client.voter_list.validation_result('ae0a1724-9791-4bb2-8331-6d4e55a9b7c8', page: 2, per_page: 10)
130
+ line_errors = validation_result.line_errors
131
+ puts "There is a total of #{line_errors.total} line-level errors."
132
+ puts "The current page is #{line_errors.page}."
133
+ puts "There are #{line_errors.per_page} errors per page."
134
+ puts "There are #{line_errors.total_pages} pages in total."
135
+ ```
136
+
137
+ PS: The `list_errors` object is always available, regardless of the line-level errors pagination.
138
+
139
+ To iterate over the line-level errors, you can use the following code:
140
+
141
+ ```ruby
142
+ if line_errors.any?
143
+ puts "Line-level errors for page #{line_errors.page}:"
144
+ line_errors.each do |line_error|
145
+ puts "Line identifier: #{line_error.voter_information_line_id}"
146
+ puts "Error messages:"
147
+ line_error.each do |line_error|
148
+ puts "Error messages #{line_error.error_messages}"
149
+ end
150
+ end
151
+ end
152
+ ```
153
+
154
+ ### Possible API Errors
47
155
 
48
156
  The following errors may be raised by the API:
49
157
 
@@ -56,6 +164,17 @@ The following errors may be raised by the API:
56
164
 
57
165
  Each error will raise an `Error` exception with a message detailing the status code and the error message returned by the API.
58
166
 
167
+ ## Documentation
168
+
169
+ The complete documentation for this gem is available at: https://electionbuddy.github.io/electionbuddy-ruby
170
+
171
+ You can also generate the documentation locally using:
172
+
173
+ ```bash
174
+ yard doc
175
+ yard server
176
+ ```
177
+
59
178
  ## Development
60
179
 
61
180
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,15 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
+ #
4
10
  class Client
5
11
  BASE_URL = "https://secure.electionbuddy.com/api/v2"
6
12
 
7
- def initialize(api_key, adapter: Faraday.default_adapter, stubs: nil)
8
- @api_key = api_key
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
9
18
  @adapter = adapter
10
19
  @stubs = stubs
11
20
  end
12
21
 
22
+ # Returns the voter list resource for handling voter-related operations
23
+ #
24
+ # @return [VoterListResource] The voter list resource
13
25
  def voter_list
14
26
  VoterListResource.new(connection)
15
27
  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,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElectionBuddy
4
+ # Represents an importation operation for voter lists
5
+ #
6
+ # @example
7
+ # importation = Importation.new(response)
8
+ # if importation.done?
9
+ # puts "Import successful: #{importation.identifier}"
10
+ # else
11
+ # puts "Import failed: #{importation.error}"
12
+ # end
13
+ #
14
+ class Importation
15
+ # @param response [Hash] The API response containing importation details
16
+ def initialize(response)
17
+ @response = response
18
+ end
19
+
20
+ # Returns the identifier for this importation
21
+ #
22
+ # @return [String] The importation identifier or "Not Available"
23
+ def identifier
24
+ @response["importation_identifier"] || "Not Available"
25
+ end
26
+
27
+ # Checks if the importation completed successfully
28
+ #
29
+ # @return [Boolean] true if importation was successful, false otherwise
30
+ def done?
31
+ @response["error"].nil?
32
+ end
33
+
34
+ # Returns the error message if importation failed
35
+ #
36
+ # @return [String, nil] The formatted error message or nil if importation was successful
37
+ def error
38
+ return nil if done?
39
+
40
+ ErrorFormatter.format(@response["error"])
41
+ end
42
+ end
43
+ 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
@@ -1,19 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
+ #
4
11
  class Validation
12
+ # @param response [Hash] The raw API response containing validation details
5
13
  def initialize(response)
6
14
  @response = response
7
15
  end
8
16
 
17
+ # Returns the unique identifier for this validation
18
+ #
19
+ # @return [String] The validation identifier or "Not available" if not present
9
20
  def identifier
10
21
  @response["validation_identifier"] || "Not available"
11
22
  end
12
23
 
24
+ # Checks if the validation completed successfully without errors
25
+ #
26
+ # @return [Boolean] true if validation completed without errors, false otherwise
13
27
  def done?
14
28
  @response["error"].nil?
15
29
  end
16
30
 
31
+ # Returns formatted error message if validation failed
32
+ #
33
+ # @return [String, nil] Formatted error message or nil if validation was successful
17
34
  def error
18
35
  return nil if done?
19
36
 
@@ -1,6 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
4
11
  class Error < StandardError
5
12
  end
6
13
  end
@@ -1,7 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
+ #
4
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
5
15
  def self.format(error_hash)
6
16
  return if error_hash.nil? || error_hash.empty?
7
17
 
@@ -1,15 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
+ #
4
13
  class Resource
14
+ # @param connection [Object] The HTTP client connection
5
15
  def initialize(connection)
6
16
  @connection = connection
7
17
  end
8
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
9
25
  def get_request(url, params = {}, headers = {}, &block)
10
26
  handle_response(@connection.get(url, params, headers, &block))
11
27
  end
12
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
13
35
  def post_request(url, body = {}, headers = {}, &block)
14
36
  handle_response(@connection.post(url, body, headers, &block))
15
37
  end
@@ -17,7 +39,7 @@ module ElectionBuddy
17
39
  private
18
40
 
19
41
  def handle_response(response)
20
- return response.body if response.success? || response.status == 422
42
+ return response.body if response.success? || [422, 423].include?(response.status)
21
43
 
22
44
  raise_error(response.status, ErrorFormatter.format(response.body))
23
45
  end
@@ -1,11 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
+ #
4
11
  class VoterListResource < Resource
12
+ # Imports voters for a given vote
13
+ #
14
+ # @param vote_id [Integer] The ID of the vote to import voters for
15
+ # @param voters [Array<Hash>] Array of voter data to import
16
+ # @param append_mode [Boolean] Whether to append voters to existing list (default: false)
17
+ # @return [Importation] The importation response
18
+ def import(vote_id, voters, append_mode: false)
19
+ response = post_request("/api/v2/votes/voters/importations", vote_id: vote_id.to_i, voters: voters, append_mode: append_mode)
20
+
21
+ Importation.new(response)
22
+ end
23
+
24
+ # Initiates a validation for a given vote
25
+ #
26
+ # @param vote_id [Integer] The ID of the vote to validate
27
+ # @return [Validation] The validation response
5
28
  def validate(vote_id)
6
29
  response = post_request("/api/v2/votes/voters/validations", vote_id: vote_id.to_i)
7
30
 
8
31
  Validation.new(response)
9
32
  end
33
+
34
+ # Retrieves the validation results
35
+ #
36
+ # @param identifier [String] The validation identifier
37
+ # @param page [Integer, nil] The page number for paginated results (optional, default: 1)
38
+ # @param per_page [Integer, nil] The number of items per page (optional, default: 10)
39
+ # @return [Validation::Result] The validation results
40
+ def get_validation_result(identifier, page: 1, per_page: 10)
41
+ params = { "identifier" => identifier, "page" => page, "per_page" => per_page }
42
+ response = get_request("/api/v2/votes/voters/validations", params)
43
+
44
+ Validation::Result.new(response)
45
+ end
10
46
  end
11
47
  end
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElectionBuddy
4
- VERSION = "0.2.0"
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.4.0"
10
+ VERSION = "0.4.0"
5
11
  end
@@ -10,7 +10,34 @@ require "election_buddy/error"
10
10
  require "election_buddy/resource"
11
11
  require "election_buddy/resources/voter_list_resource"
12
12
  require "election_buddy/entities/validation"
13
+ require "election_buddy/entities/importation"
13
14
  require "election_buddy/error_formatter"
15
+ require "election_buddy/configuration"
16
+ require "election_buddy/entities/validation/result"
17
+ require "election_buddy/entities/validation/line_error"
18
+ require "election_buddy/entities/validation/line_errors"
19
+ require "election_buddy/entities/validation/list_error"
20
+ require "election_buddy/entities/validation/list_errors"
14
21
 
22
+ # ElectionBuddy API client library
23
+ #
24
+ # @api public
25
+ # @example Configure the client
26
+ # ElectionBuddy.configure do |config|
27
+ # config.api_key = 'your-api-key'
28
+ # end
15
29
  module ElectionBuddy
30
+ class << self
31
+ # @return [Configuration] Current configuration
32
+ attr_accessor :configuration
33
+
34
+ # Configures the ElectionBuddy client
35
+ #
36
+ # @yield [config] Configuration instance to be modified
37
+ # @yieldparam [Configuration] config The configuration instance
38
+ def configure
39
+ self.configuration ||= Configuration.new
40
+ yield(configuration)
41
+ end
42
+ end
16
43
  end
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.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alberto Rocha
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2024-11-07 00:00:00.000000000 Z
12
+ date: 2024-11-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
@@ -35,13 +35,21 @@ extra_rdoc_files: []
35
35
  files:
36
36
  - ".devcontainer/devcontainer.json"
37
37
  - ".rubocop.yml"
38
+ - ".yardopts"
38
39
  - CHANGELOG.md
39
40
  - LICENSE.txt
40
41
  - README.md
41
42
  - Rakefile
42
43
  - lib/election_buddy.rb
43
44
  - lib/election_buddy/client.rb
45
+ - lib/election_buddy/configuration.rb
46
+ - lib/election_buddy/entities/importation.rb
44
47
  - lib/election_buddy/entities/validation.rb
48
+ - lib/election_buddy/entities/validation/line_error.rb
49
+ - lib/election_buddy/entities/validation/line_errors.rb
50
+ - lib/election_buddy/entities/validation/list_error.rb
51
+ - lib/election_buddy/entities/validation/list_errors.rb
52
+ - lib/election_buddy/entities/validation/result.rb
45
53
  - lib/election_buddy/error.rb
46
54
  - lib/election_buddy/error_formatter.rb
47
55
  - lib/election_buddy/resource.rb
@@ -72,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
80
  - !ruby/object:Gem::Version
73
81
  version: '0'
74
82
  requirements: []
75
- rubygems_version: 3.5.16
83
+ rubygems_version: 3.2.33
76
84
  signing_key:
77
85
  specification_version: 4
78
86
  summary: Ruby SDK for the ElectionBuddy API.