hudu 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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
  - - ">="