showroom 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.
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Showroom
4
+ module Search
5
+ # Wraps the +resources.results+ hash from a Shopify search suggest response,
6
+ # exposing typed arrays for each resource kind.
7
+ #
8
+ # @example
9
+ # result = Showroom::Search::Result.new(raw['resources']['results'])
10
+ # result.products # => [Array<ProductSuggestion>]
11
+ # result.collections # => [Array<CollectionSuggestion>]
12
+ # result.queries # => [Array<QuerySuggestion>]
13
+ class Result
14
+ # @param results_hash [Hash] the +resources.results+ hash from the API response
15
+ def initialize(results_hash)
16
+ @data = results_hash
17
+ end
18
+
19
+ # Returns product suggestions from the search result.
20
+ #
21
+ # @return [Array<ProductSuggestion>]
22
+ def products
23
+ @data.fetch('products', []).map { |h| ProductSuggestion.new(h) }
24
+ end
25
+
26
+ # Returns collection suggestions from the search result.
27
+ #
28
+ # @return [Array<CollectionSuggestion>]
29
+ def collections
30
+ @data.fetch('collections', []).map { |h| CollectionSuggestion.new(h) }
31
+ end
32
+
33
+ # Returns page suggestions from the search result.
34
+ #
35
+ # @return [Array<PageSuggestion>]
36
+ def pages
37
+ @data.fetch('pages', []).map { |h| PageSuggestion.new(h) }
38
+ end
39
+
40
+ # Returns article suggestions from the search result.
41
+ #
42
+ # @return [Array<ArticleSuggestion>]
43
+ def articles
44
+ @data.fetch('articles', []).map { |h| ArticleSuggestion.new(h) }
45
+ end
46
+
47
+ # Returns query suggestions from the search result.
48
+ #
49
+ # @return [Array<QuerySuggestion>]
50
+ def queries
51
+ @data.fetch('queries', []).map { |h| QuerySuggestion.new(h) }
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Showroom
4
+ module Search
5
+ # Base class for all search suggestion types.
6
+ #
7
+ # Subclasses that correspond to a fully-loadable model override {.complete_model_class}
8
+ # to declare which model to fetch. Calling {#load} on a suggestion that has no
9
+ # model raises +NoMethodError+.
10
+ #
11
+ # @example Loadable suggestion
12
+ # suggestion = result.products.first
13
+ # suggestion.load # => Showroom::Product
14
+ #
15
+ # @example Non-loadable suggestion
16
+ # suggestion = result.queries.first
17
+ # suggestion.load # => NoMethodError
18
+ class Suggestion < Resource
19
+ class << self
20
+ # Override in subclasses to declare the corresponding full model class.
21
+ #
22
+ # @return [Class]
23
+ # @raise [NoMethodError] when the suggestion type has no associated model
24
+ def complete_model_class
25
+ raise NoMethodError, "#{name} has no associated model — #load is not available"
26
+ end
27
+ end
28
+
29
+ # Returns the identifier used to fetch the full record (defaults to the
30
+ # +handle+ attribute). Override in subclasses when the identifier differs.
31
+ #
32
+ # @return [String]
33
+ def loadable_identifier
34
+ @attrs['handle']
35
+ end
36
+
37
+ # Fetches the full model record for this suggestion.
38
+ #
39
+ # Delegates to the model class declared by {.complete_model_class}, finding
40
+ # by {#loadable_identifier}.
41
+ #
42
+ # @return [Resource]
43
+ # @raise [NoMethodError] when the suggestion type has no associated model
44
+ # @raise [Showroom::NotFound] when the record is not found
45
+ def load
46
+ self.class.complete_model_class.find(loadable_identifier)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Showroom
4
+ # Provides search/suggest functionality against the Shopify search API.
5
+ #
6
+ # @example
7
+ # result = Showroom::Search.suggest('lorem', types: [:product, :collection], limit: 5)
8
+ # result.products.first.title # => "Lorem Road Bike"
9
+ module Search
10
+ # Calls GET /search/suggest.json and returns a {Result} wrapping the response.
11
+ #
12
+ # Builds query parameters of the form:
13
+ # ?q=...&resources[type]=product,collection&resources[limit]=10
14
+ #
15
+ # @param query_str [String] the search query
16
+ # @param types [Array<Symbol>] resource types to search (e.g. +:product+, +:collection+, +:query+)
17
+ # @param limit [Integer] maximum number of results per type
18
+ # @param params [Hash] additional query parameters forwarded to the API
19
+ # @return [Result]
20
+ def self.suggest(query_str, types: %i[product collection], limit: Showroom.per_page, **params)
21
+ query = { q: query_str, 'resources[limit]' => limit }
22
+ query['resources[type]'] = types.join(',') unless types.empty?
23
+ query.merge!(params)
24
+ raw = Showroom.client.get('/search/suggest.json', query)
25
+ Result.new(raw.dig('resources', 'results') || {})
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core/countable'
4
+ require_relative 'models/resource'
5
+
6
+ require_relative 'models/product_image'
7
+ require_relative 'models/product_option'
8
+ require_relative 'models/product_variant'
9
+ require_relative 'models/product'
10
+
11
+ require_relative 'models/collection'
12
+
13
+ require_relative 'models/search/suggestion'
14
+ require_relative 'models/search/product_suggestion'
15
+ require_relative 'models/search/collection_suggestion'
16
+ require_relative 'models/search/page_suggestion'
17
+ require_relative 'models/search/article_suggestion'
18
+ require_relative 'models/search/query_suggestion'
19
+ require_relative 'models/search/result'
20
+ require_relative 'models/search'
data/lib/showroom.rb ADDED
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'showroom/core/version'
4
+ require_relative 'showroom/core/error'
5
+ require_relative 'showroom/core/default'
6
+ require_relative 'showroom/core/configurable'
7
+ require_relative 'showroom/core/store_url'
8
+ require_relative 'showroom/http/middleware/raise_error'
9
+ require_relative 'showroom/core/connection'
10
+ require_relative 'showroom/client'
11
+ require_relative 'showroom/models'
12
+
13
+ # Top-level namespace for the Showroom gem.
14
+ #
15
+ # Acts as a module-level client with {Core::Configurable} mixed in, so you can
16
+ # configure and use Showroom directly without instantiating a {Client}.
17
+ #
18
+ # @example Global configuration
19
+ # Showroom.configure do |c|
20
+ # c.store = 'example.myshopify.com'
21
+ # end
22
+ #
23
+ # @example Module-level request
24
+ # Showroom.client.get('/products.json')
25
+ module Showroom
26
+ extend Core::Configurable
27
+
28
+ reset!
29
+
30
+ # Returns the memoized module-level {Client}.
31
+ #
32
+ # @return [Client]
33
+ def self.client
34
+ @client ||= Client.new(**options)
35
+ end
36
+
37
+ # Configures the module and resets the memoized client so the next call to
38
+ # {.client} picks up the new settings.
39
+ #
40
+ # @yield [self]
41
+ # @return [self]
42
+ def self.configure
43
+ super.tap { @client = nil }
44
+ end
45
+
46
+ # Resets all configuration to defaults and clears the memoized client.
47
+ #
48
+ # @return [void]
49
+ def self.reset!
50
+ super
51
+ @client = nil
52
+ end
53
+
54
+ # Alias for {.configure} — yields self for block-style setup.
55
+ #
56
+ # @yield [self]
57
+ # @return [self]
58
+ def self.setup(&)
59
+ configure(&)
60
+ end
61
+
62
+ # Fetches products from the configured store.
63
+ #
64
+ # @param params [Hash] Shopify query parameters
65
+ # @return [Array<Product>]
66
+ def self.products(**params)
67
+ Product.where(**params)
68
+ end
69
+
70
+ # Fetches a single product by handle from the configured store.
71
+ #
72
+ # @param handle [String] the product handle
73
+ # @return [Product]
74
+ # @raise [Showroom::NotFound] when the product is not found
75
+ def self.product(handle)
76
+ Product.find(handle)
77
+ end
78
+
79
+ # Fetches collections from the configured store.
80
+ #
81
+ # @param params [Hash] Shopify query parameters
82
+ # @return [Array<Collection>]
83
+ def self.collections(**params)
84
+ Collection.where(**params)
85
+ end
86
+
87
+ # Fetches a single collection by handle from the configured store.
88
+ #
89
+ # @param handle [String] the collection handle
90
+ # @return [Collection]
91
+ # @raise [Showroom::NotFound] when the collection is not found
92
+ def self.collection(handle)
93
+ Collection.find(handle)
94
+ end
95
+
96
+ # Calls the Shopify search suggest endpoint and returns a {Search::Result}.
97
+ #
98
+ # All keyword arguments are forwarded verbatim to {Search.suggest}
99
+ # (accepted keys: +types:+, +limit:+; see {Search.suggest} for details).
100
+ #
101
+ # @param query_str [String] the search query
102
+ # @return [Search::Result]
103
+ def self.search(query_str, **)
104
+ Search.suggest(query_str, **)
105
+ end
106
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: showroom
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - 01max
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: faraday
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '3'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '1'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '3'
32
+ description: |-
33
+ A standalone Ruby gem providing a Faraday-based client
34
+ to interact with the Shopify public API.
35
+ email: []
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - CHANGELOG.md
41
+ - LICENSE
42
+ - README.md
43
+ - lib/showroom.rb
44
+ - lib/showroom/client.rb
45
+ - lib/showroom/core/configurable.rb
46
+ - lib/showroom/core/connection.rb
47
+ - lib/showroom/core/countable.rb
48
+ - lib/showroom/core/default.rb
49
+ - lib/showroom/core/error.rb
50
+ - lib/showroom/core/store_url.rb
51
+ - lib/showroom/core/version.rb
52
+ - lib/showroom/http/middleware/raise_error.rb
53
+ - lib/showroom/models.rb
54
+ - lib/showroom/models/collection.rb
55
+ - lib/showroom/models/product.rb
56
+ - lib/showroom/models/product_image.rb
57
+ - lib/showroom/models/product_option.rb
58
+ - lib/showroom/models/product_variant.rb
59
+ - lib/showroom/models/resource.rb
60
+ - lib/showroom/models/search.rb
61
+ - lib/showroom/models/search/article_suggestion.rb
62
+ - lib/showroom/models/search/collection_suggestion.rb
63
+ - lib/showroom/models/search/page_suggestion.rb
64
+ - lib/showroom/models/search/product_suggestion.rb
65
+ - lib/showroom/models/search/query_suggestion.rb
66
+ - lib/showroom/models/search/result.rb
67
+ - lib/showroom/models/search/suggestion.rb
68
+ homepage: https://github.com/01max/showroom
69
+ licenses:
70
+ - GPL-3.0-or-later
71
+ metadata:
72
+ rubygems_mfa_required: 'true'
73
+ homepage_uri: https://github.com/01max/showroom
74
+ source_code_uri: https://github.com/01max/showroom
75
+ changelog_uri: https://github.com/01max/showroom/blob/main/CHANGELOG.md
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '3.3'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubygems_version: 3.6.7
91
+ specification_version: 4
92
+ summary: An unauthenticated Ruby client for Shopify's public API.
93
+ test_files: []