Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ Here are some examples with inline comments that walk you through how to use the

Tests are also a good place to know how the the library works internally: [spec](spec)

### Error handling

Failed requests raise a subclass of `Typesense::Error`. The exception's `message` is the API error string (`"Object Not Found"`, etc.), and the underlying Faraday response is available on `#data` for callers that need the HTTP status, headers, or raw body:

```ruby
begin
client.collections['unknown'].retrieve
rescue Typesense::Error::ObjectNotFound => e
e.message # => "Not Found"
e.data.status # => 404
e.data.body # => raw response body
end
```

The exception classes that map to specific status codes are: `RequestMalformed` (400), `RequestUnauthorized` (401), `ObjectNotFound` (404), `ObjectAlreadyExists` (409), `ObjectUnprocessable` (422), `ServerError` (5xx), `HTTPStatus0Error` (status 0), `TimeoutError`, and `HTTPError` (anything else).

## Compatibility

| Typesense Server | typesense-ruby |
Expand Down
3 changes: 2 additions & 1 deletion lib/typesense/analytics_rules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class AnalyticsRules

def initialize(api_call)
@api_call = api_call
@analytics_rules = {}
end

def create(rules)
Expand All @@ -17,7 +18,7 @@ def retrieve
end

def [](rule_name)
AnalyticsRule.new(rule_name, @api_call)
@analytics_rules[rule_name] ||= AnalyticsRule.new(rule_name, @api_call)
end
end
end
4 changes: 2 additions & 2 deletions lib/typesense/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(options = {})
@nearest_node = options[:nearest_node]
@connection_timeout_seconds = options[:connection_timeout_seconds] || options[:timeout_seconds] || 10
@healthcheck_interval_seconds = options[:healthcheck_interval_seconds] || 15
@num_retries = options[:num_retries] || (@nodes.length + (@nearest_node.nil? ? 0 : 1)) || 3
@num_retries = options[:num_retries] || (@nodes.length + (@nearest_node.nil? ? 0 : 1))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do want the default num retries to b 3. Any reason this was removed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The || 3 here is effectively unreachable. (nodes.length + nearest_count) always returns a truthy Integer, and validate! ensures it’s ≥ 1 at runtime.

So the effective default is nodes.length + (nearest_node ? 1 : 0), which often ends up as 3 in typical setups.

Happy to switch to a flat 3, but that would change behavior for smaller clusters rather than restore it.

Added extra tests in configuration_spec.rb that pin down num_retries's default.

@retry_interval_seconds = options[:retry_interval_seconds] || 0.1
@api_key = options[:api_key]

Expand All @@ -36,7 +36,7 @@ def validate!
private

def node_missing_parameters?(node)
%i[protocol host port].any? { |attr| node.send(:[], attr).nil? }
%i[protocol host port].any? { |attr| node[attr].nil? }
end

def show_deprecation_warnings(options)
Expand Down
3 changes: 2 additions & 1 deletion lib/typesense/curation_sets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class CurationSets

def initialize(api_call)
@api_call = api_call
@curation_sets = {}
end

def upsert(curation_set_name, curation_set_data)
Expand All @@ -17,7 +18,7 @@ def retrieve
end

def [](curation_set_name)
CurationSet.new(curation_set_name, @api_call)
@curation_sets[curation_set_name] ||= CurationSet.new(curation_set_name, @api_call)
end
end
end
4 changes: 2 additions & 2 deletions spec/typesense/analytics_rules_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@
expect(result.instance_variable_get(:@rule_name)).to eq(rule_name)
end

it 'does not memoize the analytics rule instance' do
it 'memoizes the analytics rule instance' do
first_call = integration_client.analytics.rules[rule_name]
second_call = integration_client.analytics.rules[rule_name]
expect(first_call).not_to equal(second_call)
expect(first_call).to equal(second_call)
end
end
end
43 changes: 43 additions & 0 deletions spec/typesense/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,47 @@
end
end
end

describe '#num_retries default' do
let(:base_options) do
{
api_key: 'abcd',
nodes: [
{ host: 'node0', port: 8108, protocol: 'http' },
{ host: 'node1', port: 8108, protocol: 'http' },
{ host: 'node2', port: 8108, protocol: 'http' }
],
log_level: Logger::ERROR
}
end

it 'defaults to the number of nodes when no nearest_node is set' do
config = described_class.new(base_options)
expect(config.num_retries).to eq(3)
end

it 'defaults to nodes.length + 1 when a nearest_node is set' do
config = described_class.new(
base_options.merge(nearest_node: { host: 'nearestNode', port: 6108, protocol: 'http' })
)
expect(config.num_retries).to eq(4)
end

it 'reflects node count for single-node setups' do
config = described_class.new(
base_options.merge(nodes: [{ host: 'node0', port: 8108, protocol: 'http' }])
)
expect(config.num_retries).to eq(1)
end

it 'honors an explicit num_retries option' do
config = described_class.new(base_options.merge(num_retries: 7))
expect(config.num_retries).to eq(7)
end

it 'honors an explicit num_retries of 0 (Integer is truthy in Ruby)' do
config = described_class.new(base_options.merge(num_retries: 0))
expect(config.num_retries).to eq(0)
end
end
end
Loading