zkt_client 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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.byebug_history +256 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +20 -0
  5. data/CHANGELOG.md +5 -0
  6. data/CODE_OF_CONDUCT.md +132 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +854 -0
  9. data/Rakefile +12 -0
  10. data/lib/generators/zkt_client/USAGE +1 -0
  11. data/lib/generators/zkt_client/install_generator.rb +18 -0
  12. data/lib/generators/zkt_client/templates/configure_template.rake +10 -0
  13. data/lib/zkt_client/access_token.rb +36 -0
  14. data/lib/zkt_client/configuration.rb +55 -0
  15. data/lib/zkt_client/exceptions.rb +11 -0
  16. data/lib/zkt_client/http_client.rb +111 -0
  17. data/lib/zkt_client/models/area.rb +14 -0
  18. data/lib/zkt_client/models/base.rb +67 -0
  19. data/lib/zkt_client/models/concerns/helperable.rb +84 -0
  20. data/lib/zkt_client/models/concerns/validatable.rb +86 -0
  21. data/lib/zkt_client/models/department.rb +14 -0
  22. data/lib/zkt_client/models/device.rb +14 -0
  23. data/lib/zkt_client/models/employee.rb +40 -0
  24. data/lib/zkt_client/models/position.rb +14 -0
  25. data/lib/zkt_client/models/transaction.rb +16 -0
  26. data/lib/zkt_client/monky_patcher/array.rb +28 -0
  27. data/lib/zkt_client/monky_patcher/base.rb +17 -0
  28. data/lib/zkt_client/monky_patcher/false.rb +15 -0
  29. data/lib/zkt_client/monky_patcher/hash.rb +5 -0
  30. data/lib/zkt_client/monky_patcher/integer.rb +15 -0
  31. data/lib/zkt_client/monky_patcher/nil.rb +15 -0
  32. data/lib/zkt_client/monky_patcher/string.rb +15 -0
  33. data/lib/zkt_client/monky_patcher/true.rb +15 -0
  34. data/lib/zkt_client/version.rb +5 -0
  35. data/lib/zkt_client.rb +46 -0
  36. data/sig/zkt_client.rbs +4 -0
  37. metadata +212 -0
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1 @@
1
+ rails generate zkt_client:install
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zkt_client"
4
+
5
+ module ZktClient
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ desc "This generator creates an initializer file at config/initializers, " \
9
+ "with the default configuration options for ZktClient."
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ # copy rake tasks
13
+ def copy_tasks
14
+ template "configure_template.rake", "config/initializers/zkt_client.rb"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ ZktClient.configure do |config|
4
+ # ZktClient configurations
5
+ # config.host = 'http://localhost:3000' # Required
6
+ # config.username = 'admin' # Required unless access_token is set
7
+ # config.password = 'admin' # Required unless access_token is set
8
+ # config.access_token = 'YOUR_ACCESS_TOKEN' # Required unless username and password are set
9
+ # config.is_object_response_enabled = true # Optional default is false
10
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require_relative "zkt_client/http_client"
4
+
5
+ module ZktClient
6
+ # AccessToken class handles the retrieval of access token
7
+ class AccessToken
8
+ BASE_URL = "/api-token-auth/"
9
+
10
+ # Calls the access token retrieval
11
+ #
12
+ # @return [String] the access token prefixed with "Token "
13
+ def call
14
+ "Token #{access_token}"
15
+ end
16
+
17
+ private
18
+
19
+ # Retrieves the access token from the server
20
+ #
21
+ # @return [String] the access token
22
+ def access_token
23
+ HttpClient.post(url: BASE_URL, params:)["token"]
24
+ end
25
+
26
+ # Constructs the parameters for the access token request
27
+ #
28
+ # @return [Hash] the parameters including username and password
29
+ def params
30
+ {
31
+ username: ZktClient.username,
32
+ password: ZktClient.password
33
+ }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+
5
+ module ZktClient
6
+ # Configuration class handles the configuration settings for ZktClient
7
+ class Configuration
8
+ include Singleton
9
+
10
+ attr_writer :host, :username, :password, :access_token, :is_object_response_enabled
11
+
12
+ # Retrieves the host configuration
13
+ #
14
+ # @return [String, nil] the host or nil if not set
15
+ def host
16
+ @host || ENV["ZKT_CLIENT_HOST"]
17
+ end
18
+
19
+ # Retrieves the username configuration
20
+ #
21
+ # @return [String, nil] the username or nil if not set
22
+ def username
23
+ @username || ENV["ZKT_CLIENT_USERNAME"]
24
+ end
25
+
26
+ # Retrieves the password configuration
27
+ #
28
+ # @return [String, nil] the password or nil if not set
29
+ def password
30
+ @password || ENV["ZKT_CLIENT_PASSWORD"]
31
+ end
32
+
33
+ # Retrieves the access token configuration
34
+ #
35
+ # @return [String, nil] the access token or nil if not set
36
+ def access_token
37
+ @access_token || ENV["ZKT_CLIENT_ACCESS_TOKEN"]
38
+ end
39
+
40
+ # Checks if object response is enabled
41
+ #
42
+ # @return [Boolean] true if object response is enabled, false otherwise
43
+ def with_object_response?
44
+ @is_object_response_enabled || ENV["ZKT_OBJECT_RESPONSE_ENABLED"] || false
45
+ end
46
+
47
+ # Checks if the client is configured
48
+ #
49
+ # @return [Boolean] true if the client is configured, false otherwise
50
+ def configured?
51
+ host.present? &&
52
+ (access_token.present? || (username.present? && password.present?))
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ZktClient
4
+ class Error < StandardError; end
5
+ class MissingConfigurationError < Error; end
6
+ class RecordNotFound < Error; end
7
+ class BadRequestError < Error; end
8
+ class UnprocessableEntityError < Error; end
9
+ class ServerError < Error; end
10
+ class RequestFailedError < Error; end
11
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+
5
+ module ZktClient
6
+ # HttpClient class handles HTTP requests for ZktClient
7
+ class HttpClient
8
+ class << self
9
+ # Makes a GET HTTP request
10
+ #
11
+ # @param url [String] the URL to send the request to
12
+ # @param headers [Hash] the headers to include in the request
13
+ # @param params [Hash] the query parameters to include in the request
14
+ # @return [Hash] the response body
15
+ def get(url:, headers:, params: {})
16
+ do_request(:get, url, params:, headers:)
17
+ end
18
+
19
+ # Makes a POST HTTP request
20
+ #
21
+ # @param url [String] the URL to send the request to
22
+ # @param params [Hash] the body of the request
23
+ # @param headers [Hash] the headers to include in the request
24
+ # @return [Hash] the response body
25
+ def post(url:, params:, headers: {})
26
+ do_request(:post, url, body: params, headers:)
27
+ end
28
+
29
+ # Makes a PUT HTTP request
30
+ #
31
+ # @param url [String] the URL to send the request to
32
+ # @param params [Hash] the body of the request
33
+ # @param headers [Hash] the headers to include in the request
34
+ # @return [Hash] the response body
35
+ def put(url:, params:, headers:)
36
+ do_request(:put, url, body: params, headers:)
37
+ end
38
+
39
+ # Makes a DELETE HTTP request
40
+ #
41
+ # @param url [String] the URL to send the request to
42
+ # @param headers [Hash] the headers to include in the request
43
+ # @return [Hash] the response body
44
+ def delete(url:, headers:)
45
+ do_request(:delete, url, headers:)
46
+ end
47
+
48
+ private
49
+
50
+ # Makes an HTTP request
51
+ #
52
+ # @param method [Symbol] the HTTP method to use (:get, :post, :put, :delete)
53
+ # @param url [String] the URL to send the request to
54
+ # @param params [Hash] the query parameters to include in the request
55
+ # @param body [Hash] the body of the request
56
+ # @param headers [Hash] the headers to include in the request
57
+ # @return [Hash] the response body
58
+ def do_request(method, url, params: {}, body: {}, headers: {})
59
+ uri = ZktClient.host + url
60
+
61
+ request = connection.public_send(method, uri) do |req|
62
+ req.headers = headers if headers.present?
63
+ req.body = body if body.present?
64
+ req.body = req.body.to_json if headers.present?
65
+ req.params = params if params.present?
66
+ end
67
+
68
+ validate_status!(request) unless request.success?
69
+ request.body
70
+ end
71
+
72
+ # Establishes a Faraday connection
73
+ #
74
+ # @return [Faraday::Connection] the Faraday connection
75
+ def connection
76
+ Faraday.new do |conn|
77
+ conn.request :url_encoded
78
+ conn.response :json
79
+ # conn.options[:timeout] = ZktClient.timeout
80
+ conn.adapter Faraday.default_adapter
81
+ end
82
+ end
83
+
84
+ # Validates the status of the HTTP response
85
+ #
86
+ # @param request [Faraday::Response] the HTTP response
87
+ # @raise [ZktClient::RecordNotFound] if the response status is 404
88
+ # @raise [ZktClient::BadRequestError] if the response status is 400
89
+ # @raise [ZktClient::UnprocessableEntityError] if the response status is 422
90
+ # @raise [ZktClient::ServerError] if the response status is 500-599
91
+ # @raise [ZktClient::RequestFailedError] for other response statuses
92
+ def validate_status!(request)
93
+ response = request.body
94
+
95
+ case request.status
96
+ when 404
97
+ raise(ZktClient::RecordNotFound, response)
98
+ when 400
99
+ raise(ZktClient::BadRequestError, response)
100
+ when 422
101
+ raise(ZktClient::UnprocessableEntityError, response)
102
+ when 500..599
103
+ raise(ZktClient::ServerError, response)
104
+ else
105
+ raise(ZktClient::RequestFailedError, response)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ZktClient
6
+ # Area class handles operations related to areas in the ZktClient
7
+ class Area < Base
8
+ # URL endpoints for the Area resource
9
+ URLS = { base: "/personnel/api/areas/" }.freeze
10
+
11
+ # Required fields for creating or updating an Area resource
12
+ REQUIRED_FEILDS = %i[area_code area_name].freeze
13
+ end
14
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../models/concerns/helperable"
4
+ require_relative "../models/concerns/validatable"
5
+
6
+ module ZktClient
7
+ # Base class for ZktClient module
8
+ class Base
9
+ extend Helperable
10
+ extend Validatable
11
+
12
+ class << self
13
+ # Shows the details of a specific resource by ID
14
+ #
15
+ # @param id [Integer] the ID of the resource
16
+ # @return [Hash] the response from the server
17
+ def show(id)
18
+ response = HttpClient.get(url: url_with_id(id), headers:)
19
+
20
+ build_response(response)
21
+ end
22
+
23
+ # Lists all resources with optional parameters
24
+ #
25
+ # @param params [Hash] optional parameters for filtering the list
26
+ # @return [Array<Hash>] the list of resources
27
+ def list(**params)
28
+ response = HttpClient.get(url: self::URLS[:base], headers:, params:)
29
+
30
+ build_response(response)
31
+ end
32
+
33
+ # Creates a new resource with the given parameters
34
+ #
35
+ # @param params [Hash] the parameters for creating the resource
36
+ # @return [Hash] the response from the server
37
+ def create(params)
38
+ validate_create_params!(params)
39
+ response = HttpClient.post(url: self::URLS[:base], headers:, params:)
40
+
41
+ build_response(response)
42
+ end
43
+
44
+ # Updates an existing resource by ID with the given parameters
45
+ #
46
+ # @param id [Integer] the ID of the resource
47
+ # @param params [Hash] the parameters for updating the resource
48
+ # @return [Hash] the response from the server
49
+ def update(id, params)
50
+ validate_update_params!(params)
51
+ response = HttpClient.put(url: url_with_id(id), headers:, params:)
52
+
53
+ build_response(response)
54
+ end
55
+
56
+ # Deletes a resource by ID
57
+ #
58
+ # @param id [Integer] the ID of the resource
59
+ # @return [Boolean] true if the resource was successfully deleted
60
+ def delete(id)
61
+ HttpClient.delete(url: url_with_id(id), headers:)
62
+
63
+ true
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ostruct"
4
+
5
+ module ZktClient
6
+ # Helperable module provides utility methods for ZktClient
7
+ module Helperable
8
+ private
9
+
10
+ # Validates the parameters for creating a resource
11
+ #
12
+ # @param params [Hash] the parameters to validate
13
+ # @raise [ArgumentError] if required fields are missing or blank
14
+ def validate_create_params!(params)
15
+ self::REQUIRED_FEILDS.each do |field|
16
+ validate!(params, field, required: true, blank: false)
17
+ end
18
+
19
+ params.except(*self::REQUIRED_FEILDS).each_key do |field|
20
+ validate!(params, field.to_sym, blank: false)
21
+ end
22
+ end
23
+
24
+ # Validates the parameters for updating a resource
25
+ #
26
+ # @param params [Hash] the parameters to validate
27
+ # @raise [ArgumentError] if any fields are blank
28
+ def validate_update_params!(params)
29
+ params.each_key do |field|
30
+ validate!(params, field.to_sym, blank: false)
31
+ end
32
+ end
33
+
34
+ # Builds the response from the server
35
+ #
36
+ # @param response [Hash] the response from the server
37
+ # @return [Array<OpenStruct>, Hash] the processed response
38
+ def build_response(response)
39
+ return response unless ZktClient.with_object_response?
40
+
41
+ if response["data"].is_a?(Array)
42
+ response["data"].map { |hash| init_object(hash) }
43
+ else
44
+ init_object(response)
45
+ end
46
+ end
47
+
48
+ # Initializes an OpenStruct object from a hash
49
+ #
50
+ # @param hash [Hash] the hash to convert to an OpenStruct object
51
+ # @return [OpenStruct] the initialized OpenStruct object
52
+ def init_object(hash)
53
+ OpenStruct.new(hash)
54
+ end
55
+
56
+ # Retrieves the access token for the API
57
+ #
58
+ # @raise [ZktClient::MissingConfigurationError] if the client is not configured
59
+ # @return [String] the access token
60
+ def access_token
61
+ raise(ZktClient::MissingConfigurationError, "Configurations are missing!") unless ZktClient.configured?
62
+
63
+ ZktClient.access_token || AccessToken.new.call
64
+ end
65
+
66
+ # Constructs a URL with the given resource ID
67
+ #
68
+ # @param id [Integer] the ID of the resource
69
+ # @return [String] the constructed URL
70
+ def url_with_id(id)
71
+ "#{self::URLS[:base]}#{id}/"
72
+ end
73
+
74
+ # Generates the headers for the HTTP request
75
+ #
76
+ # @return [Hash] the headers for the request
77
+ def headers
78
+ {
79
+ "Content-Type" => "application/json",
80
+ "Authorization" => access_token
81
+ }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ZktClient
4
+ # Validatable module provides validation methods for ZktClient
5
+ module Validatable
6
+ private
7
+
8
+ # Validates the given parameters based on the provided options
9
+ #
10
+ # @param params [Hash] the parameters to validate
11
+ # @param attribute [Symbol] the attribute to validate
12
+ # @param options [Hash] the validation options
13
+ # @option options [Boolean] :required whether the attribute is required
14
+ # @option options [Boolean] :blank whether the attribute can be blank
15
+ # @option options [Class] :type the expected type of the attribute
16
+ # @option options [Array] :in the allowed values for the attribute
17
+ # @raise [ArgumentError] if validation fails
18
+ # @return [void]
19
+ def validate!(params, attribute, **options)
20
+ raise(ArgumentError, "Params must be hash") unless params.is_a?(Hash)
21
+
22
+ validate_required!(params, attribute, options)
23
+ validate_blank!(params, attribute, options)
24
+ validate_value!(params, attribute, options)
25
+ validate_type!(params, attribute, options)
26
+ end
27
+
28
+ # Validates that the attribute is present if required
29
+ #
30
+ # @param params [Hash] the parameters to validate
31
+ # @param attribute [Symbol] the attribute to validate
32
+ # @param options [Hash] the validation options
33
+ # @option options [Boolean] :required whether the attribute is required
34
+ # @raise [ArgumentError] if the attribute is required but not present
35
+ # @return [void]
36
+ def validate_required!(params, attribute, options)
37
+ return unless options[:required] && !params.key?(attribute)
38
+
39
+ raise(ArgumentError, "#{attribute} is required")
40
+ end
41
+
42
+ # Validates that the attribute is not blank if blank is not allowed
43
+ #
44
+ # @param params [Hash] the parameters to validate
45
+ # @param attribute [Symbol] the attribute to validate
46
+ # @param options [Hash] the validation options
47
+ # @option options [Boolean] :blank whether the attribute can be blank
48
+ # @raise [ArgumentError] if the attribute is blank but blank is not allowed
49
+ # @return [void]
50
+ def validate_blank!(params, attribute, options)
51
+ return unless !options[:blank] && (params.key?(attribute) && params[attribute].blank?)
52
+
53
+ raise(ArgumentError, "#{attribute} can't be blank")
54
+ end
55
+
56
+ # Validates that the attribute is of the expected type
57
+ #
58
+ # @param params [Hash] the parameters to validate
59
+ # @param attribute [Symbol] the attribute to validate
60
+ # @param options [Hash] the validation options
61
+ # @option options [Class] :type the expected type of the attribute
62
+ # @raise [ArgumentError] if the attribute is not of the expected type
63
+ # @return [void]
64
+ def validate_type!(params, attribute, options)
65
+ klass_type = options[:type]
66
+
67
+ return unless klass_type.to_s.present? && !params[attribute].is_a?(klass_type)
68
+
69
+ raise(ArgumentError, "#{attribute} must be #{klass_type}")
70
+ end
71
+
72
+ # Validates that the attribute's value is within the allowed values
73
+ #
74
+ # @param params [Hash] the parameters to validate
75
+ # @param attribute [Symbol] the attribute to validate
76
+ # @param options [Hash] the validation options
77
+ # @option options [Array] :in the allowed values for the attribute
78
+ # @raise [ArgumentError] if the attribute's value is not within the allowed values
79
+ # @return [void]
80
+ def validate_value!(params, attribute, options)
81
+ return unless params[attribute].to_s.present? && options[:in]&.exclude?(params[attribute])
82
+
83
+ raise(ArgumentError, "#{attribute} must be in #{options[:in]}")
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ZktClient
6
+ # Department class handles operations related to departments in the ZktClient
7
+ class Department < Base
8
+ # URL endpoints for the Department resource
9
+ URLS = { base: "/personnel/api/departments/" }.freeze
10
+
11
+ # Required fields for creating or updating a Department resource
12
+ REQUIRED_FEILDS = %i[dept_code dept_name].freeze
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ZktClient
6
+ # Department class handles operations related to devices in the ZktClient
7
+ class Device < Base
8
+ # URL endpoints for the device resource
9
+ URLS = { base: "/iclock/api/terminals/" }.freeze
10
+
11
+ # Required fields for creating or updating a device resource
12
+ REQUIRED_FEILDS = %i[sn alias ip_address].freeze
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ZktClient
6
+ # Employee class handles operations related to employees in the ZktClient
7
+ class Employee < Base
8
+ # URL endpoints for the Employee resource
9
+ URLS = {
10
+ base: "/personnel/api/employees/"
11
+ }.freeze
12
+
13
+ # Required fields for creating or updating an Employee resource
14
+ REQUIRED_FEILDS = %i[emp_code department area].freeze
15
+
16
+ class << self
17
+ private
18
+
19
+ # Validates the parameters for creating an Employee resource
20
+ #
21
+ # @param params [Hash] the parameters to validate
22
+ # @raise [ArgumentError] if required fields are missing or invalid
23
+ def validate_create_params!(params)
24
+ super(params)
25
+ validate!(params, :app_status, in: [1, 0, "Enable", "Disable"])
26
+ validate!(params, :area, type: Array)
27
+ end
28
+
29
+ # Validates the parameters for updating an Employee resource
30
+ #
31
+ # @param params [Hash] the parameters to validate
32
+ # @raise [ArgumentError] if required fields are missing or invalid
33
+ def validate_update_params!(params)
34
+ super(params)
35
+ validate!(params, :gender, in: %w[F M])
36
+ validate!(params, :app_status, in: [1, 0, "Enable", "Disable"])
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ZktClient
6
+ # Position class handles operations related to positions in the ZktClient
7
+ class Position < Base
8
+ # URL endpoints for the Position resource
9
+ URLS = { base: "/personnel/api/positions/" }.freeze
10
+
11
+ # Required fields for creating or updating a Position resource
12
+ REQUIRED_FEILDS = %i[position_code position_name].freeze
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ZktClient
6
+ # Transaction class handles operations related to transactions in the ZktClient
7
+ class Transaction < Base
8
+ # URL endpoints for the Transaction resource
9
+ URLS = {
10
+ base: "/iclock/api/transactions/"
11
+ }.freeze
12
+
13
+ # Required fields for creating or updating a Transaction resource
14
+ REQUIRED_FEILDS = [].freeze
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ # Extends the Array class with additional methods
6
+ class Array
7
+ include Base
8
+
9
+ # Wraps the given object in an array.
10
+ #
11
+ # @param object [Object] the object to wrap
12
+ # @return [Array] the wrapped object as an array
13
+ def self.wrap(object)
14
+ if object.respond_to?(:to_a)
15
+ object.to_a
16
+ else
17
+ [object]
18
+ end
19
+ end
20
+
21
+ # Checks if the array excludes the given value.
22
+ #
23
+ # @param value [Object] the value to check
24
+ # @return [Boolean] true if the array does not include the value, false otherwise
25
+ def exclude?(value)
26
+ !include?(value)
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Base
4
+ # Checks if the object is blank (empty)
5
+ #
6
+ # @return [Boolean] true if the object is empty, false otherwise
7
+ def blank?
8
+ empty?
9
+ end
10
+
11
+ # Checks if the object is present (not blank)
12
+ #
13
+ # @return [Boolean] true if the object is not empty, false otherwise
14
+ def present?
15
+ !blank?
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ # Extends the FalseClass with additional methods
6
+ class FalseClass
7
+ include Base
8
+
9
+ # Checks if the object is blank (always true for FalseClass)
10
+ #
11
+ # @return [Boolean] true, since false is considered blank
12
+ def blank?
13
+ true
14
+ end
15
+ end