super 0.0.4 → 0.0.9

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +13 -0
  3. data/CONTRIBUTING.md +56 -0
  4. data/README.md +60 -85
  5. data/STABILITY.md +50 -0
  6. data/app/assets/javascripts/super/application.js +1169 -359
  7. data/app/assets/stylesheets/super/application.css +86648 -30707
  8. data/app/controllers/super/application_controller.rb +44 -71
  9. data/app/views/layouts/super/application.html.erb +26 -6
  10. data/app/views/super/application/{_resources_header.html.erb → _collection_header.html.erb} +5 -6
  11. data/app/views/super/application/_display_rich_text.html.erb +1 -0
  12. data/app/views/super/application/_filter.html.erb +14 -0
  13. data/app/views/super/application/_filter_type_select.html.erb +31 -0
  14. data/app/views/super/application/_filter_type_text.html.erb +22 -0
  15. data/app/views/super/application/_filter_type_timestamp.html.erb +35 -0
  16. data/app/views/super/application/_flash.html.erb +13 -13
  17. data/app/views/super/application/_form_field__destroy.html.erb +6 -2
  18. data/app/views/super/application/_form_field_checkbox.html.erb +15 -0
  19. data/app/views/super/application/_form_field_generic.html.erb +19 -0
  20. data/app/views/super/application/_form_field_rich_text_area.html.erb +13 -0
  21. data/app/views/super/application/_form_field_select.html.erb +11 -5
  22. data/app/views/super/application/_form_field_text.html.erb +13 -5
  23. data/app/views/super/application/_form_has_many.html.erb +3 -3
  24. data/app/views/super/application/_form_inline_errors.html.erb +1 -1
  25. data/app/views/super/application/{_resource_header.html.erb → _member_header.html.erb} +6 -6
  26. data/app/views/super/application/_super_layout.html.erb +12 -17
  27. data/app/views/super/application/_super_pagination.html.erb +16 -0
  28. data/app/views/super/application/_super_panel.html.erb +3 -7
  29. data/app/views/super/application/_super_schema_display_actions.html.erb +5 -0
  30. data/app/views/super/application/_super_schema_display_index.html.erb +24 -0
  31. data/app/views/super/application/_super_schema_display_show.html.erb +8 -0
  32. data/app/views/super/application/{_form.html.erb → _super_schema_form.html.erb} +2 -4
  33. data/app/views/super/application/edit.html.erb +1 -1
  34. data/app/views/super/application/index.html.erb +1 -1
  35. data/app/views/super/application/new.html.erb +1 -1
  36. data/app/views/super/application/show.html.erb +1 -1
  37. data/app/views/super/feather/{_chevron_down.svg → _chevron_down.html} +0 -0
  38. data/config/locales/en.yml +5 -0
  39. data/docs/README.md +8 -0
  40. data/docs/action_text.md +48 -0
  41. data/docs/cheat.md +41 -0
  42. data/docs/faq.md +44 -0
  43. data/docs/installation.md +21 -0
  44. data/docs/quick_start.md +30 -0
  45. data/docs/webpacker.md +25 -0
  46. data/docs/yard_customizations.rb +41 -0
  47. data/frontend/super-frontend/build.js +1 -1
  48. data/frontend/super-frontend/dist/application.css +86648 -30707
  49. data/frontend/super-frontend/dist/application.js +1169 -359
  50. data/frontend/super-frontend/package.json +2 -3
  51. data/frontend/super-frontend/src/javascripts/super/{application.ts → application.js} +5 -8
  52. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.js +17 -0
  53. data/frontend/super-frontend/src/javascripts/super/{toggle_pending_destruction_controller.ts → toggle_pending_destruction_controller.js} +2 -2
  54. data/frontend/super-frontend/tailwind.config.js +7 -1
  55. data/frontend/super-frontend/yarn.lock +1368 -1391
  56. data/lib/generators/super/action_text/USAGE +23 -0
  57. data/lib/generators/super/action_text/action_text_generator.rb +30 -0
  58. data/lib/generators/super/action_text/templates/pack_super_action_text.css +23 -0
  59. data/lib/generators/super/action_text/templates/pack_super_action_text.js +4 -0
  60. data/lib/generators/super/install/install_generator.rb +16 -0
  61. data/lib/generators/super/resource/templates/resources_controller.rb.tt +1 -31
  62. data/lib/generators/super/webpacker/USAGE +5 -4
  63. data/lib/generators/super/webpacker/webpacker_generator.rb +10 -1
  64. data/lib/super.rb +22 -5
  65. data/lib/super/action_inquirer.rb +2 -2
  66. data/lib/super/assets.rb +112 -38
  67. data/lib/super/client_error.rb +43 -0
  68. data/lib/super/compatibility.rb +25 -0
  69. data/lib/super/configuration.rb +21 -69
  70. data/lib/super/controls.rb +9 -257
  71. data/lib/super/controls/optional.rb +79 -0
  72. data/lib/super/controls/required.rb +13 -0
  73. data/lib/super/controls/steps.rb +114 -0
  74. data/lib/super/display.rb +66 -3
  75. data/lib/super/display/guesser.rb +34 -0
  76. data/lib/super/display/schema_types.rb +61 -25
  77. data/lib/super/engine.rb +7 -1
  78. data/lib/super/error.rb +8 -9
  79. data/lib/super/filter.rb +12 -0
  80. data/lib/super/filter/form_object.rb +97 -0
  81. data/lib/super/filter/guesser.rb +30 -0
  82. data/lib/super/filter/operator.rb +103 -0
  83. data/lib/super/filter/plugin.rb +47 -0
  84. data/lib/super/filter/schema_types.rb +112 -0
  85. data/lib/super/form.rb +35 -0
  86. data/lib/super/form/builder.rb +48 -0
  87. data/lib/super/form/guesser.rb +27 -0
  88. data/lib/super/form/schema_types.rb +29 -22
  89. data/lib/super/form/strong_params.rb +29 -0
  90. data/lib/super/layout.rb +28 -0
  91. data/lib/super/link.rb +55 -32
  92. data/lib/super/pagination.rb +55 -0
  93. data/lib/super/panel.rb +13 -0
  94. data/lib/super/partial.rb +12 -0
  95. data/lib/super/partial/resolving.rb +24 -0
  96. data/lib/super/plugin.rb +34 -63
  97. data/lib/super/schema.rb +12 -22
  98. data/lib/super/schema/common.rb +25 -0
  99. data/lib/super/schema/guesser.rb +77 -0
  100. data/lib/super/version.rb +1 -1
  101. data/lib/super/view_helper.rb +43 -0
  102. metadata +138 -41
  103. data/app/helpers/super/application_helper.rb +0 -32
  104. data/app/views/super/application/_index.html.erb +0 -45
  105. data/app/views/super/application/_show.html.erb +0 -10
  106. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.ts +0 -21
  107. data/frontend/super-frontend/src/javascripts/super/rails__ujs.d.ts +0 -1
  108. data/frontend/super-frontend/tsconfig.json +0 -13
  109. data/lib/super/action.rb +0 -22
  110. data/lib/super/action/step.rb +0 -36
  111. data/lib/super/test_support/copy_app_templates/controllers/favorite_things_controller.rb +0 -50
  112. data/lib/super/test_support/copy_app_templates/controllers/members_controller.rb +0 -74
  113. data/lib/super/test_support/copy_app_templates/controllers/ships_controller.rb +0 -47
  114. data/lib/super/test_support/copy_app_templates/migrations/20190216224956_create_members.rb +0 -11
  115. data/lib/super/test_support/copy_app_templates/migrations/20190803143320_create_ships.rb +0 -11
  116. data/lib/super/test_support/copy_app_templates/migrations/20190806014121_add_ship_to_members.rb +0 -5
  117. data/lib/super/test_support/copy_app_templates/migrations/20191126050453_create_favorite_things.rb +0 -10
  118. data/lib/super/test_support/copy_app_templates/models/favorite_thing.rb +0 -7
  119. data/lib/super/test_support/copy_app_templates/models/member.rb +0 -23
  120. data/lib/super/test_support/copy_app_templates/models/ship.rb +0 -3
  121. data/lib/super/test_support/copy_app_templates/routes.rb +0 -11
  122. data/lib/super/test_support/copy_app_templates/seeds.rb +0 -2
  123. data/lib/super/test_support/copy_app_templates/views/members/_favorite_things.html.erb +0 -11
  124. data/lib/super/test_support/fixtures/favorite_things.yml +0 -9
  125. data/lib/super/test_support/fixtures/members.yml +0 -336
  126. data/lib/super/test_support/fixtures/ships.yml +0 -10
  127. data/lib/super/test_support/generate_copy_app.rb +0 -42
  128. data/lib/super/test_support/generate_dummy.rb +0 -93
  129. data/lib/super/test_support/starfleet_seeder.rb +0 -50
  130. data/lib/tasks/super_tasks.rake +0 -4
@@ -0,0 +1,30 @@
1
+ module Super
2
+ class Filter
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
+ .call
15
+ end
16
+
17
+ private
18
+
19
+ def attribute_type_for(attribute_name)
20
+ type = @model.type_for_attribute(attribute_name).type
21
+ case type
22
+ when :datetime
23
+ @type.timestamp
24
+ else
25
+ @type.text
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,103 @@
1
+ module Super
2
+ class Filter
3
+ module Operator
4
+ class Definition
5
+ def initialize(identifier, name, filter)
6
+ @identifier = identifier
7
+ @name = name
8
+ @filter = filter
9
+ end
10
+
11
+ attr_reader :identifier
12
+ attr_reader :name
13
+
14
+ def filter(*args)
15
+ @filter.call(args)
16
+ end
17
+ end
18
+
19
+ class << self
20
+ def registry
21
+ @registry ||= {}
22
+ end
23
+
24
+ def range_defaults
25
+ [
26
+ registry["between"],
27
+ ]
28
+ end
29
+
30
+ def select_defaults
31
+ [
32
+ registry["eq"],
33
+ registry["neq"],
34
+ ]
35
+ end
36
+
37
+ def text_defaults
38
+ [
39
+ registry["eq"],
40
+ registry["neq"],
41
+ registry["contain"],
42
+ registry["ncontain"],
43
+ registry["start"],
44
+ registry["end"],
45
+ ]
46
+ end
47
+
48
+ def define(identifier, name, &filter)
49
+ identifier = identifier.to_s
50
+ name = name.to_s
51
+
52
+ definition = Definition.new(identifier, name, filter)
53
+
54
+ registry[identifier] = definition
55
+
56
+ define_singleton_method(identifier) do
57
+ registry[identifier]
58
+ end
59
+ end
60
+ end
61
+
62
+ define("eq", "equals") do |relation, field, query|
63
+ relation.where(field => query)
64
+ end
65
+
66
+ define("neq", "doesn't equal") do |relation, field, query|
67
+ relation.where.not(field => query)
68
+ end
69
+
70
+ define("contain", "contains") do |relation, field, query|
71
+ query = "%#{Compatability.sanitize_sql_like(query)}%"
72
+ relation.where("#{field} LIKE ?", "%#{query}%")
73
+ end
74
+
75
+ define("ncontain", "doesn't contain") do |relation, field, query|
76
+ query = "%#{Compatability.sanitize_sql_like(query)}%"
77
+ relation.where("#{field} NOT LIKE ?", query)
78
+ end
79
+
80
+ define("start", "starts with") do |relation, field, query|
81
+ query = "#{Compatability.sanitize_sql_like(query)}%"
82
+ relation.where("#{field} LIKE ?", query)
83
+ end
84
+
85
+ define("end", "ends with") do |relation, field, query|
86
+ query = "%#{Compatability.sanitize_sql_like(query)}"
87
+ relation.where("#{field} LIKE ?", query)
88
+ end
89
+
90
+ define("between", "between") do |relation, field, query0, query1|
91
+ if query0.present?
92
+ relation = relation.where("#{field} >= ?", query0)
93
+ end
94
+
95
+ if query1.present?
96
+ relation = relation.where("#{field} <= ?", query1)
97
+ end
98
+
99
+ relation
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,47 @@
1
+ module Super
2
+ class Filter
3
+ module ControllerMethods
4
+ def index
5
+ super
6
+ @filter_form = controls.initialize_filtering(params: params, query_params: request.GET)
7
+ @records = controls.filter_records(filter_form: @filter_form, records: @records)
8
+ @view.asides.push(:@filter_form)
9
+ end
10
+ end
11
+ end
12
+
13
+ class Controls
14
+ module Optional
15
+ def filters_enabled?
16
+ true
17
+ end
18
+
19
+ def filter_schema
20
+ Filter.new do |fields, type|
21
+ Filter::Guesser.new(model: model, fields: fields, type: type).call
22
+ end
23
+ end
24
+ end
25
+
26
+ module Steps
27
+ def initialize_filtering(params:, query_params:)
28
+ if filters_enabled?
29
+ Super::Filter::FormObject.new(
30
+ model: model,
31
+ schema: filter_schema,
32
+ params: params[:q],
33
+ url: query_params
34
+ )
35
+ end
36
+ end
37
+
38
+ def filter_records(filter_form:, records:)
39
+ if filters_enabled? && records
40
+ filter_form.to_search_query(records)
41
+ else
42
+ records
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,112 @@
1
+ module Super
2
+ class Filter
3
+ # This schema type is used to configure the filtering form on your +#index+
4
+ # action.
5
+ #
6
+ # The +operators:+ keyword argument can be left out in each case. There is
7
+ # a default set of operators that are provided.
8
+ #
9
+ # Note: The constants under "Defined Under Namespace" are considered
10
+ # private.
11
+ #
12
+ # class MemberDashboard
13
+ # # ...
14
+ #
15
+ # def filter_schema
16
+ # Super::Filter.new do |fields, type|
17
+ # fields[:name] = type.text(operators: [
18
+ # Super::Filter::Operator.eq,
19
+ # Super::Filter::Operator.contain,
20
+ # Super::Filter::Operator.ncontain,
21
+ # Super::Filter::Operator.start,
22
+ # Super::Filter::Operator.end,
23
+ # ])
24
+ # fields[:rank] = type.select(collection: Member.ranks.values)
25
+ # fields[:position] = type.text(operators: [
26
+ # Super::Filter::Operator.eq,
27
+ # Super::Filter::Operator.neq,
28
+ # Super::Filter::Operator.contain,
29
+ # Super::Filter::Operator.ncontain,
30
+ # ])
31
+ # fields[:ship_id] = type.select(
32
+ # collection: Ship.all.map { |s| ["#{s.name} (Ship ##{s.id})", s.id] },
33
+ # )
34
+ # fields[:created_at] = type.timestamp
35
+ # fields[:updated_at] = type.timestamp
36
+ # end
37
+ # end
38
+ #
39
+ # # ...
40
+ # end
41
+ class SchemaTypes
42
+ class Text
43
+ def initialize(partial_path:, operators:)
44
+ @partial_path = partial_path
45
+ @operators = operators
46
+ end
47
+
48
+ attr_reader :operators
49
+
50
+ def to_partial_path
51
+ @partial_path
52
+ end
53
+
54
+ def q
55
+ [:q]
56
+ end
57
+ end
58
+
59
+ class Select
60
+ def initialize(collection:, operators:)
61
+ @collection = collection
62
+ @operators = operators
63
+ end
64
+
65
+ attr_reader :collection
66
+ attr_reader :operators
67
+
68
+ def to_partial_path
69
+ "filter_type_select"
70
+ end
71
+
72
+ def q
73
+ [:q]
74
+ end
75
+ end
76
+
77
+ class Timestamp
78
+ def initialize(operators:)
79
+ @operators = operators
80
+ end
81
+
82
+ attr_reader :operators
83
+
84
+ def to_partial_path
85
+ "filter_type_timestamp"
86
+ end
87
+
88
+ def q
89
+ [:q0, :q1]
90
+ end
91
+ end
92
+
93
+ def select(collection:, operators: Filter::Operator.select_defaults)
94
+ Select.new(
95
+ collection: collection,
96
+ operators: operators
97
+ )
98
+ end
99
+
100
+ def text(operators: Filter::Operator.text_defaults)
101
+ Text.new(
102
+ partial_path: "filter_type_text",
103
+ operators: operators
104
+ )
105
+ end
106
+
107
+ def timestamp(operators: Filter::Operator.range_defaults)
108
+ Timestamp.new(operators: operators)
109
+ end
110
+ end
111
+ end
112
+ end
@@ -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
@@ -69,7 +58,7 @@ module Super
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,24 @@ 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
+
77
+ alias text string
78
+
79
+ def rich_text_area(**extras)
80
+ Generic.new(partial_path: "form_field_rich_text_area", extras: extras, nested: {})
81
+ end
82
+
83
+ def checkbox(**extras)
84
+ Generic.new(partial_path: "form_field_checkbox", extras: extras, nested: {})
85
+ end
86
+
80
87
  def has_many(reader, **extras)
81
88
  nested = @fields.nested do
82
89
  yield