hudu 0.3.0 → 0.3.1

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: 6de79a28de35dea4c3ac9ebd26823427f839192e44113fe67f9d5f5e845cade1
4
- data.tar.gz: 1bada2b529ac16cc376b85f33b953a451a23f8e955dcc2068814171bcbf0509c
3
+ metadata.gz: ed87eec77a92013c3e0c83084432ad5a91a50762cdd19e0e24ea6b383481d2c0
4
+ data.tar.gz: '048caa2a4f7abb2e2b940fd9681a026669ab84ef7d67cfc7c3655736a41de75d'
5
5
  SHA512:
6
- metadata.gz: 59adb93ef27db882adc800a183ffbe1b7297e96a11014492ff61774395d0969a79603bd442c452f028fae38fc2b6a9ac591f66151276b94bc4d9b154bddda8f6
7
- data.tar.gz: bdf2dfd500420081eb4b530f3621f90ae21f3204e0c817e7e33da8c1baae660425e2c844059693c5f5048d7ee1d16e26159987a7ff1d6562dbc716453e06ea97
6
+ metadata.gz: 64307e6e97f707ad42b2736c4473fa178e9b4a8f86a62fb9840c65519ab5e4cb4f057d8d0f7f06e52c931f67ef0066f0ceb1b27f777c75dfda8566d4a77222f5
7
+ data.tar.gz: 14a41bde5270cd5e5710674ad9372ca9579a396ddb18d3ba6d517c4b89b9a7bad37608b5bae52fa6d496952589e9d6aed333f70727b0251d0202aa70007e3bbe
data/CHANGELOG.md CHANGED
@@ -6,3 +6,8 @@
6
6
  ## [0.2.0] - 2024-03-13
7
7
  - update_company_asset added
8
8
 
9
+ ## [0.3.0] - 2024-03-13
10
+ - throtling connection rate
11
+
12
+ ## [0.3.1] - 2024-03-13
13
+ - rubocop fixes
data/Gemfile CHANGED
@@ -8,4 +8,4 @@ gemspec
8
8
  gem 'rake', '~> 13.0'
9
9
  gem 'rubocop', '~> 1.7'
10
10
  gem 'simplecov', require: false, group: :test
11
- gem 'wrapi'
11
+ gem 'wrapi'
data/hudu.gemspec CHANGED
@@ -29,9 +29,9 @@ Gem::Specification.new do |s|
29
29
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
30
30
  s.platform = Gem::Platform::RUBY
31
31
  s.add_runtime_dependency 'faraday'
32
- s.add_runtime_dependency 'wrapi', ">= 0.3.0"
32
+ s.add_runtime_dependency 'wrapi', '>= 0.3.0'
33
33
  s.add_development_dependency 'dotenv'
34
34
  s.add_development_dependency 'minitest'
35
- s.add_development_dependency 'simplecov'
36
35
  s.add_development_dependency 'rubocop'
36
+ s.add_development_dependency 'simplecov'
37
37
  end
data/lib/hudu/api.rb CHANGED
@@ -1,4 +1,6 @@
1
- require "wrapi"
1
+ # frozen_string_literal: true
2
+
3
+ require 'wrapi'
2
4
  require File.expand_path('authentication', __dir__)
3
5
  require File.expand_path('configuration', __dir__)
4
6
  require File.expand_path('connection', __dir__)
@@ -6,9 +8,8 @@ require File.expand_path('connection', __dir__)
6
8
  module Hudu
7
9
  # @private
8
10
  class API
9
-
10
11
  # @private
11
- attr_accessor *Configuration::VALID_OPTIONS_KEYS
12
+ attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
12
13
 
13
14
  # Creates a new API and copies settings from singleton
14
15
  def initialize(options = {})
@@ -1,32 +1,60 @@
1
- module Hudu
1
+ # frozen_string_literal: true
2
2
 
3
+ module Hudu
4
+ # The AssetHelper class contains helper methods for constructing and creating asset data.
3
5
  class AssetHelper
4
- # Construct asset for updates, assuem it is an entity
5
- def self.construct_asset asset
6
- custom_asset = asset.attributes.slice( *%w(
7
- id company_id asset_layout_id slug name
8
- primary_serial primary_model primary_mail
9
- primary_manufacturer )
6
+ # Constructs an asset for updates by extracting key attributes and formatting custom fields.
7
+ #
8
+ # @param asset [Object] An Hudu entity that represents an asset, expected to respond to `attributes` and `fields`.
9
+ # @return [Hash] A formatted hash representing the asset for update operations.
10
+ #
11
+ # @example
12
+ # asset = SomeAssetEntity.new(attributes: { id: 1, name: "Asset 1", company_id: 101 }, fields: [field1, field2])
13
+ # Hudu::AssetHelper.construct_asset(asset)
14
+ # # => { asset: { id: 1, company_id: 101, asset_layout_id: nil, slug: nil, name: "Asset 1", custom_fields: [...] } }
15
+ def self.construct_asset(asset)
16
+ custom_asset = asset.attributes.slice(*%w[
17
+ id company_id asset_layout_id slug name
18
+ primary_serial primary_model primary_mail
19
+ primary_manufacturer
20
+ ]
10
21
  )
11
- custom_asset['custom_fields'] = self.custom_fields(asset.fields)
22
+ custom_asset['custom_fields'] = custom_fields(asset.fields)
12
23
  { asset: custom_asset }
13
24
  end
14
25
 
15
- # create new asset from layout
16
- def self.create_asset name, asset_layout_id, fields
17
- custom_asset = {
26
+ # Creates a new asset from the given layout and fields.
27
+ #
28
+ # @param name [String] The name of the new asset.
29
+ # @param asset_layout_id [Integer] The ID of the asset layout to use.
30
+ # @param fields [Array<Object>] A collection of field objects representing the asset's custom fields.
31
+ # @return [Hash] A formatted hash representing the new asset.
32
+ #
33
+ # @example
34
+ # fields = [Field.new(label: "Warranty", value: "2025"), Field.new(label: "Location", value: "NYC")]
35
+ # Hudu::AssetHelper.create_asset("New Asset", 10, fields)
36
+ # # => { asset: { name: "New Asset", asset_layout_id: 10, custom_fields: [...] } }
37
+ def self.create_asset(name, asset_layout_id, fields)
38
+ {
18
39
  asset: {
19
40
  name: name,
20
41
  asset_layout_id: asset_layout_id,
21
- custom_fields: self.custom_fields(fields)
42
+ custom_fields: custom_fields(fields)
22
43
  }
23
44
  }
24
45
  end
25
-
26
- private
46
+
47
+ # Formats custom fields into a standardized hash structure.
48
+ #
49
+ # @param fields [Array<Object>] A collection of field objects, each expected to respond to `label` and `value`.
50
+ # @return [Array<Hash>] An array containing a single hash mapping field labels (downcased and underscored) to their values.
51
+ #
52
+ # @example
53
+ # fields = [Field.new(label: "Warranty", value: "2025"), Field.new(label: "Location", value: "NYC")]
54
+ # Hudu::AssetHelper.custom_fields(fields)
55
+ # # => [{ "warranty" => "2025", "location" => "NYC" }]
27
56
  def self.custom_fields(fields)
28
- [fields.map{|field| [field.label.downcase.gsub(' ','_'),field.value]}.to_h]
57
+ [fields.map { |field| [field.label.downcase.gsub(' ', '_'), field.value] }.to_h]
29
58
  end
30
-
31
59
  end
32
- end
60
+ end
@@ -1,20 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path('error', __dir__)
2
4
 
3
5
  module Hudu
4
6
  # Deals with authentication flow and stores it within global configuration
5
7
  module Authentication
6
-
7
- #
8
+ #
8
9
  # Authorize to the Hudu portal and return access_token
9
10
  def login(options = {})
10
- raise ArgumentError, "Accesstoken/api-key not set" unless api_key
11
- connection_options.merge!({ headers: { "x-api-key": api_key }})
12
- # only api key needed
11
+ raise ArgumentError, 'Accesstoken/api-key not set' unless api_key
12
+
13
+ connection_options.merge!({ headers: { "x-api-key": api_key } })
14
+ # only api key needed
13
15
  # will do sanitty check if token if valid
14
- get('/api/v1/api_info')
16
+ get('/api/v1/api_info', options)
15
17
  rescue Faraday::Error => e
16
18
  raise AuthenticationError, e
17
19
  end
18
-
19
20
  end
20
21
  end
data/lib/hudu/client.rb CHANGED
@@ -1,53 +1,76 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path('api', __dir__)
2
4
  require File.expand_path('asset_helper', __dir__)
3
5
 
4
6
  module Hudu
5
- # Wrapper for the Hudu REST API
7
+ # The Client class serves as a wrapper for the Hudu REST API, providing methods to interact
8
+ # with various Hudu resources.
9
+ #
10
+ # This class dynamically defines methods to fetch, create, update, and manipulate Hudu resources
11
+ # such as companies, articles, assets, and more.
6
12
  #
7
- # @see
13
+ # @example Basic Usage
14
+ # client.companies # Fetch all companies
15
+ # client.company(1) # Fetch a company by ID
16
+ # client.update_company(1, { name: "Updated Company" }) # Update a company
17
+ # client.create_company({ name: "New Company" }) # Create a new company
8
18
  class Client < API
9
19
 
10
- private
20
+ # Dynamically defines methods for interacting with Hudu API resources.
21
+ #
22
+ # Depending on the arguments, this will define methods to:
23
+ # - Fetch all records for a resource
24
+ # - Fetch a specific record by ID
25
+ # - Update a record
26
+ # - Create a new record
27
+ #
28
+ # @param method [Symbol] The method name for fetching all records.
29
+ # @param singular_method [Symbol, nil] The method name for fetching a single record by ID. Optional.
30
+ # @param path [String] The API path for the resource. Defaults to the method name.
31
+ #
32
+ # @example Defining endpoints
33
+ # api_endpoint :companies, :company
34
+ # # Defines:
35
+ # # - `companies(params = {})` to fetch all companies.
36
+ # # - `company(id, params = {})` to fetch a single company by ID.
37
+ # # - `update_companies(id, params = {})` to update a company.
38
+ # # - `create_companies(params = {})` to create a new company.
11
39
  def self.api_endpoint(method, singular_method = nil, path = method)
12
- # generate all and by id
13
40
  if singular_method
14
- # all records
15
- self.send(:define_method, method) do |params = {}|
41
+ # Define method to fetch all records and one by id
42
+ send(:define_method, method) do |params = {}|
16
43
  r = get_paged(api_url(path), params)
17
- r = hudu_data(r,method)
44
+ hudu_data(r, method)
18
45
  end
19
- # record by id
20
- self.send(:define_method, singular_method) do |id, params = {}|
46
+ # Define method to fetch a single record by ID
47
+ send(:define_method, singular_method) do |id, params = {}|
21
48
  r = get(api_url("#{path}/#{id}"), params)
22
- r = hudu_data(r,singular_method)
49
+ hudu_data(r, singular_method)
23
50
  end
24
51
  else
25
- # normal method, return result
26
- self.send(:define_method, method) do |params = {}|
52
+ # Define simple method to fetch data
53
+ send(:define_method, method) do |params = {}|
27
54
  get(api_url(path), params)
28
55
  end
29
56
  end
30
- # update
31
- self.send(:define_method, "update_#{method}") do |id=nil,params = {}|
57
+
58
+ # Define method to update a record
59
+ send(:define_method, "update_#{method}") do |id = nil, params = {}|
32
60
  r = put(api_url("#{path}/#{id}"), params)
33
- r = hudu_data(r,method)
61
+ hudu_data(r, method)
34
62
  end
35
- # create
36
- self.send(:define_method, "create_#{method}") do |id=nil,params = {}|
63
+
64
+ # Define method to create a record
65
+ send(:define_method, "create_#{method}") do |id = nil, params = {}|
37
66
  r = post(api_url("#{path}/#{id}"), params)
38
- r = hudu_data(r,method)
67
+ hudu_data(r, method)
39
68
  end
40
-
41
69
  end
42
70
 
43
- public
71
+
72
+ # Define API endpoints for various resources
44
73
  api_endpoint :api_info
45
-
46
- # Activity logs can be filtered on
47
- # user_id, user_email
48
- # resource_id, resource_type
49
- # action_message
50
- # start_date - Must be in ISO 8601 format
51
74
  api_endpoint :activity_logs
52
75
  api_endpoint :companies, :company
53
76
  api_endpoint :articles, :article
@@ -61,31 +84,66 @@ module Hudu
61
84
  api_endpoint :relations
62
85
  api_endpoint :magic_dashes, :magic_dash, 'magic_dash'
63
86
 
64
- def company_articles( company_id, params = {} )
65
- articles({company_id: company_id}.merge(params))
87
+ # Fetches all articles for a specific company.
88
+ #
89
+ # @param company_id [Integer] The ID of the company.
90
+ # @param params [Hash] Additional query parameters.
91
+ # @return [Array<Hash>] A list of articles.
92
+ def company_articles(company_id, params = {})
93
+ articles({ company_id: company_id }.merge(params))
66
94
  end
67
- def company_assets(id,params={})
95
+
96
+ # Fetches all assets for a specific company.
97
+ #
98
+ # @param id [Integer] The ID of the company.
99
+ # @param params [Hash] Additional query parameters.
100
+ # @return [Array<Hash>] A list of assets.
101
+ def company_assets(id, params = {})
68
102
  get_paged(api_url("companies/#{id}/assets"), params)
69
103
  end
70
- def company_asset(company_id,asset_id,params={})
104
+
105
+ # Fetches a specific asset for a company.
106
+ #
107
+ # @param company_id [Integer] The ID of the company.
108
+ # @param asset_id [Integer] The ID of the asset.
109
+ # @param params [Hash] Additional query parameters.
110
+ # @return [Hash] The asset details.
111
+ def company_asset(company_id, asset_id, params = {})
71
112
  get(api_url("companies/#{company_id}/assets/#{asset_id}"), params)
72
113
  end
73
114
 
115
+ # Updates an existing company asset.
116
+ #
117
+ # @param asset [Object] The asset object to update.
118
+ # @return [Hash] The updated asset data.
74
119
  def update_company_asset(asset)
75
- hudu_data(put(api_url("companies/#{asset.company_id}/assets/#{asset.id}"), AssetHelper.construct_asset(asset),false),:asset)
120
+ hudu_data(put(api_url("companies/#{asset.company_id}/assets/#{asset.id}"), AssetHelper.construct_asset(asset), false), :asset)
76
121
  end
77
122
 
78
- def create_company_asset(company_id,asset_layout, fields)
79
- hudu_data(post(api_url("companies/#{company_id}/assets"), AssetHelper.create_asset(asset_layout.name,asset_layout.id,fields),false),:asset)
123
+ # Creates a new asset for a company.
124
+ #
125
+ # @param company_id [Integer] The ID of the company.
126
+ # @param asset_layout [Object] The asset layout object.
127
+ # @param fields [Array<Hash>] The custom fields for the asset.
128
+ # @return [Hash] The newly created asset data.
129
+ def create_company_asset(company_id, asset_layout, fields)
130
+ hudu_data(post(api_url("companies/#{company_id}/assets"), AssetHelper.create_asset(asset_layout.name, asset_layout.id, fields), false), :asset)
80
131
  end
81
132
 
82
- # return api path
83
- def api_url path
133
+ # Constructs the full API URL for a given path.
134
+ #
135
+ # @param path [String] The API path.
136
+ # @return [String] The full API URL.
137
+ def api_url(path)
84
138
  "/api/v1/#{path}"
85
139
  end
86
140
 
87
- # hudu returns data as {resource:{}} or {resource:[]}
88
- def hudu_data(result,resource)
141
+ # Extracts resource data from the API response.
142
+ #
143
+ # @param result [Hash, Object] The API response.
144
+ # @param resource [Symbol] The name of the resource to extract.
145
+ # @return [Object] The resource data.
146
+ def hudu_data(result, resource)
89
147
  if result.is_a?(WrAPI::Request::Entity) && result.attributes[resource.to_s]
90
148
  result.send resource.to_s
91
149
  else
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'wrapi'
2
4
  require File.expand_path('version', __dir__)
3
5
  require File.expand_path('pagination', __dir__)
@@ -11,11 +13,11 @@ module Hudu
11
13
  VALID_OPTIONS_KEYS = (WrAPI::Configuration::VALID_OPTIONS_KEYS + [:api_key]).freeze
12
14
 
13
15
  # @private
14
- attr_accessor *VALID_OPTIONS_KEYS
16
+ attr_accessor(*VALID_OPTIONS_KEYS)
15
17
 
16
- DEFAULT_UA = "Ruby Hudu API wrapper #{Hudu::VERSION}".freeze
18
+ DEFAULT_UA = "Ruby Hudu API wrapper #{Hudu::VERSION}"
17
19
  DEFAULT_PAGINATION = RequestPagination::PagingInfoPager
18
- DEFAULT_PAGE_SIZE = 25
20
+ DEFAULT_PAGE_SIZE = 100
19
21
 
20
22
  # When this module is extended, set all configuration options to their default values
21
23
  def self.extended(base)
@@ -40,4 +42,3 @@ module Hudu
40
42
  end
41
43
  end
42
44
  end
43
-
@@ -1,11 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path('rate_throttle_middleware', __dir__)
2
4
 
3
5
  module Hudu
4
- # Create connection including authorization parameters with default Accept format and User-Agent
5
- # By default
6
- # - Bearer authorization is access_token is not nil override with @setup_authorization
7
- # - Headers setup for client-id and client-secret when client_id and client_secret are not nil @setup_headers
8
- # @private
6
+ # Create connection including and keep it persistent so we add the rate throtling middleware only once
9
7
  module Connection
10
8
  private
11
9
 
data/lib/hudu/error.rb CHANGED
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hudu
2
-
3
4
  # Generic error to be able to rescue all Hudu errors
4
5
  class HuduError < StandardError; end
5
6
 
6
7
  # Error when authentication fails
7
8
  class AuthenticationError < HuduError; end
8
- end
9
+ end
@@ -1,42 +1,82 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
  require 'json'
3
5
 
4
6
  module Hudu
5
-
6
7
  # Defines HTTP request methods
7
8
  # @see https://support.hudu.com/hc/en-us/articles/11422780787735-REST-API#pagination-0-5
8
9
  module RequestPagination
9
-
10
+ # The PagingInfoPager class provides a mechanism to handle pagination information for API responses.
11
+ #
12
+ # It manages the current page, page size, and provides utilities for determining if there are more pages to fetch.
13
+ #
14
+ # @example Basic Usage
15
+ # pager = PagingInfoPager.new(50)
16
+ # while pager.more_pages?
17
+ # response = api_client.get_data(pager.page_options)
18
+ # pager.next_page!(response.body)
19
+ # end
10
20
  class PagingInfoPager
11
21
  attr_reader :offset, :limit, :total
22
+
23
+ # Initializes a new PagingInfoPager instance.
24
+ #
25
+ # @param page_size [Integer] The number of records to fetch per page.
12
26
  def initialize(page_size)
13
27
  @page = 1
14
28
  @page_total = @page_size = page_size
15
29
  end
16
30
 
31
+ # Provides the current pagination parameter options for each rest request.
32
+ #
33
+ # @return [Hash] A hash containing the current page and page size.
34
+ #
35
+ # @example
36
+ # pager.page_options # => { page: 1, page_size: 50 }
17
37
  def page_options
18
38
  { page: @page, page_size: @page_size }
19
39
  end
20
40
 
41
+ # Advances to the next page based on the response body and updates internal pagination state.
42
+ #
43
+ # @param body [Hash] The response body from the API, expected to contain a paginated resource.
44
+ # @return [Integer] The updated page total, typically the count of items on the current page.
45
+ #
46
+ # @example
47
+ # response_body = { "items" => [...] }
48
+ # pager.next_page!(response_body)
21
49
  def next_page!(body)
22
50
  @page += 1
23
51
  a = PagingInfoPager.data(body)
24
52
  @page_total = a.is_a?(Array) ? a.count : 1
25
53
  end
26
54
 
55
+ # Determines whether there are more pages to fetch.
56
+ #
57
+ # @return [Boolean] Returns `true` if the current page is full, indicating another page might exist.
58
+ #
59
+ # @example
60
+ # pager.more_pages? # => true or false
27
61
  def more_pages?
28
62
  # while full page we have next page
29
63
  @page_total == @page_size
30
64
  end
31
65
 
32
- def self.data(body)
66
+ # Extracts paginated data from the response body.
67
+ #
68
+ # @param body [Hash] The response body containing resource data, expected to be in a hash format.
69
+ # @return [Array, Hash, Object] Returns the extracted data, which could be an array, hash, or other object.
70
+ #
71
+ # @example
72
+ # response_body = { "items" => [1, 2, 3] }
73
+ # PagingInfoPager.data(response_body) # => [1, 2, 3]
74
+ def self.data(body)
33
75
  # assume hash {"resource":[...]}, get first key and return array data
34
- result = body
35
- if result && result.respond_to?(:first)
36
- k,v = body.first
37
- if v.is_a?(Array) || v.is_a?(Hash)
38
- result = v
39
- end
76
+ result = body
77
+ if result.respond_to?(:first)
78
+ _k, v = body.first
79
+ result = v if v.is_a?(Array) || v.is_a?(Hash)
40
80
  end
41
81
  result
42
82
  end
@@ -1,67 +1,72 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'faraday'
2
- require 'thread'
3
4
 
4
- # A Faraday middleware for rate limiting requests.
5
- #
6
- # This middleware ensures that the number of requests made through a Faraday connection
7
- # does not exceed a specified limit within a given time period.
8
- #
9
- # @example Add middleware to a Faraday connection
10
- # connection = Faraday.new(url: 'https://api.example.com') do |faraday|
11
- # faraday.use RateThrottleMiddleware, limit: 300, period: 60
12
- # faraday.adapter Faraday.default_adapter
13
- # end
14
- #
15
- # @see https://github.com/lostisland/faraday Faraday Documentation
16
- #
17
- class RateThrottleMiddleware < Faraday::Middleware
18
- # Initializes the RateThrottleMiddleware.
5
+ module Hudu
6
+ # A Faraday middleware for rate limiting requests.
19
7
  #
20
- # @param app [#call] The next middleware or the actual Faraday adapter.
21
- # @param limit [Integer] The maximum number of requests allowed within the specified period. Default is 300.
22
- # @param period [Integer] The time period in seconds over which the limit applies. Default is 60 seconds.
8
+ # This middleware ensures that the number of requests made through a Faraday connection
9
+ # does not exceed a specified limit within a given time period.
23
10
  #
24
- # @example
25
- # middleware = RateThrottleMiddleware.new(app, limit: 300, period: 60)
11
+ # @example Add middleware to a Faraday connection
12
+ # connection = Faraday.new(url: 'https://api.example.com') do |faraday|
13
+ # faraday.use RateThrottleMiddleware, limit: 300, period: 60
14
+ # faraday.adapter Faraday.default_adapter
15
+ # end
26
16
  #
27
- def initialize(app, limit: 300, period: 60)
28
- super(app)
29
- @limit = limit
30
- @period = period
31
- @requests = []
32
- @mutex = Mutex.new
33
- @condition = ConditionVariable.new
34
- end
17
+ # @see https://github.com/lostisland/faraday Faraday Documentation
18
+ #
19
+ class RateThrottleMiddleware < Faraday::Middleware
20
+ # Initializes the RateThrottleMiddleware.
21
+ #
22
+ # @param app [#call] The next middleware or the actual Faraday adapter.
23
+ # @param limit [Integer] The maximum number of requests allowed within the specified period. Default is 300.
24
+ # @param period [Integer] The time period in seconds over which the limit applies. Default is 60 seconds.
25
+ #
26
+ # @example
27
+ # middleware = RateThrottleMiddleware.new(app, limit: 300, period: 60)
28
+ #
29
+ def initialize(app, limit: 300, period: 60)
30
+ super(app)
31
+ @limit = limit
32
+ @period = period
33
+ @requests = []
34
+ @mutex = Mutex.new
35
+ @condition = ConditionVariable.new
36
+ end
35
37
 
36
- def call(env)
37
- throttle_request
38
- @app.call(env)
39
- end
38
+ def call(env)
39
+ throttle_request
40
+ @app.call(env)
41
+ end
40
42
 
41
- private
43
+ private
42
44
 
43
- def throttle_request
44
- @mutex.synchronize do
45
- now = Time.now.to_f
46
- # Clear requests older than the rate limit period
47
- while !@requests.empty? && @requests[0] < (now - @period)
48
- @requests.pop
45
+ def throttle_request
46
+ @mutex.synchronize do
47
+ now = Time.now.to_f
48
+ remove_expired_requests(now)
49
+
50
+ rate_limited(now)
51
+
52
+ # Record the new request
53
+ @requests.push(Time.now.to_f)
54
+ @condition.broadcast
49
55
  end
56
+ end
57
+
58
+ def remove_expired_requests(now)
59
+ # Clear requests older than the rate limit period
60
+ @requests.pop while !@requests.empty? && @requests[0] < (now - @period)
61
+ end
50
62
 
63
+ def rate_limited(now)
51
64
  # Wait if the request limit is reached
52
65
  while @requests.size >= @limit
53
66
  sleep_time = @requests[0] + @period - now
54
- puts "<#{sleep_time}>"
55
- @condition.wait(@mutex, sleep_time) if sleep_time > 0
56
- now = Time.now.to_f
57
- while !@requests.empty? && @requests[0] < (now - @period)
58
- @requests.pop
59
- end
67
+ @condition.wait(@mutex, sleep_time) if sleep_time.positive?
68
+ remove_expired_requests(Time.now.to_f)
60
69
  end
61
-
62
- # Record the new request
63
- @requests.push(Time.now.to_f)
64
- @condition.broadcast
65
70
  end
66
71
  end
67
72
  end
data/lib/hudu/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hudu
4
- VERSION = '0.3.0'
4
+ VERSION = '0.3.1'
5
5
  end
data/lib/hudu.rb CHANGED
@@ -1,7 +1,10 @@
1
- require "wrapi"
1
+ # frozen_string_literal: true
2
+
3
+ require 'wrapi'
2
4
  require File.expand_path('hudu/configuration', __dir__)
3
5
  require File.expand_path('hudu/client', __dir__)
4
6
 
7
+ # The Hudu module provides utilities to manage and manipulate assets within the Hudu api
5
8
  module Hudu
6
9
  extend Configuration
7
10
  extend WrAPI::RespondTo
@@ -11,9 +14,4 @@ module Hudu
11
14
  def self.client(options = {})
12
15
  Hudu::Client.new(options)
13
16
  end
14
-
15
- def self.reset
16
- super
17
-
18
- end
19
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hudu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janco Tanis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-13 00:00:00.000000000 Z
11
+ date: 2024-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: simplecov
70
+ name: rubocop
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rubocop
84
+ name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="