solidus_feeds 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +41 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.github_changelog_generator +2 -0
  6. data/.gitignore +20 -0
  7. data/.rspec +2 -0
  8. data/.rubocop.yml +12 -0
  9. data/Gemfile +33 -0
  10. data/LICENSE +26 -0
  11. data/README.md +246 -0
  12. data/Rakefile +6 -0
  13. data/app/assets/javascripts/spree/backend/solidus_feeds.js +2 -0
  14. data/app/assets/javascripts/spree/frontend/solidus_feeds.js +2 -0
  15. data/app/assets/stylesheets/spree/backend/solidus_feeds.css +4 -0
  16. data/app/assets/stylesheets/spree/frontend/solidus_feeds.css +4 -0
  17. data/bin/console +17 -0
  18. data/bin/rails +7 -0
  19. data/bin/rails-engine +13 -0
  20. data/bin/rails-sandbox +16 -0
  21. data/bin/rake +7 -0
  22. data/bin/sandbox +86 -0
  23. data/bin/setup +8 -0
  24. data/config/locales/en.yml +5 -0
  25. data/config/routes.rb +5 -0
  26. data/lib/generators/solidus_feeds/install/install_generator.rb +37 -0
  27. data/lib/generators/solidus_feeds/install/templates/initializer.rb +15 -0
  28. data/lib/solidus_feeds.rb +13 -0
  29. data/lib/solidus_feeds/configuration.rb +59 -0
  30. data/lib/solidus_feeds/engine.rb +19 -0
  31. data/lib/solidus_feeds/factories.rb +4 -0
  32. data/lib/solidus_feeds/feed.rb +21 -0
  33. data/lib/solidus_feeds/generators/google_merchant.rb +106 -0
  34. data/lib/solidus_feeds/publishers/s3.rb +30 -0
  35. data/lib/solidus_feeds/publishers/static_file.rb +17 -0
  36. data/lib/solidus_feeds/testing_support/factories.rb +4 -0
  37. data/lib/solidus_feeds/version.rb +5 -0
  38. data/solidus_feeds.gemspec +36 -0
  39. data/spec/lib/solidus_feeds/generators/google_merchant_spec.rb +132 -0
  40. data/spec/lib/solidus_feeds/publishers/s3_spec.rb +37 -0
  41. data/spec/lib/solidus_feeds/publishers/static_file_spec.rb +31 -0
  42. data/spec/lib/solidus_feeds/solidus_feeds_spec.rb +23 -0
  43. data/spec/solidus_feeds_spec.rb +29 -0
  44. data/spec/spec_helper.rb +31 -0
  45. metadata +157 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 58531c73946da9c28461e17502503cf2b5a4705c6a616f08c9c113013c98a5a0
4
+ data.tar.gz: 57e0b6b0f02d1a6b9da731c6a6c4f248fc4804ab26e810324849a7691a7deab2
5
+ SHA512:
6
+ metadata.gz: b57c1b93cfb53bd1d188028e101cb92355a3b5920becb8758374c511141f2eb91858a0277d00953537aa0537e3168670ed7989854e44590f46daae333f547645
7
+ data.tar.gz: aca21bcca2411cd27ae410ca6e13ea9a7848ad428a9d6168b59da33c2dfe94579fe376eca2046a395cdac3ee970c31ab67f513dadb922146dfbdf6e71c29fe8b
@@ -0,0 +1,41 @@
1
+ version: 2.1
2
+
3
+ orbs:
4
+ # Always take the latest version of the orb, this allows us to
5
+ # run specs against Solidus supported versions only without the need
6
+ # to change this configuration every time a Solidus version is released
7
+ # or goes EOL.
8
+ solidusio_extensions: solidusio/extensions@volatile
9
+
10
+ jobs:
11
+ run-specs-with-postgres:
12
+ executor: solidusio_extensions/postgres
13
+ steps:
14
+ - solidusio_extensions/run-tests
15
+ run-specs-with-mysql:
16
+ executor: solidusio_extensions/mysql
17
+ steps:
18
+ - solidusio_extensions/run-tests
19
+ lint-code:
20
+ executor: solidusio_extensions/sqlite-memory
21
+ steps:
22
+ - solidusio_extensions/lint-code
23
+
24
+ workflows:
25
+ "Run specs on supported Solidus versions":
26
+ jobs:
27
+ - run-specs-with-postgres
28
+ - run-specs-with-mysql
29
+ - lint-code
30
+
31
+ "Weekly run specs against master":
32
+ triggers:
33
+ - schedule:
34
+ cron: "0 0 * * 4" # every Thursday
35
+ filters:
36
+ branches:
37
+ only:
38
+ - master
39
+ jobs:
40
+ - run-specs-with-postgres
41
+ - run-specs-with-mysql
@@ -0,0 +1,5 @@
1
+ bump:
2
+ recurse: false
3
+ file: 'lib/solidus_feeds/version.rb'
4
+ message: Bump SolidusFeeds to %{version}
5
+ tag: true
@@ -0,0 +1,17 @@
1
+ # Number of days of inactivity before an issue becomes stale
2
+ daysUntilStale: 60
3
+ # Number of days of inactivity before a stale issue is closed
4
+ daysUntilClose: false
5
+ # Issues with these labels will never be considered stale
6
+ exemptLabels:
7
+ - pinned
8
+ - security
9
+ # Label to use when marking an issue as stale
10
+ staleLabel: stale
11
+ # Comment to post when marking an issue as stale. Set to `false` to disable
12
+ markComment: >
13
+ This issue has been automatically marked as stale because it has not had
14
+ recent activity. It might be closed if no further activity occurs. Thank you
15
+ for your contributions.
16
+ # Comment to post when closing a stale issue. Set to `false` to disable
17
+ closeComment: false
@@ -0,0 +1,2 @@
1
+ issues=false
2
+ exclude-labels=infrastructure
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ \#*
3
+ *~
4
+ .#*
5
+ .DS_Store
6
+ .idea
7
+ .project
8
+ .sass-cache
9
+ coverage
10
+ Gemfile.lock
11
+ tmp
12
+ nbproject
13
+ pkg
14
+ *.swp
15
+ spec/dummy
16
+ spec/examples.txt
17
+ /sandbox
18
+ .rvmrc
19
+ .ruby-version
20
+ .ruby-gemset
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,12 @@
1
+ require:
2
+ - solidus_dev_support/rubocop
3
+
4
+ AllCops:
5
+ NewCops: disable
6
+
7
+
8
+ RSpec/MultipleExpectations:
9
+ Enabled: false
10
+
11
+ Style/ClassAndModuleChildren:
12
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5
+
6
+ branch = ENV.fetch('SOLIDUS_BRANCH', 'master')
7
+ gem 'solidus', github: 'solidusio/solidus', branch: branch
8
+
9
+ # Needed to help Bundler figure out how to resolve dependencies,
10
+ # otherwise it takes forever to resolve them.
11
+ # See https://github.com/bundler/bundler/issues/6677
12
+ gem 'rails', '>0.a'
13
+
14
+ # Provides basic authentication functionality for testing parts of your engine
15
+ gem 'solidus_auth_devise'
16
+
17
+ case ENV['DB']
18
+ when 'mysql'
19
+ gem 'mysql2'
20
+ when 'postgresql'
21
+ gem 'pg'
22
+ else
23
+ gem 'sqlite3'
24
+ end
25
+
26
+ gemspec
27
+
28
+ # Use a local Gemfile to include development dependencies that might not be
29
+ # relevant for the project or for other contributors, e.g. pry-byebug.
30
+ #
31
+ # We use `send` instead of calling `eval_gemfile` to work around an issue with
32
+ # how Dependabot parses projects: https://github.com/dependabot/dependabot-core/issues/1658.
33
+ send(:eval_gemfile, 'Gemfile-local') if File.exist? 'Gemfile-local'
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2021 Nebulab SRLs
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name Solidus nor the names of its contributors may be used to
13
+ endorse or promote products derived from this software without specific
14
+ prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,246 @@
1
+ # Solidus Feeds
2
+
3
+ [![CircleCI](https://circleci.com/gh/solidusio-contrib/solidus_feeds.svg?style=shield)](https://circleci.com/gh/solidusio-contrib/solidus_feeds)
4
+ [![codecov](https://codecov.io/gh/solidusio-contrib/solidus_feeds/branch/master/graph/badge.svg)](https://codecov.io/gh/solidusio-contrib/solidus_feeds)
5
+
6
+ <!-- Explain what your extension does. -->
7
+ A framework for producing and publishing feeds on Solidus.
8
+
9
+ ## Installation
10
+
11
+ Add solidus_feeds to your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'solidus_feeds'
15
+ ```
16
+
17
+ Bundle your dependencies and run the installation generator:
18
+
19
+ ```shell
20
+ bin/rails generate solidus_feeds:install
21
+ ```
22
+
23
+ ## Out of the box usage
24
+
25
+ <!-- Explain how to use your extension once it's been installed. -->
26
+
27
+ Let's say that you want to generate a XML feed for products belonging to the `Shoes` taxon,
28
+ consumable by Google Merchant compatible marketplaces and make it publicly available on your
29
+ `my-bucket` S3 bucket at the path `foo/bar.xml`.
30
+
31
+ To register the feed you'd need to add the following code to an initializer such as
32
+ `config/initializers/solidus.rb` or better yet `config/initializers/solidus_feeds.rb`
33
+
34
+ ```ruby
35
+ SolidusFeeds.config.register :google_merchant_shoes do |feed|
36
+ taxon = Spree::Taxon.find_by(name: "Shoes")
37
+ products = Spree::Product.available.in_taxon(taxon)
38
+
39
+ feed.generator = SolidusFeeds::Generators::GoogleMerchant.new(products)
40
+ feed.publisher = SolidusFeeds::Publishers::S3.new(bucket: "my-bucket", object_key: "foo/bar.xml")
41
+ end
42
+ ```
43
+
44
+ Then make your Solidus app generate and publish the feed by calling the following line. It's
45
+ recommended to call it in a background job, especially when generating feeds with large amounts of
46
+ products.
47
+
48
+ ```ruby
49
+ SolidusFeeds.find(:google_merchant_shoes).publish
50
+ ```
51
+
52
+ Having it in a background job makes it easier to:
53
+
54
+ - Launch it manually from the backend dashboard
55
+ - Make it refresh periodically via cron, sidekiq-scheduler, Heroku scheduler or similar
56
+ - Refresh the feed when receiving data from a [webhook](https://github.com/solidusio-contrib/solidus_webhooks)
57
+ or after specific Solidus events
58
+
59
+ ## Serving the feed from the products controller (legacy)
60
+
61
+ We suggest to avoid this behaviour because it could be resource intensive, especially with a large
62
+ number of products.
63
+
64
+ If you want to support the legacy behaviour of [`solidus_product_feed`](https://github.com/solidusio-contrib/solidus_product_feed)
65
+ and publish a XML feed at `/products.rss`, you can add the following decorator:
66
+
67
+ ```ruby
68
+ # app/decorators/controllers/solidus_feeds/spree/products_controller.rb
69
+
70
+ module SolidusFeeds
71
+ module Spree
72
+ module ProductsControllerDecorator
73
+ def self.prepended(klass)
74
+ klass.respond_to :rss, only: :index
75
+ klass.before_action :verify_requested_format!, only: :index
76
+ end
77
+
78
+ def index
79
+ render as: :xml, body: load_feed_products if request.format.rss?
80
+ super
81
+ end
82
+
83
+ private
84
+
85
+ def load_feed_products
86
+ @products = ::Spree::Product.all
87
+ io = StringIO.new
88
+ SolidusFeeds::Generators::GoogleMerchant
89
+ .new(@products, host: 'https://example.com')
90
+ .call(io)
91
+ io.rewind
92
+ io.read
93
+ end
94
+
95
+ ::Spree::ProductsController.prepend self
96
+ end
97
+ end
98
+ end
99
+ ```
100
+
101
+ ## Publishing backends
102
+
103
+ ### S3
104
+
105
+ If you don't want to configure a S3 `client` each time, you can load your AWS config in an
106
+ initializer:
107
+
108
+ ```ruby
109
+ # config/initializers/aws.rb
110
+
111
+ Aws.config[:profile] = 'my-profile'
112
+ ```
113
+
114
+ Then config your S3 publisher specifying the `bucket`, `object_key` and an optional `client` if you
115
+ need custom configuration on a per-publisher basis.
116
+
117
+ ```ruby
118
+ # config/initializers/solidus_feeds.rb
119
+
120
+ SolidusFeeds.config.register :all_products do |feed|
121
+ feed.generator = SolidusFeeds::Generators::GoogleMerchant.new(Spree::Product.all)
122
+ feed.publisher = SolidusFeeds::Publishers::S3.new(
123
+ bucket: "foo",
124
+ object_key: "bar/my_feed.xml",
125
+ client: Aws::S3::Client.new(…), # This is optional - use only if a custom config is needed
126
+ )
127
+
128
+ # visit https://s3.us-east-1.amazonaws.com/foo/bar/my_feed.xml
129
+ end
130
+ ```
131
+
132
+ ### Static file
133
+
134
+ To publish the feed directly from an app directory (e.g. the `public` directory), you can use the
135
+ Static File Publisher as such:
136
+
137
+ ```ruby
138
+ # config/initializers/solidus_feeds.rb
139
+
140
+ SolidusFeeds.config.register :all_products do |feed|
141
+ feed.generator = SolidusFeeds::Generators::GoogleMerchant.new(Spree::Product.all)
142
+ feed.publisher = SolidusFeeds::Publishers::StaticFile.new(
143
+ path: Rails.root.join('public/products.xml')
144
+ )
145
+ end
146
+ ```
147
+
148
+ ## Builtin Marketplace format generators
149
+
150
+ - Google Merchant XML: compatible with Google Merchant and Facebook/Instagram feeds
151
+
152
+ ## Creating your own Generators and Publishers
153
+
154
+ Both the generator and the publisher are expected to respond to `#call`.
155
+
156
+ The publisher's `#call` method is expected to yield an IO-like object that responds to `#<<`.
157
+
158
+ ### Example
159
+
160
+ For example a simple feed that will publish recently added products to Rails' public folder in JSON
161
+ format would look like this:
162
+
163
+ ```ruby
164
+ class FilePublisher < Struct.new(:path)
165
+ def call
166
+ File.open(path, 'w') do |file|
167
+ yield file
168
+ end
169
+ end
170
+ end
171
+
172
+ class JsonProductFeed < Struct.new(:products)
173
+ def call(io)
174
+ products.find_each do |product|
175
+ io << product.to_json
176
+ end
177
+ end
178
+ end
179
+
180
+ SolidusFeeds.register :recent_products do |feed|
181
+ recent_products = Spree::Product.where(created_at: Time.now..2.weeks.ago)
182
+
183
+ feed.generator = JsonProductFeed.new(recent_products)
184
+ feed.publisher = FilePublisher.new(Rails.root.join("public/product.json")
185
+ end
186
+ ```
187
+
188
+ ## Development
189
+
190
+ ### Testing the extension
191
+
192
+ First bundle your dependencies, then run `bin/rake`. `bin/rake` will default to building the dummy
193
+ app if it does not exist, then it will run specs. The dummy app can be regenerated by using
194
+ `bin/rake extension:test_app`.
195
+
196
+ ```shell
197
+ bin/rake
198
+ ```
199
+
200
+ To run [Rubocop](https://github.com/bbatsov/rubocop) static code analysis run
201
+
202
+ ```shell
203
+ bundle exec rubocop
204
+ ```
205
+
206
+ When testing your application's integration with this extension you may use its factories.
207
+ Simply add this require statement to your spec_helper:
208
+
209
+ ```ruby
210
+ require 'solidus_feeds/factories'
211
+ ```
212
+
213
+ ### Running the sandbox
214
+
215
+ To run this extension in a sandboxed Solidus application, you can run `bin/sandbox`. The path for
216
+ the sandbox app is `./sandbox` and `bin/rails` will forward any Rails commands to
217
+ `sandbox/bin/rails`.
218
+
219
+ Here's an example:
220
+
221
+ ```
222
+ $ bin/rails server
223
+ => Booting Puma
224
+ => Rails 6.0.2.1 application starting in development
225
+ * Listening on tcp://127.0.0.1:3000
226
+ Use Ctrl-C to stop
227
+ ```
228
+
229
+ ### Updating the changelog
230
+
231
+ Before and after releases the changelog should be updated to reflect the up-to-date status of
232
+ the project:
233
+
234
+ ```shell
235
+ bin/rake changelog
236
+ git add CHANGELOG.md
237
+ git commit -m "Update the changelog"
238
+ ```
239
+
240
+ ### Releasing new versions
241
+
242
+ Please refer to the dedicated [page](https://github.com/solidusio/solidus/wiki/How-to-release-extensions) on Solidus wiki.
243
+
244
+ ## License
245
+
246
+ Copyright (c) 2021 Nebulab SRLs, released under the New BSD License.
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_dev_support/rake_tasks'
4
+ SolidusDevSupport::RakeTasks.install
5
+
6
+ task default: 'extension:specs'
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/backend/all.js'
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/frontend/all.js'
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */