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
@@ -2,9 +2,15 @@ module Super
2
2
  # Configures the host Rails app to work with Super
3
3
  class Engine < ::Rails::Engine
4
4
  initializer "super.assets.precompile" do |app|
5
- if Super::Assets.sprockets_available?
5
+ if Super::Assets::Handler.sprockets_available?
6
6
  app.config.assets.precompile << "config/super_manifest.js"
7
7
  end
8
8
  end
9
+
10
+ config.to_prepare do
11
+ Super::Plugin::Registry.controller.ordered do |klass, method_name|
12
+ Super::ApplicationController.public_send(method_name, klass)
13
+ end
14
+ end
9
15
  end
10
16
  end
@@ -1,17 +1,17 @@
1
1
  module Super
2
- # A container class for all custom errors thrown by this library
2
+ # A container class for all internal errors thrown by this library
3
+ #
4
+ # See also `Super::ClientError`
3
5
  class Error < StandardError
6
+ # Error raised when some configuration is not set
4
7
  class UnconfiguredConfiguration < Error; end
8
+ # Error raised when a configuration is set to a invalid value
5
9
  class InvalidConfiguration < Error; end
6
- class InvalidStyle < Error; end
10
+ # Error raised on problematic plugins, see `Super::Plugin`
7
11
  class InvalidPluginArgument < Error; end
12
+ # Error raised on problematic ActionInquirer settings, see `Super::ActionInquirer`
8
13
  class ActionInquirerError < Error; end
9
-
10
- class ClientError < Error; end
11
- class BadRequest < ClientError; end
12
- class Unauthorized < ClientError; end
13
- class Forbidden < ClientError; end
14
- class NotFound < ClientError; end
15
- class UnprocessableEntity < ClientError; end
14
+ # Error raised when a `Super::Link` couldn't be found
15
+ class LinkNotRegistered < Error; end
16
16
  end
17
17
  end
@@ -0,0 +1,12 @@
1
+ module Super
2
+ class Filter
3
+ def initialize
4
+ @schema_type = Filter::SchemaTypes.new
5
+ @fields = Schema::Fields.new
6
+
7
+ yield(@fields, @schema_type)
8
+ end
9
+
10
+ attr_reader :fields
11
+ end
12
+ end
@@ -0,0 +1,97 @@
1
+ module Super
2
+ class Filter
3
+ class FormObject
4
+ class FilterFormField
5
+ def initialize(humanized_field_name:, field_name:, type:, params:)
6
+ @humanized_field_name = humanized_field_name
7
+ @field_name = field_name
8
+ @field_type = type
9
+ @params = params
10
+ @specified_values =
11
+ type.q
12
+ .map do |query_field_name|
13
+ [
14
+ query_field_name,
15
+ (params || {})[query_field_name],
16
+ ]
17
+ end
18
+ .to_h
19
+
20
+ @specified_values.each do |key, value|
21
+ define_singleton_method(key) { value }
22
+ end
23
+ end
24
+
25
+ attr_reader :humanized_field_name
26
+ attr_reader :field_name
27
+ attr_reader :field_type
28
+ attr_reader :specified_values
29
+
30
+ def op
31
+ (@params || {})[:op]
32
+ end
33
+
34
+ def operators
35
+ @field_type.operators
36
+ .map { |o| [o.name, o.identifier] }
37
+ .to_h
38
+ end
39
+
40
+ def to_partial_path
41
+ @field_type.to_partial_path
42
+ end
43
+ end
44
+
45
+ def initialize(model:, url:, schema:, params:)
46
+ @model = model
47
+ @url = url
48
+ @schema = schema
49
+ @params = params
50
+
51
+ @form_fields = {}
52
+ end
53
+
54
+ def each_field
55
+ @schema.fields.each do |field_name, _field_type|
56
+ yield(form_field_for(field_name))
57
+ end
58
+ end
59
+
60
+ def url
61
+ @url
62
+ end
63
+
64
+ def to_partial_path
65
+ "filter"
66
+ end
67
+
68
+ def to_search_query(relation)
69
+ each_field do |form_field|
70
+ next if form_field.specified_values.values.map(&:to_s).map(&:strip).all? { |specified_value| specified_value == "" }
71
+ next if !Super::Filter::Operator.registry.key?(form_field.op)
72
+
73
+ operator = Super::Filter::Operator.registry[form_field.op]
74
+ updated_relation = operator.filter(relation, form_field.field_name, *form_field.specified_values.values)
75
+
76
+ if updated_relation.is_a?(ActiveRecord::Relation)
77
+ relation = updated_relation
78
+ end
79
+ end
80
+
81
+ relation
82
+ end
83
+
84
+ private
85
+
86
+ def form_field_for(field_name)
87
+ @form_fields[field_name] ||=
88
+ FilterFormField.new(
89
+ humanized_field_name: @model.human_attribute_name(field_name),
90
+ field_name: field_name,
91
+ type: @schema.fields[field_name],
92
+ params: (@params || {})[field_name]
93
+ )
94
+ end
95
+ end
96
+ end
97
+ end
@@ -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