super 0.0.3 → 0.0.8

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING.md +56 -0
  4. data/README.md +65 -76
  5. data/STABILITY.md +50 -0
  6. data/app/assets/javascripts/super/application.js +1186 -11151
  7. data/app/assets/stylesheets/super/application.css +93514 -27381
  8. data/app/controllers/super/application_controller.rb +49 -72
  9. data/app/views/layouts/super/application.html.erb +24 -14
  10. data/app/views/super/application/_collection_header.html.erb +15 -0
  11. data/app/views/super/application/_filter.html.erb +14 -0
  12. data/app/views/super/application/_filter_type_select.html.erb +31 -0
  13. data/app/views/super/application/_filter_type_text.html.erb +22 -0
  14. data/app/views/super/application/_filter_type_timestamp.html.erb +35 -0
  15. data/app/views/super/application/_flash.html.erb +13 -13
  16. data/app/views/super/application/_form_field__destroy.html.erb +7 -3
  17. data/app/views/super/application/_form_field_select.html.erb +14 -8
  18. data/app/views/super/application/_form_field_text.html.erb +13 -5
  19. data/app/views/super/application/_form_fieldset.html.erb +1 -1
  20. data/app/views/super/application/_form_has_many.html.erb +5 -5
  21. data/app/views/super/application/_form_inline_errors.html.erb +1 -1
  22. data/app/views/super/application/_member_header.html.erb +16 -0
  23. data/app/views/super/application/_super_layout.html.erb +29 -0
  24. data/app/views/super/application/_super_pagination.html.erb +16 -0
  25. data/app/views/super/application/_super_panel.html.erb +7 -0
  26. data/app/views/super/application/_super_schema_display_actions.html.erb +5 -0
  27. data/app/views/super/application/_super_schema_display_index.html.erb +24 -0
  28. data/app/views/super/application/_super_schema_display_show.html.erb +8 -0
  29. data/app/views/super/application/_super_schema_form.html.erb +15 -0
  30. data/app/views/super/application/edit.html.erb +1 -6
  31. data/app/views/super/application/index.html.erb +1 -1
  32. data/app/views/super/application/new.html.erb +1 -6
  33. data/app/views/super/application/show.html.erb +1 -1
  34. data/app/views/super/feather/{_chevron_down.svg → _chevron_down.html} +0 -0
  35. data/config/locales/en.yml +5 -0
  36. data/docs/README.md +6 -0
  37. data/docs/cheat.md +41 -0
  38. data/docs/faq.md +44 -0
  39. data/docs/quick_start.md +45 -0
  40. data/docs/webpacker.md +17 -0
  41. data/docs/yard_customizations.rb +41 -0
  42. data/frontend/super-frontend/build.js +12 -12
  43. data/frontend/super-frontend/dist/application.css +93514 -27381
  44. data/frontend/super-frontend/dist/application.js +1186 -11151
  45. data/frontend/super-frontend/package.json +8 -4
  46. data/frontend/super-frontend/postcss.config.js +4 -4
  47. data/frontend/super-frontend/src/javascripts/super/application.ts +11 -9
  48. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.ts +19 -0
  49. data/frontend/super-frontend/src/javascripts/super/rails__ujs.d.ts +1 -1
  50. data/frontend/super-frontend/src/javascripts/super/toggle_pending_destruction_controller.ts +15 -0
  51. data/frontend/super-frontend/src/stylesheets/super/application.css +63 -0
  52. data/frontend/super-frontend/tailwind.config.js +12 -4
  53. data/frontend/super-frontend/yarn.lock +1429 -1372
  54. data/lib/generators/super/install/install_generator.rb +16 -0
  55. data/lib/generators/super/resource/templates/resources_controller.rb.tt +1 -31
  56. data/lib/generators/super/webpacker/webpacker_generator.rb +10 -1
  57. data/lib/super.rb +26 -5
  58. data/lib/super/action_inquirer.rb +2 -2
  59. data/lib/super/assets.rb +108 -38
  60. data/lib/super/client_error.rb +43 -0
  61. data/lib/super/compatibility.rb +25 -0
  62. data/lib/super/configuration.rb +21 -69
  63. data/lib/super/controls.rb +9 -37
  64. data/lib/super/controls/optional.rb +79 -0
  65. data/lib/super/controls/required.rb +13 -0
  66. data/lib/super/controls/steps.rb +114 -0
  67. data/lib/super/display.rb +66 -3
  68. data/lib/super/display/guesser.rb +34 -0
  69. data/lib/super/display/schema_types.rb +55 -25
  70. data/lib/super/engine.rb +7 -1
  71. data/lib/super/error.rb +9 -9
  72. data/lib/super/filter.rb +12 -0
  73. data/lib/super/filter/form_object.rb +97 -0
  74. data/lib/super/filter/guesser.rb +30 -0
  75. data/lib/super/filter/operator.rb +103 -0
  76. data/lib/super/filter/plugin.rb +47 -0
  77. data/lib/super/filter/schema_types.rb +112 -0
  78. data/lib/super/form.rb +35 -0
  79. data/lib/super/form/builder.rb +48 -0
  80. data/lib/super/form/guesser.rb +27 -0
  81. data/lib/super/form/schema_types.rb +20 -23
  82. data/lib/super/form/strong_params.rb +29 -0
  83. data/lib/super/layout.rb +47 -0
  84. data/lib/super/link.rb +110 -0
  85. data/lib/super/pagination.rb +74 -8
  86. data/lib/super/panel.rb +30 -0
  87. data/lib/super/partial.rb +23 -0
  88. data/lib/super/partial/resolving.rb +24 -0
  89. data/lib/super/plugin.rb +34 -63
  90. data/lib/super/schema.rb +12 -22
  91. data/lib/super/schema/common.rb +25 -0
  92. data/lib/super/schema/guesser.rb +77 -0
  93. data/lib/super/version.rb +1 -1
  94. data/lib/super/view_helper.rb +43 -0
  95. metadata +133 -33
  96. data/app/views/super/application/_form.html.erb +0 -14
  97. data/app/views/super/application/_index.html.erb +0 -60
  98. data/app/views/super/application/_show.html.erb +0 -12
  99. data/frontend/super-frontend/src/javascripts/super/nested_attributes_controller.ts +0 -33
  100. data/lib/super/inline_callback.rb +0 -82
  101. data/lib/super/test_support/copy_app_templates/controllers/favorite_things_controller.rb +0 -50
  102. data/lib/super/test_support/copy_app_templates/controllers/members_controller.rb +0 -57
  103. data/lib/super/test_support/copy_app_templates/controllers/ships_controller.rb +0 -47
  104. data/lib/super/test_support/copy_app_templates/migrations/20190216224956_create_members.rb +0 -11
  105. data/lib/super/test_support/copy_app_templates/migrations/20190803143320_create_ships.rb +0 -11
  106. data/lib/super/test_support/copy_app_templates/migrations/20190806014121_add_ship_to_members.rb +0 -5
  107. data/lib/super/test_support/copy_app_templates/migrations/20191126050453_create_favorite_things.rb +0 -10
  108. data/lib/super/test_support/copy_app_templates/models/favorite_thing.rb +0 -7
  109. data/lib/super/test_support/copy_app_templates/models/member.rb +0 -23
  110. data/lib/super/test_support/copy_app_templates/models/ship.rb +0 -3
  111. data/lib/super/test_support/copy_app_templates/routes.rb +0 -11
  112. data/lib/super/test_support/copy_app_templates/seeds.rb +0 -2
  113. data/lib/super/test_support/fixtures/favorite_things.yml +0 -9
  114. data/lib/super/test_support/fixtures/members.yml +0 -336
  115. data/lib/super/test_support/fixtures/ships.yml +0 -10
  116. data/lib/super/test_support/generate_copy_app.rb +0 -41
  117. data/lib/super/test_support/generate_dummy.rb +0 -93
  118. data/lib/super/test_support/starfleet_seeder.rb +0 -50
  119. data/lib/super/view.rb +0 -25
  120. data/lib/tasks/super_tasks.rake +0 -4
@@ -0,0 +1,35 @@
1
+ module Super
2
+ # This schema type is used on your +#edit+ and +#new+ forms
3
+ #
4
+ # ```ruby
5
+ # class MembersController::Controls
6
+ # # ...
7
+ #
8
+ # def new_schema
9
+ # Super::Form.new do |fields, type|
10
+ # fields[:name] = type.string
11
+ # fields[:rank] = type.select
12
+ # fields[:position] = type.string
13
+ # fields[:ship_id] = type.select(
14
+ # collection: Ship.all.map { |s| ["#{s.name} (Ship ##{s.id})", s.id] },
15
+ # )
16
+ # end
17
+ # end
18
+ #
19
+ # # ...
20
+ # end
21
+ # ```
22
+ class Form
23
+ include Schema::Common
24
+
25
+ def initialize
26
+ @fields = Schema::Fields.new
27
+ @schema_types = SchemaTypes.new(fields: @fields)
28
+ yield(@fields, @schema_types)
29
+ end
30
+
31
+ def to_partial_path
32
+ "super_schema_form"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,48 @@
1
+ module Super
2
+ class Form
3
+ module FieldErrorProc
4
+ def error_wrapping(html_tag)
5
+ if Thread.current[:super_form_builder]
6
+ return html_tag
7
+ end
8
+
9
+ super
10
+ end
11
+ end
12
+
13
+ class Builder < ActionView::Helpers::FormBuilder
14
+ # These methods were originally defined in the following files
15
+ #
16
+ # * actionview/lib/action_view/helpers/form_helper.rb
17
+ # * actionview/lib/action_view/helpers/form_options_helper.rb
18
+ # * actionview/lib/action_view/helpers/date_helper.rb
19
+ %w[
20
+ label text_field password_field hidden_field file_field text_area
21
+ check_box radio_button color_field search_field telephone_field
22
+ date_field time_field datetime_field month_field week_field url_field
23
+ email_field number_field range_field
24
+
25
+ select collection_select grouped_collection_select time_zone_select
26
+ collection_radio_buttons collection_check_boxes
27
+
28
+ time_select datetime_select date_select
29
+ ].each do |field_type_method|
30
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
31
+ def #{field_type_method}(*)
32
+ Thread.current[:super_form_builder] = true
33
+ super
34
+ ensure
35
+ Thread.current[:super_form_builder] = nil
36
+ end
37
+ RUBY
38
+ end
39
+
40
+ alias datetime_local_field datetime_field
41
+ alias phone_field telephone_field
42
+ end
43
+ end
44
+ end
45
+
46
+ ActionView::Helpers::Tags::Base.class_eval do
47
+ prepend Super::Form::FieldErrorProc
48
+ end
@@ -0,0 +1,27 @@
1
+ module Super
2
+ class Form
3
+ class Guesser
4
+ def initialize(model:, fields:, type:)
5
+ @model = model
6
+ @fields = fields
7
+ @type = type
8
+ end
9
+
10
+ def call
11
+ Schema::Guesser
12
+ .new(model: @model, fields: @fields, type: @type)
13
+ .assign_type { |attribute_name| attribute_type_for(attribute_name) }
14
+ .reject { |attribute_name| attribute_name == "id" }
15
+ .reject { |attribute_name| attribute_name == "created_at" }
16
+ .reject { |attribute_name| attribute_name == "updated_at" }
17
+ .call
18
+ end
19
+
20
+ private
21
+
22
+ def attribute_type_for(attribute_name)
23
+ @type.string
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,26 +1,5 @@
1
1
  module Super
2
2
  class Form
3
- # This schema type is used on your +#edit+ and +#new+ forms
4
- #
5
- # ```ruby
6
- # class MembersController::Controls
7
- # # ...
8
- #
9
- # def new_schema
10
- # Super::Schema.new(Super::Form::SchemaTypes.new) do |fields, type|
11
- # fields[:name] = type.generic("form_field_text")
12
- # fields[:rank] = type.generic("form_field_select", collection: Member.ranks.keys)
13
- # fields[:position] = type.generic("form_field_text")
14
- # fields[:ship_id] = type.generic(
15
- # "form_field_select",
16
- # collection: Ship.all.map { |s| ["#{s.name} (Ship ##{s.id})", s.id] },
17
- # )
18
- # end
19
- # end
20
- #
21
- # # ...
22
- # end
23
- # ```
24
3
  class SchemaTypes
25
4
  class Generic
26
5
  def initialize(partial_path:, extras:, nested:)
@@ -31,6 +10,16 @@ module Super
31
10
 
32
11
  attr_reader :nested_fields
33
12
 
13
+ def each_attribute
14
+ if block_given?
15
+ @nested_fields.each do |key, value|
16
+ yield(key, value)
17
+ end
18
+ end
19
+
20
+ enum_for(:each_attribute)
21
+ end
22
+
34
23
  # This takes advantage of a feature of Rails. If the value of
35
24
  # `#to_partial_path` is `my_form_field`, Rails renders
36
25
  # `app/views/super/application/_my_form_field.html.erb`, and this
@@ -63,13 +52,13 @@ module Super
63
52
  return false if other.class != self.class
64
53
  return false if other.instance_variable_get(:@partial_path) != @partial_path
65
54
  return false if other.instance_variable_get(:@extras) != @extras
66
- return false if other.instance_variable_get(:@nested) != @nested
55
+ return false if other.instance_variable_get(:@nested_fields) != @nested_fields
67
56
 
68
57
  true
69
58
  end
70
59
  end
71
60
 
72
- def setup(fields:)
61
+ def initialize(fields:)
73
62
  @fields = fields
74
63
  end
75
64
 
@@ -77,6 +66,14 @@ module Super
77
66
  Generic.new(partial_path: partial_path, extras: extras, nested: {})
78
67
  end
79
68
 
69
+ def select(**extras)
70
+ Generic.new(partial_path: "form_field_select", extras: extras, nested: {})
71
+ end
72
+
73
+ def string(**extras)
74
+ Generic.new(partial_path: "form_field_text", extras: extras, nested: {})
75
+ end
76
+
80
77
  def has_many(reader, **extras)
81
78
  nested = @fields.nested do
82
79
  yield
@@ -0,0 +1,29 @@
1
+ module Super
2
+ class Form
3
+ class StrongParams
4
+ def initialize(form_schema)
5
+ @form_schema = form_schema
6
+ end
7
+
8
+ def require(model)
9
+ model.model_name.singular
10
+ end
11
+
12
+ def permit
13
+ unfurl(@form_schema)
14
+ end
15
+
16
+ private
17
+
18
+ def unfurl(responds_to_each_attribute)
19
+ responds_to_each_attribute.each_attribute.map do |name, type|
20
+ if type.nested_fields&.any?
21
+ { name => [:id, *unfurl(type)] }
22
+ else
23
+ name
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ require "super/partial/resolving"
2
+
3
+ module Super
4
+ class Layout
5
+ include Super::Partial::Resolving
6
+
7
+ def initialize(headers: nil, asides: nil, mains: nil, footers: nil)
8
+ @headers = Array(headers).compact
9
+ @asides = Array(asides).compact
10
+ @mains = Array(mains).compact
11
+ @footers = Array(footers).compact
12
+ end
13
+
14
+ attr_reader :headers
15
+ attr_reader :asides
16
+ attr_reader :mains
17
+ attr_reader :footers
18
+
19
+ def to_partial_path
20
+ "super_layout"
21
+ end
22
+
23
+ def resolve(template)
24
+ @resolved_headers = resolve_for_rendering(template, headers, nil)
25
+ @resolved_asides = resolve_for_rendering(template, asides, nil)
26
+ @resolved_mains = resolve_for_rendering(template, mains, nil)
27
+ @resolved_footers = resolve_for_rendering(template, footers, nil)
28
+ self
29
+ end
30
+
31
+ def resolved_headers
32
+ @resolved_headers || []
33
+ end
34
+
35
+ def resolved_asides
36
+ @resolved_asides || []
37
+ end
38
+
39
+ def resolved_mains
40
+ @resolved_mains || []
41
+ end
42
+
43
+ def resolved_footers
44
+ @resolved_footers || []
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,110 @@
1
+ module Super
2
+ # Links have three required attributes that are passed directly into Rails'
3
+ # `link_to` helper
4
+ class Link
5
+ def self.find_all(*links)
6
+ links.map { |link| find(link) }
7
+ end
8
+
9
+ def self.find(link)
10
+ if link.kind_of?(self)
11
+ return link
12
+ end
13
+
14
+ if registry.key?(link)
15
+ return registry[link]
16
+ end
17
+
18
+ raise Error::LinkNotRegistered, "Unknown link `#{link}`"
19
+ end
20
+
21
+ def self.registry
22
+ @registry ||= {
23
+ new: new(
24
+ "New",
25
+ -> (params:) {
26
+ Rails.application.routes.url_for(
27
+ controller: params[:controller],
28
+ action: :new,
29
+ only_path: true
30
+ )
31
+ }
32
+ ),
33
+ index: new(
34
+ "Index",
35
+ -> (params:) {
36
+ Rails.application.routes.url_for(
37
+ controller: params[:controller],
38
+ action: :index,
39
+ only_path: true
40
+ )
41
+ }
42
+ ),
43
+ show: new(
44
+ "View",
45
+ -> (record:, params:) {
46
+ Rails.application.routes.url_for(
47
+ controller: params[:controller],
48
+ action: :show,
49
+ id: record,
50
+ only_path: true
51
+ )
52
+ }
53
+ ),
54
+ edit: new(
55
+ "Edit",
56
+ -> (record:, params:) {
57
+ Rails.application.routes.url_for(
58
+ controller: params[:controller],
59
+ action: :edit,
60
+ id: record,
61
+ only_path: true
62
+ )
63
+ }
64
+ ),
65
+ destroy: new(
66
+ "Delete",
67
+ -> (record:, params:) {
68
+ Rails.application.routes.url_for(
69
+ controller: params[:controller],
70
+ action: :destroy,
71
+ id: record,
72
+ only_path: true
73
+ )
74
+ },
75
+ method: :delete,
76
+ data: { confirm: "Really delete?" }
77
+ ),
78
+ }
79
+ end
80
+
81
+ def initialize(text, href, **options)
82
+ @text = text
83
+ @href = href
84
+ @options = options
85
+ end
86
+
87
+ def to_s(default_options: nil, **proc_arguments)
88
+ default_options ||= {}
89
+ ActionController::Base.helpers.link_to(
90
+ value(text, proc_arguments),
91
+ value(href, proc_arguments),
92
+ default_options.deep_merge(value(options, proc_arguments))
93
+ )
94
+ end
95
+
96
+ private
97
+
98
+ attr_reader :text
99
+ attr_reader :href
100
+ attr_reader :options
101
+
102
+ def value(proc_or_value, proc_arguments)
103
+ if proc_or_value.kind_of?(Proc)
104
+ proc_or_value.call(**proc_arguments)
105
+ else
106
+ proc_or_value
107
+ end
108
+ end
109
+ end
110
+ end
@@ -29,19 +29,15 @@ module Super
29
29
  end
30
30
  end
31
31
 
32
+ def necessary?
33
+ pages > 1
34
+ end
35
+
32
36
  def each
33
37
  if !block_given?
34
38
  return enum_for(:each)
35
39
  end
36
40
 
37
- quotient, remainder = @total_count.divmod(@limit)
38
- pages =
39
- if remainder.zero?
40
- quotient
41
- else
42
- quotient + 1
43
- end
44
-
45
41
  (1..pages).each do |pageno|
46
42
  is_current_page = pageno == current_pageno
47
43
  display = pageno.to_s
@@ -55,5 +51,75 @@ module Super
55
51
  yield(page_query_params, is_current_page, display)
56
52
  end
57
53
  end
54
+
55
+ def to_partial_path
56
+ "super_pagination"
57
+ end
58
+
59
+ private
60
+
61
+ def pages
62
+ @pages ||=
63
+ begin
64
+ quotient, remainder = @total_count.divmod(@limit)
65
+
66
+ if remainder.zero?
67
+ quotient
68
+ else
69
+ quotient + 1
70
+ end
71
+ end
72
+ end
73
+
74
+ module ControllerMethods
75
+ def index
76
+ super
77
+ @pagination = controls.initialize_pagination(action: action_inquirer, records: @records, query_params: request.GET)
78
+ @records = controls.paginate_records(action: action_inquirer, records: @records, pagination: @pagination)
79
+ @view.mains.first.parts.push(:@pagination)
80
+ end
81
+ end
82
+ end
83
+
84
+ class Controls
85
+ module Optional
86
+ # Specifies how many records to show per page
87
+ #
88
+ # @param action [ActionInquirer]
89
+ # @param query_params [Hash]
90
+ # @return [ActiveRecord::Relation]
91
+ def records_per_page(action:, query_params:)
92
+ Super.configuration.index_records_per_page
93
+ end
94
+ end
95
+
96
+ module Steps
97
+ # Sets up pagination
98
+ #
99
+ # @param action [ActionInquirer]
100
+ # @param records [ActiveRecord::Relation]
101
+ # @param query_params [Hash]
102
+ # @return [Pagination]
103
+ def initialize_pagination(action:, records:, query_params:)
104
+ Pagination.new(
105
+ total_count: records.size,
106
+ limit: records_per_page(action: action, query_params: query_params),
107
+ query_params: query_params,
108
+ page_query_param: :page
109
+ )
110
+ end
111
+
112
+ # Paginates
113
+ #
114
+ # @param action [ActionInquirer]
115
+ # @param records [ActiveRecord::Relation]
116
+ # @param pagination [Pagination]
117
+ # @return [ActiveRecord::Relation]
118
+ def paginate_records(action:, records:, pagination:)
119
+ records
120
+ .limit(pagination.limit)
121
+ .offset(pagination.offset)
122
+ end
123
+ end
58
124
  end
59
125
  end