hotsheet 0.1.1 → 0.2.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +21 -34
  4. data/app/assets/hotsheet.css +1 -0
  5. data/app/assets/hotsheet.js +33 -0
  6. data/app/controllers/hotsheet/application_controller.rb +2 -4
  7. data/app/controllers/hotsheet/sheets_controller.rb +47 -0
  8. data/app/helpers/hotsheet/application_helper.rb +3 -3
  9. data/app/views/hotsheet/shared/_nav.html.erb +14 -0
  10. data/app/views/hotsheet/sheets/error.html.erb +1 -0
  11. data/app/views/hotsheet/sheets/index.html.erb +20 -0
  12. data/app/views/hotsheet/sheets/root.html.erb +1 -0
  13. data/app/views/layouts/hotsheet/application.html.erb +7 -7
  14. data/config/routes.rb +6 -5
  15. data/lib/generators/templates/hotsheet.rb +7 -4
  16. data/lib/hotsheet/column.rb +31 -0
  17. data/lib/hotsheet/config.rb +29 -0
  18. data/lib/hotsheet/engine.rb +5 -4
  19. data/lib/hotsheet/sheet.rb +44 -0
  20. data/lib/hotsheet/version.rb +1 -1
  21. data/lib/hotsheet.rb +38 -12
  22. metadata +17 -90
  23. data/CHANGELOG.md +0 -22
  24. data/app/assets/config/hotsheet.js +0 -2
  25. data/app/assets/javascripts/hotsheet/application.js +0 -1
  26. data/app/assets/javascripts/hotsheet/channels/consumer.js +0 -3
  27. data/app/assets/javascripts/hotsheet/channels/inline_edit_channel.js +0 -3
  28. data/app/assets/javascripts/hotsheet/controllers/application.js +0 -8
  29. data/app/assets/javascripts/hotsheet/controllers/editable_attribute_controller.js +0 -39
  30. data/app/assets/javascripts/hotsheet/controllers/flash_controller.js +0 -8
  31. data/app/assets/stylesheets/hotsheet/application.css +0 -153
  32. data/app/channels/application_cable/channel.rb +0 -6
  33. data/app/channels/application_cable/connection.rb +0 -6
  34. data/app/channels/inline_edit_channel.rb +0 -9
  35. data/app/controllers/hotsheet/pages_controller.rb +0 -43
  36. data/app/views/hotsheet/pages/_editable_attribute.html.erb +0 -15
  37. data/app/views/hotsheet/pages/index.html.erb +0 -27
  38. data/app/views/layouts/hotsheet/_flash.html.erb +0 -10
  39. data/app/views/layouts/hotsheet/_sidebar.html.erb +0 -11
  40. data/config/initializers/hotsheet/content_security_policy.rb +0 -14
  41. data/config/initializers/hotsheet/pagy.rb +0 -7
  42. data/config/locales/en.yml +0 -5
  43. data/lib/hotsheet/configuration.rb +0 -59
  44. data/lib/hotsheet/editable_attributes.rb +0 -24
  45. data/vendor/assets/stylesheets/hotsheet/pagy.css +0 -37
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ApplicationCable
4
- class Connection < ActionCable::Connection::Base
5
- end
6
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class InlineEditChannel < ApplicationCable::Channel
4
- STREAM_NAME = "inline_edit"
5
-
6
- def subscribed
7
- stream_from STREAM_NAME
8
- end
9
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hotsheet
4
- class PagesController < ApplicationController
5
- def index
6
- @pagy, @records = pagy model.all if model
7
- end
8
-
9
- def broadcast_edit_intent
10
- ActionCable.server.broadcast InlineEditChannel::STREAM_NAME, {
11
- resource_name: broadcast_params[:resource_name],
12
- resource_id: broadcast_params[:resource_id]
13
- }
14
- end
15
-
16
- def update # rubocop:disable Metrics/AbcSize
17
- record = model.find params[:id]
18
-
19
- if record.update model_params
20
- flash[:success] = t("hotsheet.success", record: model.model_name.human)
21
- else
22
- flash[:alert] = t("hotsheet.error", record: model.model_name.human,
23
- errors: record.errors.full_messages.join(", "))
24
- end
25
-
26
- redirect_to "#{root_path}#{model.table_name}"
27
- end
28
-
29
- private
30
-
31
- def broadcast_params
32
- params.require(:broadcast).permit :resource_name, :resource_id
33
- end
34
-
35
- def model_params
36
- params.require(model.name.underscore).permit(*Hotsheet.editable_attributes_for(model: model))
37
- end
38
-
39
- def model
40
- @model ||= params[:model]&.constantize
41
- end
42
- end
43
- end
@@ -1,15 +0,0 @@
1
- <%# locals: (attribute:, model:, record:) %>
2
-
3
- <%= turbo_frame_tag "#{dom_id record}-#{attribute}" do %>
4
- <%= form_for record, url: "#{root_path}#{model.table_name}/#{record.id}",
5
- html: { 'data-controller': "editable-attribute",
6
- 'data-editable-attribute-broadcast-url-value': broadcast_edit_intent_path,
7
- 'data-editable-attribute-resource-name-value': model.table_name,
8
- 'data-editable-attribute-resource-id-value': record.id,
9
- 'data-editable-attribute-resource-initial-value-value': record.public_send(attribute) } do |f| %>
10
- <%= f.text_field attribute, class: "editable-input", data: {
11
- editable_attribute_target: "attributeFormInput",
12
- action: "keydown.enter->editable-attribute#submitForm blur->editable-attribute#submitForm"
13
- } %>
14
- <% end %>
15
- <% end %>
@@ -1,27 +0,0 @@
1
- <% if @model.nil? %>
2
- <h1>Hotsheet</h1>
3
- <p>Welcome to the hottest sheet around. Select a model from the sidebar to view the spreadsheet.</p>
4
- <% else %>
5
- <h1><%= @model.model_name.human %></h1>
6
- <table>
7
- <thead>
8
- <tr>
9
- <% Hotsheet.editable_attributes_for(model: @model).each do |attribute| %>
10
- <th><%= attribute %></th>
11
- <% end %>
12
- </tr>
13
- </thead>
14
- <tbody>
15
- <% @records.each do |record| %>
16
- <tr>
17
- <% Hotsheet.editable_attributes_for(model: @model).each do |attribute| %>
18
- <td>
19
- <%= render "editable_attribute", model: @model, record: record, attribute: attribute %>
20
- </td>
21
- <% end %>
22
- </tr>
23
- <% end %>
24
- </tbody>
25
- </table>
26
- <%== pagy_nav @pagy %>
27
- <% end %>
@@ -1,10 +0,0 @@
1
- <%# locals: () %>
2
-
3
- <div class='flash-container'>
4
- <% flash.each do |type, msg| %>
5
- <div class='flash <%= type %>' data-controller='flash'>
6
- <span><%= msg %></span>
7
- <button class='btn-close' data-action='click->flash#close'>&times;</button>
8
- </div>
9
- <% end %>
10
- </div>
@@ -1,11 +0,0 @@
1
- <%# locals: () %>
2
-
3
- <aside>
4
- <nav>
5
- <ul>
6
- <% Hotsheet.models.each do |model| %>
7
- <%= link_to model.model_name.human, "#{root_path}#{model.table_name}" %>
8
- <% end %>
9
- </ul>
10
- </nav>
11
- </aside>
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- Rails.application.configure do
4
- config.content_security_policy do |csp|
5
- csp.default_src :none
6
- csp.connect_src :self
7
- csp.img_src :self
8
- csp.script_src :strict_dynamic
9
- csp.style_src :self, :unsafe_inline
10
- end
11
-
12
- config.content_security_policy_nonce_directives = %w[script-src]
13
- config.content_security_policy_nonce_generator = ->(_) { SecureRandom.base64 18 }
14
- end
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pagy"
4
-
5
- # Pagy Variables (https://ddnexus.github.io/pagy/docs/api/pagy#variables)
6
- Pagy::DEFAULT[:limit] = 100
7
- Pagy::DEFAULT.freeze
@@ -1,5 +0,0 @@
1
- en:
2
- home: Home
3
- hotsheet:
4
- success: '%{record} updated successfully'
5
- error: '%{record} update failed: %{errors}'
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hotsheet
4
- class Configuration
5
- include ActiveSupport::Configurable
6
-
7
- config_accessor(:models) { {} }
8
-
9
- def initialize
10
- self.models = {}
11
- end
12
-
13
- def model(name)
14
- model_config = ModelConfig.new(name).tap do |config|
15
- yield(config) if block_given?
16
- Rails.application.config.to_prepare { config.validate! }
17
- end
18
- models[name.to_s] = model_config
19
- end
20
-
21
- class ModelConfig
22
- attr_accessor :included_attributes, :excluded_attributes, :model_name
23
-
24
- def initialize(model_name)
25
- @model_name = model_name
26
- end
27
-
28
- def validate!
29
- return unless ActiveRecord::Base.connection.table_exists?(model_class.table_name)
30
- return if included_attributes.nil? && excluded_attributes.nil?
31
-
32
- ensure_only_one_attribute_set
33
- validate_attribute_existence
34
- end
35
-
36
- private
37
-
38
- def ensure_only_one_attribute_set
39
- return if included_attributes.blank? || excluded_attributes.blank?
40
-
41
- raise "Can only specify either included or excluded attributes for '#{model_name}'"
42
- end
43
-
44
- def validate_attribute_existence
45
- all_attributes = model_class.column_names
46
-
47
- [included_attributes, excluded_attributes].flatten.compact.each do |attr|
48
- unless all_attributes.include?(attr.to_s)
49
- raise "Attribute '#{attr}' doesn't exist on model '#{model_name}'"
50
- end
51
- end
52
- end
53
-
54
- def model_class
55
- model_name.to_s.constantize
56
- end
57
- end
58
- end
59
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hotsheet
4
- class << self
5
- def editable_attributes_for(model:)
6
- @editable_attributes_for ||= {}
7
- @editable_attributes_for[model] ||= fetch_editable_attributes(model)
8
- end
9
-
10
- private
11
-
12
- def fetch_editable_attributes(model)
13
- model_config = Hotsheet.configuration.models[model.to_s]
14
-
15
- if model_config&.included_attributes
16
- model_config.included_attributes.map(&:to_s)
17
- elsif model_config&.excluded_attributes
18
- model.column_names - model_config.excluded_attributes.map(&:to_s)
19
- else
20
- model.column_names
21
- end
22
- end
23
- end
24
- end
@@ -1,37 +0,0 @@
1
- /* https://ddnexus.github.io/pagy/docs/api/stylesheets */
2
-
3
- .pagy {
4
- color: #6b7280;
5
- display: flex;
6
- font-size: .875rem;
7
- font-weight: 600;
8
- margin-top: 1rem;
9
-
10
- a ~ a {
11
- margin-left: .25rem;
12
- }
13
-
14
- a:not(.gap) {
15
- background-color: #e5e7eb;
16
- border-radius: .5rem;
17
- color: inherit;
18
- display: block;
19
- padding: .25rem .75rem;
20
- text-decoration: none;
21
-
22
- &:hover {
23
- background-color: #d1d5db;
24
- }
25
-
26
- &:not([href]) {
27
- background-color: #f3f4f6;
28
- color: #d1d5db;
29
- cursor: default;
30
- }
31
-
32
- &.current {
33
- background-color: #9ca3af;
34
- color: white;
35
- }
36
- }
37
- }