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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +23 -0
- data/LICENSE +674 -0
- data/README.md +200 -0
- data/lib/showroom/client.rb +95 -0
- data/lib/showroom/core/configurable.rb +77 -0
- data/lib/showroom/core/connection.rb +88 -0
- data/lib/showroom/core/countable.rb +79 -0
- data/lib/showroom/core/default.rb +61 -0
- data/lib/showroom/core/error.rb +53 -0
- data/lib/showroom/core/store_url.rb +60 -0
- data/lib/showroom/core/version.rb +9 -0
- data/lib/showroom/http/middleware/raise_error.rb +81 -0
- data/lib/showroom/models/collection.rb +67 -0
- data/lib/showroom/models/product.rb +142 -0
- data/lib/showroom/models/product_image.rb +12 -0
- data/lib/showroom/models/product_option.rb +13 -0
- data/lib/showroom/models/product_variant.rb +39 -0
- data/lib/showroom/models/resource.rb +143 -0
- data/lib/showroom/models/search/article_suggestion.rb +14 -0
- data/lib/showroom/models/search/collection_suggestion.rb +19 -0
- data/lib/showroom/models/search/page_suggestion.rb +16 -0
- data/lib/showroom/models/search/product_suggestion.rb +20 -0
- data/lib/showroom/models/search/query_suggestion.rb +14 -0
- data/lib/showroom/models/search/result.rb +55 -0
- data/lib/showroom/models/search/suggestion.rb +50 -0
- data/lib/showroom/models/search.rb +28 -0
- data/lib/showroom/models.rb +20 -0
- data/lib/showroom.rb +106 -0
- metadata +93 -0
|
@@ -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: []
|