solidus_content 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +72 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.gitignore +20 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +38 -0
  8. data/Gemfile +38 -0
  9. data/LICENSE +26 -0
  10. data/README.md +439 -0
  11. data/Rakefile +6 -0
  12. data/app/assets/javascripts/spree/backend/solidus_content.js +2 -0
  13. data/app/assets/javascripts/spree/frontend/solidus_content.js +2 -0
  14. data/app/assets/stylesheets/spree/backend/solidus_content.css +4 -0
  15. data/app/assets/stylesheets/spree/frontend/solidus_content.css +4 -0
  16. data/app/controllers/solidus_content/resource_controller.rb +35 -0
  17. data/app/controllers/spree/admin/entries_controller.rb +25 -0
  18. data/app/controllers/spree/admin/entry_types_controller.rb +25 -0
  19. data/app/controllers/spree/solidus_content_controller.rb +13 -0
  20. data/app/models/solidus_content/application_record.rb +5 -0
  21. data/app/models/solidus_content/entry.rb +44 -0
  22. data/app/models/solidus_content/entry_type.rb +55 -0
  23. data/app/models/solidus_content/provider/fields.rb +20 -0
  24. data/app/views/spree/admin/entries/_form.html.erb +31 -0
  25. data/app/views/spree/admin/entries/_options_form.html.erb +18 -0
  26. data/app/views/spree/admin/entries/edit.html.erb +16 -0
  27. data/app/views/spree/admin/entries/index.html.erb +54 -0
  28. data/app/views/spree/admin/entries/new.html.erb +21 -0
  29. data/app/views/spree/admin/entry_types/_form.html.erb +30 -0
  30. data/app/views/spree/admin/entry_types/_options_form.html.erb +16 -0
  31. data/app/views/spree/admin/entry_types/edit.html.erb +16 -0
  32. data/app/views/spree/admin/entry_types/index.html.erb +54 -0
  33. data/app/views/spree/admin/entry_types/new.html.erb +21 -0
  34. data/app/views/spree/admin/shared/_solidus_content_sub_menu.html.erb +9 -0
  35. data/bin/console +17 -0
  36. data/bin/rails +7 -0
  37. data/bin/rails-engine +13 -0
  38. data/bin/rails-sandbox +16 -0
  39. data/bin/rake +7 -0
  40. data/bin/rspec +7 -0
  41. data/bin/sandbox +84 -0
  42. data/bin/setup +8 -0
  43. data/config/initializers/spree.rb +12 -0
  44. data/config/locales/en.yml +51 -0
  45. data/config/routes.rb +14 -0
  46. data/db/migrate/20200207135842_create_solidus_content_entries.rb +11 -0
  47. data/db/migrate/20200306110114_create_solidus_content_entry_types.rb +12 -0
  48. data/lib/generators/solidus_content/install/install_generator.rb +36 -0
  49. data/lib/generators/solidus_content/install/templates/initializer.rb +20 -0
  50. data/lib/solidus_content.rb +10 -0
  51. data/lib/solidus_content/active_record.rb +7 -0
  52. data/lib/solidus_content/configuration.rb +38 -0
  53. data/lib/solidus_content/engine.rb +19 -0
  54. data/lib/solidus_content/factories.rb +13 -0
  55. data/lib/solidus_content/providers.rb +23 -0
  56. data/lib/solidus_content/providers/contentful.rb +36 -0
  57. data/lib/solidus_content/providers/json.rb +27 -0
  58. data/lib/solidus_content/providers/prismic.rb +36 -0
  59. data/lib/solidus_content/providers/raw.rb +22 -0
  60. data/lib/solidus_content/providers/solidus_static_content.rb +23 -0
  61. data/lib/solidus_content/providers/yaml.rb +33 -0
  62. data/lib/solidus_content/version.rb +5 -0
  63. data/solidus_content.gemspec +36 -0
  64. data/spec/features/admin/content/create_entry_spec.rb +36 -0
  65. data/spec/features/admin/content/create_entry_type_spec.rb +36 -0
  66. data/spec/features/admin/content/delete_entry_spec.rb +20 -0
  67. data/spec/features/admin/content/delete_entry_type_spec.rb +20 -0
  68. data/spec/features/admin/content/edit_entry_spec.rb +45 -0
  69. data/spec/features/admin/content/edit_entry_type_spec.rb +44 -0
  70. data/spec/features/admin/content/list_entries_spec.rb +33 -0
  71. data/spec/features/admin/content/list_entry_types_spec.rb +33 -0
  72. data/spec/features/admin/sidebar_spec.rb +19 -0
  73. data/spec/features/render_content_with_views_spec.rb +41 -0
  74. data/spec/fixtures/content/example.json +1 -0
  75. data/spec/fixtures/content/example.yaml +1 -0
  76. data/spec/fixtures/content/example.yml +1 -0
  77. data/spec/fixtures/content/example_2.yaml +1 -0
  78. data/spec/models/solidus_content/entry_spec.rb +100 -0
  79. data/spec/models/solidus_content/entry_type_spec.rb +106 -0
  80. data/spec/solidus_content/configuration_spec.rb +37 -0
  81. data/spec/solidus_content/providers/contentful_spec.rb +56 -0
  82. data/spec/solidus_content/providers/json_spec.rb +33 -0
  83. data/spec/solidus_content/providers/prismic_spec.rb +82 -0
  84. data/spec/solidus_content/providers/solidus_static_content_spec.rb +34 -0
  85. data/spec/solidus_content/providers/yaml_spec.rb +51 -0
  86. data/spec/solidus_content_spec.rb +23 -0
  87. data/spec/spec_helper.rb +32 -0
  88. data/spec/support/warden.rb +5 -0
  89. metadata +206 -0
@@ -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
+ */
@@ -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/frontend/all.css'
4
+ */
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusContent
4
+ class ResourceController < Spree::Admin::ResourceController
5
+ private
6
+
7
+ def location_after_save
8
+ edit_object_url(@object)
9
+ end
10
+
11
+ def collection
12
+ super.page(params[:page] || 0)
13
+ end
14
+
15
+ def model_class
16
+ "SolidusContent::#{controller_name.classify}".constantize
17
+ end
18
+
19
+ def new_object_url(options = {})
20
+ send(:"new_admin_#{controller_name.singularize}_path", options)
21
+ end
22
+
23
+ def edit_object_url(object, options = {})
24
+ send(:"edit_admin_#{controller_name.singularize}_path", object, options)
25
+ end
26
+
27
+ def object_url(object = nil, options = {})
28
+ send(:"admin_#{controller_name.singularize}_path", object, options)
29
+ end
30
+
31
+ def collection_url(options = {})
32
+ send(:"admin_#{controller_name}_path", options)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Admin
5
+ class EntriesController < SolidusContent::ResourceController
6
+ helper_method :model_class
7
+
8
+ def show
9
+ redirect_to action: :edit
10
+ end
11
+
12
+ private
13
+
14
+ def permitted_resource_params
15
+ params
16
+ .require(:solidus_content_entry)
17
+ .permit(:slug, :entry_type_id, *provider_params)
18
+ end
19
+
20
+ def provider_params
21
+ @object.entry_fields || []
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Admin
5
+ class EntryTypesController < SolidusContent::ResourceController
6
+ helper_method :model_class
7
+
8
+ def show
9
+ redirect_to action: :edit
10
+ end
11
+
12
+ private
13
+
14
+ def permitted_resource_params
15
+ params
16
+ .require(:solidus_content_entry_type)
17
+ .permit(:name, :provider_name, *provider_params)
18
+ end
19
+
20
+ def provider_params
21
+ @object.entry_type_fields || []
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'solidus_content/entry_type'
4
+
5
+ class Spree::SolidusContentController < Spree::StoreController
6
+ def show
7
+ slug = params[:id] || :default
8
+ type = params[:type]
9
+
10
+ @entry = ::SolidusContent::Entry.by_type(type).by_slug(slug)
11
+ render action: type
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusContent::ApplicationRecord < ActiveRecord::Base
4
+ self.abstract_class = true
5
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusContent::Entry < SolidusContent::ApplicationRecord
4
+ include SolidusContent::Provider::Fields
5
+
6
+ belongs_to :entry_type
7
+
8
+ after_initialize { self.options ||= {} }
9
+ after_initialize :inject_entry_fields, if: :entry_type_id?
10
+
11
+ validates :slug, presence: true, uniqueness: { scope: :entry_type_id }
12
+
13
+ scope :by_slug, ->(slug) { find_by!(slug: slug) }
14
+ scope :by_type, ->(type) {
15
+ unless type.is_a? SolidusContent::EntryType
16
+ type = SolidusContent::EntryType.by_name(type)
17
+ end
18
+ where(entry_type: type)
19
+ }
20
+
21
+ def self.data_for(type, slug)
22
+ by_type(type).by_slug(slug).data
23
+ end
24
+
25
+ def data
26
+ content[:data]
27
+ end
28
+
29
+ def content
30
+ @content ||= entry_type.content_for(self)
31
+ end
32
+
33
+ def entry_fields
34
+ return unless entry_type_id?
35
+
36
+ entry_type.provider_class.entry_fields
37
+ end
38
+
39
+ private
40
+
41
+ def inject_entry_fields
42
+ provider_based_attr_reader(entry_fields)
43
+ end
44
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusContent::EntryType < SolidusContent::ApplicationRecord
4
+ include SolidusContent::Provider::Fields
5
+
6
+ has_many :entries, dependent: :destroy
7
+
8
+ scope :by_name, ->(name) { find_by!(name: name) }
9
+
10
+ validates :name, presence: true
11
+ validates :provider_name, presence: true
12
+ validates :name, :provider_name, presence: true
13
+ validate :ensure_provider_name_is_not_changed
14
+
15
+ after_initialize { self.options ||= {} }
16
+ after_initialize :inject_provider_fields, if: :provider_name?
17
+
18
+ scope :by_name, ->(name) { find_by!(name: name) }
19
+
20
+ def content_for(entry)
21
+ provider_class.call(
22
+ slug: entry.slug,
23
+ type: name,
24
+ provider: provider_name,
25
+ options: entry.options.symbolize_keys,
26
+ type_options: options.symbolize_keys,
27
+ )
28
+ end
29
+
30
+ def provider_class
31
+ SolidusContent.config.providers[provider_name.to_sym]
32
+ end
33
+
34
+ def provider_name_readonly?
35
+ persisted?
36
+ end
37
+
38
+ def entry_type_fields
39
+ return unless provider_name
40
+
41
+ provider_class.entry_type_fields
42
+ end
43
+
44
+ private
45
+
46
+ def inject_provider_fields
47
+ provider_based_attr_reader(entry_type_fields)
48
+ end
49
+
50
+ def ensure_provider_name_is_not_changed
51
+ return unless provider_name_changed? && persisted?
52
+
53
+ errors.add :provider_name, :readonly
54
+ end
55
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusContent
4
+ module Provider
5
+ module Fields
6
+ def provider_based_attr_reader(attrs)
7
+ attrs.each do |attr|
8
+ define_singleton_method(attr) do
9
+ options[attr.to_s] || ''
10
+ end
11
+
12
+ define_singleton_method("#{attr}=") do |value|
13
+ attribute_will_change!(:options)
14
+ options[attr.to_s] = value
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ <% entry_type_options = SolidusContent::EntryType.pluck(:name, :id) %>
2
+
3
+ <div data-hook="admin_entry_form_fields">
4
+ <div class="row">
5
+ <div class="left col-12" data-hook="admin_entry_form">
6
+ <div class="row">
7
+ <div class="col-6" data-hook="admin_entry_form_name">
8
+ <%= form.field_container :slug do %>
9
+ <%= form.field_hint :slug %>
10
+ <%= form.label :slug, class: 'required' %><br />
11
+ <%= form.text_field :slug, class: 'fullwidth' %>
12
+ <% end %>
13
+ </div>
14
+
15
+ <div class="col-6" data-hook="admin_entry_form_provider">
16
+ <%= form.field_container :entry_type_id do %>
17
+ <%= form.field_hint :entry_type_id %>
18
+ <%= form.label :entry_type_id, class: 'required' %><br />
19
+ <%= form.select :entry_type_id, entry_type_options, { include_blank: false }, { disabled: form.object.persisted?, class: 'custom-select fullwidth' } %>
20
+ <% end %>
21
+ </div>
22
+
23
+ <%= form.hidden_field :options, value: '{}' %>
24
+ </div>
25
+ </div>
26
+ </div>
27
+
28
+ <% if form.object.persisted? && @object.entry_fields.count > 0 %>
29
+ <%= render "options_form", form: form %>
30
+ <% end %>
31
+ </div>
@@ -0,0 +1,18 @@
1
+ <fieldset class="no-border-bottom">
2
+ <legend>
3
+ <%= @entry.entry_type.name %>
4
+ <%= model_class.human_attribute_name(:options) %>
5
+ (<%= SolidusContent.human_provider_name(@entry.entry_type.provider_name) %>)
6
+ </legend>
7
+
8
+ <div class="row">
9
+ <% @entry.entry_fields.each do |field| %>
10
+ <div class="col-12" data-hook="admin_entry_<%= field %>">
11
+ <div class="field" id="solidus_content_entry_<%= field %>_field">
12
+ <%= form.label field %>
13
+ <%= form.text_field field, class: 'fullwidth' %>
14
+ </div>
15
+ </div>
16
+ <% end %>
17
+ </div>
18
+ </fieldset>
@@ -0,0 +1,16 @@
1
+ <% admin_breadcrumb link_to(plural_resource_name(model_class), spree.admin_entries_path) %>
2
+ <% admin_breadcrumb @object.slug %>
3
+
4
+ <%= render 'spree/shared/error_messages', target: @object %>
5
+
6
+ <%= form_for @entry, url: spree.admin_entry_path(@entry) do |form| %>
7
+ <fieldset data-hook="edit_entry">
8
+ <%= content_tag :legend, model_class.model_name.human %>
9
+ <%= render 'form', form: form %>
10
+
11
+ <div class="form-buttons filter-actions actions" data-hook="buttons">
12
+ <%= submit_tag t('spree.actions.update'), class: 'btn btn-primary' %>
13
+ <%= link_to t('spree.actions.cancel'), collection_url, class: 'button' %>
14
+ </div>
15
+ </fieldset>
16
+ <% end %>
@@ -0,0 +1,54 @@
1
+ <% admin_layout "full-width" %>
2
+
3
+ <% admin_breadcrumb plural_resource_name(model_class) %>
4
+
5
+ <% content_for :page_actions do %>
6
+ <li>
7
+ <%= link_to t('spree.admin.entry.new'), new_object_url, class: 'btn btn-primary' %>
8
+ </li>
9
+ <% end if can?(:create, model_class) %>
10
+
11
+ <%= paginate @collection, theme: "solidus_admin" %>
12
+
13
+ <% if @collection.any? %>
14
+ <table class="index" id="listing_entries">
15
+ <colgroup>
16
+ <col style="width: 40%;">
17
+ <col style="width: 40%;">
18
+ <col style="width: 20%;">
19
+ </colgroup>
20
+ <thead>
21
+ <tr data-hook="admin_products_index_headers">
22
+ <th><%= model_class.human_attribute_name(:slug) %></th>
23
+ <th><%= model_class.human_attribute_name(:entry_type) %></th>
24
+ <th></th>
25
+ </tr>
26
+ </thead>
27
+ <tbody>
28
+ <% @collection.each do |entry| %>
29
+ <tr id="<%= spree_dom_id entry %>" data-hook="admin_entries_index_rows">
30
+ <td>
31
+ <%= link_to entry.slug, object_url(entry) %>
32
+ </td>
33
+ <td>
34
+ <%= link_to entry.entry_type.name, admin_entry_type_path(entry.entry_type) %>
35
+ </td>
36
+ <td class="actions" data-hook="admin_entries_index_row_actions">
37
+ <%= link_to_edit entry, url: edit_object_url(entry),
38
+ class: 'edit edit-entry' if can?(:update, entry) %>
39
+ <%= link_to_delete entry, url: object_url(entry),
40
+ class: 'trash delete-entry' if can?(:destroy, entry) %>
41
+ </td>
42
+ </tr>
43
+ <% end %>
44
+ </tbody>
45
+ </table>
46
+ <% else %>
47
+ <div class="no-objects-found">
48
+ <%= render 'spree/admin/shared/no_objects_found',
49
+ resource: model_class,
50
+ new_resource_url: new_object_url %>
51
+ </div>
52
+ <% end %>
53
+
54
+ <%= paginate @collection, theme: "solidus_admin" %>
@@ -0,0 +1,21 @@
1
+ <% admin_breadcrumb link_to(plural_resource_name(model_class), collection_url) %>
2
+ <% admin_breadcrumb "#{t('spree.actions.new')} #{model_class.model_name.human}" %>
3
+
4
+
5
+ <%= render 'spree/shared/error_messages', target: @object %>
6
+
7
+ <%= form_for @object, url: collection_url do |form| %>
8
+ <fieldset data-hook="new_entry">
9
+ <legend align="center">
10
+ <%= admin_hint nil, t('solidus_content.hints.new_entry') %>
11
+ <%= t('new', scope: %w[spree admin entry]) %>
12
+ </legend>
13
+
14
+ <%= render 'form', form: form %>
15
+
16
+ <div class="form-buttons filter-actions actions" data-hook="buttons">
17
+ <%= submit_tag t('spree.actions.create'), class: 'btn btn-primary' %>
18
+ <%= link_to t('spree.actions.cancel'), collection_url, class: 'button' %>
19
+ </div>
20
+ </fieldset>
21
+ <% end %>
@@ -0,0 +1,30 @@
1
+ <% provider_options = SolidusContent.provider_names.map do |name|
2
+ [SolidusContent.human_provider_name(name), name]
3
+ end %>
4
+
5
+ <div data-hook="admin_entry_type_form_fields">
6
+ <div class="row">
7
+ <div class="left col-12" data-hook="admin_entry_type_form">
8
+ <div class="row">
9
+ <div class="col-6" data-hook="admin_entry_type_form_name">
10
+ <%= form.field_container :user do %>
11
+ <%= form.label :name, class: 'required' %><br />
12
+ <%= form.text_field :name, class: 'fullwidth' %>
13
+ <% end %>
14
+ </div>
15
+
16
+ <div class="col-6" data-hook="admin_entry_type_form_provider">
17
+ <%= form.field_container :provider_name do %>
18
+ <%= form.field_hint :provider_name %>
19
+ <%= form.label :provider_name, class: 'required' %><br />
20
+ <%= form.select :provider_name, provider_options, { include_blank: false }, { disabled: form.object.provider_name_readonly?, class: 'custom-select fullwidth' } %>
21
+ <% end %>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+
27
+ <% if form.object.provider_name_readonly? && @object.entry_type_fields.count > 0 %>
28
+ <%= render "options_form", form: form %>
29
+ <% end %>
30
+ </div>
@@ -0,0 +1,16 @@
1
+ <fieldset class="options-form no-border-bottom">
2
+ <legend>
3
+ <%= SolidusContent.human_provider_name(@entry_type.provider_name) %>
4
+ <%= model_class.human_attribute_name(:options) %>
5
+ </legend>
6
+ <div class="row">
7
+ <% @entry_type.entry_type_fields.each do |field| %>
8
+ <div class="col-12" data-hook="admin_entry_type_<%= field %>">
9
+ <div class="field" id="solidus_content_entry_type_<%= field %>_field">
10
+ <%= form.label field %>
11
+ <%= form.text_field field, class: 'fullwidth' %>
12
+ </div>
13
+ </div>
14
+ <% end %>
15
+ </div>
16
+ </fieldset>