super 0.0.5 → 0.0.10

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -1
  3. data/CONTRIBUTING.md +56 -0
  4. data/README.md +68 -38
  5. data/STABILITY.md +50 -0
  6. data/app/assets/javascripts/super/application.js +1170 -359
  7. data/app/assets/stylesheets/super/application.css +78105 -50441
  8. data/app/controllers/super/application_controller.rb +44 -52
  9. data/app/helpers/super/form_builder_helper.rb +23 -0
  10. data/app/views/layouts/super/application.html.erb +26 -6
  11. data/app/views/super/application/{_resources_header.html.erb → _collection_header.html.erb} +5 -6
  12. data/app/views/super/application/_display_rich_text.html.erb +1 -0
  13. data/app/views/super/application/_filter.html.erb +14 -0
  14. data/app/views/super/application/_filter_type_select.html.erb +18 -0
  15. data/app/views/super/application/_filter_type_text.html.erb +16 -0
  16. data/app/views/super/application/_filter_type_timestamp.html.erb +23 -0
  17. data/app/views/super/application/_flash.html.erb +13 -13
  18. data/app/views/super/application/_form_field__destroy.html.erb +1 -9
  19. data/app/views/super/application/_form_field_checkbox.html.erb +1 -0
  20. data/app/views/super/application/_form_field_rich_text_area.html.erb +1 -0
  21. data/app/views/super/application/_form_field_select.html.erb +1 -23
  22. data/app/views/super/application/_form_field_text.html.erb +1 -13
  23. data/app/views/super/application/_form_has_many.html.erb +1 -1
  24. data/app/views/super/application/{_resource_header.html.erb → _member_header.html.erb} +6 -6
  25. data/app/views/super/application/_super_layout.html.erb +12 -17
  26. data/app/views/super/application/_super_pagination.html.erb +16 -0
  27. data/app/views/super/application/_super_panel.html.erb +3 -7
  28. data/app/views/super/application/_super_schema_display_actions.html.erb +5 -0
  29. data/app/views/super/application/_super_schema_display_index.html.erb +24 -0
  30. data/app/views/super/application/_super_schema_display_show.html.erb +8 -0
  31. data/app/views/super/application/_super_schema_form.html.erb +15 -0
  32. data/app/views/super/application/edit.html.erb +2 -6
  33. data/app/views/super/application/index.html.erb +2 -6
  34. data/app/views/super/application/new.html.erb +2 -6
  35. data/app/views/super/application/show.html.erb +2 -6
  36. data/app/views/super/feather/{_chevron_down.svg → _chevron_down.html} +0 -0
  37. data/config/locales/en.yml +5 -0
  38. data/docs/README.md +4 -2
  39. data/docs/action_text.md +48 -0
  40. data/docs/cheat.md +41 -0
  41. data/docs/faq.md +3 -3
  42. data/docs/installation.md +21 -0
  43. data/docs/quick_start.md +1 -16
  44. data/docs/webpacker.md +13 -5
  45. data/frontend/super-frontend/dist/application.css +78105 -50441
  46. data/frontend/super-frontend/dist/application.js +1170 -359
  47. data/lib/generators/super/action_text/USAGE +23 -0
  48. data/lib/generators/super/action_text/action_text_generator.rb +30 -0
  49. data/lib/generators/super/action_text/templates/pack_super_action_text.css +23 -0
  50. data/lib/generators/super/action_text/templates/pack_super_action_text.js +4 -0
  51. data/lib/generators/super/install/install_generator.rb +16 -0
  52. data/lib/generators/super/resource/templates/resources_controller.rb.tt +1 -31
  53. data/lib/generators/super/webpacker/USAGE +5 -4
  54. data/lib/generators/super/webpacker/webpacker_generator.rb +3 -2
  55. data/lib/super.rb +21 -3
  56. data/lib/super/action_inquirer.rb +2 -2
  57. data/lib/super/assets.rb +112 -38
  58. data/lib/super/client_error.rb +43 -0
  59. data/lib/super/compatibility.rb +13 -1
  60. data/lib/super/configuration.rb +21 -69
  61. data/lib/super/controls.rb +9 -116
  62. data/lib/super/controls/optional.rb +79 -0
  63. data/lib/super/controls/required.rb +13 -0
  64. data/lib/super/controls/steps.rb +114 -0
  65. data/lib/super/display.rb +66 -3
  66. data/lib/super/display/guesser.rb +34 -0
  67. data/lib/super/display/schema_types.rb +61 -25
  68. data/lib/super/engine.rb +9 -1
  69. data/lib/super/error.rb +17 -9
  70. data/lib/super/filter.rb +12 -0
  71. data/lib/super/filter/form_object.rb +97 -0
  72. data/lib/super/filter/guesser.rb +30 -0
  73. data/lib/super/filter/operator.rb +103 -0
  74. data/lib/super/filter/plugin.rb +47 -0
  75. data/lib/super/filter/schema_types.rb +112 -0
  76. data/lib/super/form.rb +35 -0
  77. data/lib/super/form/builder.rb +204 -0
  78. data/lib/super/form/guesser.rb +27 -0
  79. data/lib/super/form/inline_errors.rb +26 -0
  80. data/lib/super/form/schema_types.rb +29 -22
  81. data/lib/super/form/strong_params.rb +29 -0
  82. data/lib/super/layout.rb +28 -0
  83. data/lib/super/link.rb +55 -32
  84. data/lib/super/pagination.rb +55 -0
  85. data/lib/super/panel.rb +13 -0
  86. data/lib/super/partial.rb +12 -0
  87. data/lib/super/partial/resolving.rb +24 -0
  88. data/lib/super/plugin.rb +34 -63
  89. data/lib/super/schema.rb +12 -22
  90. data/lib/super/schema/common.rb +25 -0
  91. data/lib/super/schema/guesser.rb +77 -0
  92. data/lib/super/version.rb +1 -1
  93. data/lib/super/view_helper.rb +1 -20
  94. metadata +90 -33
  95. data/app/helpers/super/application_helper.rb +0 -32
  96. data/app/views/super/application/_form.html.erb +0 -17
  97. data/app/views/super/application/_form_inline_errors.html.erb +0 -10
  98. data/app/views/super/application/_index.html.erb +0 -45
  99. data/app/views/super/application/_show.html.erb +0 -10
  100. data/docs/controls.md +0 -39
  101. data/frontend/super-frontend/build.js +0 -36
  102. data/frontend/super-frontend/package.json +0 -21
  103. data/frontend/super-frontend/postcss.config.js +0 -6
  104. data/frontend/super-frontend/src/javascripts/super/application.ts +0 -18
  105. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.ts +0 -21
  106. data/frontend/super-frontend/src/javascripts/super/rails__ujs.d.ts +0 -1
  107. data/frontend/super-frontend/src/javascripts/super/toggle_pending_destruction_controller.ts +0 -15
  108. data/frontend/super-frontend/src/stylesheets/super/application.css +0 -77
  109. data/frontend/super-frontend/tailwind.config.js +0 -9
  110. data/frontend/super-frontend/tsconfig.json +0 -13
  111. data/frontend/super-frontend/yarn.lock +0 -5540
  112. data/lib/super/step.rb +0 -36
  113. data/lib/tasks/super_tasks.rake +0 -4
@@ -1,9 +1,72 @@
1
1
  module Super
2
+ # This schema type is meant to be used for +#index+ or +#show+ actions to
3
+ # transform database fields into something that is human friendly.
4
+ #
5
+ # ```
6
+ # class MembersController::Controls
7
+ # # ...
8
+ #
9
+ # def show_schema
10
+ # Super::Display.new do |fields, type|
11
+ # fields[:name] = type.manual { |name| name }
12
+ # fields[:rank] = type.manual { |rank| rank }
13
+ # fields[:position] = type.manual { |position| position }
14
+ # fields[:ship] = type.manual { |ship| "#{ship.name} (Ship ##{ship.id})" }
15
+ # fields[:created_at] = type.manual { |created_at| created_at.iso8601 }
16
+ # fields[:updated_at] = type.manual { |updated_at| updated_at.iso8601 }
17
+ # end
18
+ # end
19
+ #
20
+ # # ...
21
+ # end
22
+ # ```
2
23
  class Display
3
- def self.format(schema, resource, column)
4
- value = resource.public_send(column)
24
+ include Schema::Common
5
25
 
6
- schema.fields[column].present(value)
26
+ def initialize(action:)
27
+ @action_inquirer = action
28
+ @fields = Super::Schema::Fields.new
29
+ @schema_types = SchemaTypes.new(fields: @fields)
30
+
31
+ yield(@fields, @schema_types)
32
+
33
+ return if !@action_inquirer.index?
34
+ return if @schema_types.actions_called?
35
+ @fields[:actions] = @schema_types.actions
36
+ end
37
+
38
+ def to_partial_path
39
+ if @action_inquirer.index?
40
+ "super_schema_display_index"
41
+ elsif @action_inquirer.show?
42
+ "super_schema_display_show"
43
+ else
44
+ "super_schema_display_#{@action_inquirer.action}"
45
+ end
46
+ end
47
+
48
+ # @private
49
+ def render_field(template:, record:, column:)
50
+ formatter = @fields[column]
51
+
52
+ formatted =
53
+ if formatter.real?
54
+ value = record.public_send(column)
55
+ formatter.present(value)
56
+ else
57
+ formatter.present
58
+ end
59
+
60
+ if formatted.respond_to?(:to_partial_path)
61
+ if formatted.respond_to?(:locals)
62
+ formatted.locals[:record] ||= record
63
+ template.render(formatted, formatted.locals)
64
+ else
65
+ template.render(formatted)
66
+ end
67
+ else
68
+ formatted
69
+ end
7
70
  end
8
71
  end
9
72
  end
@@ -0,0 +1,34 @@
1
+ module Super
2
+ class Display
3
+ class Guesser
4
+ def initialize(model:, action:, fields:, type:)
5
+ @model = model
6
+ @action_inquirer = action
7
+ @fields = fields
8
+ @type = type
9
+ end
10
+
11
+ def call
12
+ Schema::Guesser
13
+ .new(model: @model, fields: @fields, type: @type)
14
+ .ignore_foreign_keys
15
+ .limit { 5 if @action_inquirer.index? }
16
+ .assign_type { |attribute_name| attribute_type_for(attribute_name) }
17
+ .call
18
+ end
19
+
20
+ private
21
+
22
+ def attribute_type_for(attribute_name)
23
+ type = @model.type_for_attribute(attribute_name).type
24
+
25
+ case type
26
+ when :datetime
27
+ @type.timestamp
28
+ else
29
+ @type.text
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,39 +1,75 @@
1
1
  module Super
2
2
  class Display
3
- # This schema type is meant to be used for +#index+ or +#show+ actions to
4
- # transform database fields into something that is human friendly.
5
- #
6
- # ```
7
- # class MembersController::Controls
8
- # # ...
9
- #
10
- # def show_schema
11
- # Super::Schema.new(Super::Display::SchemaTypes.new) do |fields, type|
12
- # fields[:name] = type.dynamic { |name| name }
13
- # fields[:rank] = type.dynamic { |rank| rank }
14
- # fields[:position] = type.dynamic { |position| position }
15
- # fields[:ship] = type.dynamic { |ship| "#{ship.name} (Ship ##{ship.id})" }
16
- # fields[:created_at] = type.dynamic { |created_at| created_at.iso8601 }
17
- # fields[:updated_at] = type.dynamic { |updated_at| updated_at.iso8601 }
18
- # end
19
- # end
20
- #
21
- # # ...
22
- # end
23
- # ```
24
3
  class SchemaTypes
25
4
  class Dynamic
26
- def initialize(transform_block)
5
+ def initialize(ignore_nil: true, &transform_block)
27
6
  @transform_block = transform_block
7
+ @ignore_nil = ignore_nil
28
8
  end
29
9
 
30
- def present(field)
31
- @transform_block.call(field)
10
+ def present(value)
11
+ return nil if value.nil? && @ignore_nil
12
+
13
+ @transform_block.call(value)
14
+ end
15
+
16
+ def real?
17
+ true
18
+ end
19
+ end
20
+
21
+ class Bypass
22
+ def initialize(partial:, real:)
23
+ @partial = partial
24
+ @real = real
25
+ end
26
+
27
+ def present
28
+ Partial.new(@partial)
29
+ end
30
+
31
+ def real?
32
+ @real
33
+ end
34
+ end
35
+
36
+ def initialize(fields:)
37
+ @actions_called = false
38
+ @fields = fields
39
+ end
40
+
41
+ def string
42
+ Dynamic.new(&:to_s)
43
+ end
44
+
45
+ alias text string
46
+
47
+ def timestamp
48
+ Dynamic.new(&:iso8601)
49
+ end
50
+
51
+ def rich_text
52
+ Dynamic.new do |value|
53
+ Partial.new("display_rich_text", locals: { rich_text: value })
32
54
  end
33
55
  end
34
56
 
57
+ def manual(&transform_block)
58
+ Dynamic.new(&transform_block)
59
+ end
60
+
35
61
  def dynamic(&transform_block)
36
- Dynamic.new(transform_block)
62
+ Dynamic.new(&transform_block)
63
+ end
64
+
65
+ def actions
66
+ @actions_called = true
67
+ Bypass.new(partial: "super_schema_display_actions", real: false)
68
+ end
69
+
70
+ # @private
71
+ def actions_called?
72
+ @actions_called
37
73
  end
38
74
  end
39
75
  end
@@ -1,10 +1,18 @@
1
1
  module Super
2
2
  # Configures the host Rails app to work with Super
3
3
  class Engine < ::Rails::Engine
4
+ isolate_namespace Super
5
+
4
6
  initializer "super.assets.precompile" do |app|
5
- if Super::Assets.sprockets_available?
7
+ if Super::Assets::Handler.sprockets_available?
6
8
  app.config.assets.precompile << "config/super_manifest.js"
7
9
  end
8
10
  end
11
+
12
+ config.to_prepare do
13
+ Super::Plugin::Registry.controller.ordered do |klass, method_name|
14
+ Super::ApplicationController.public_send(method_name, klass)
15
+ end
16
+ end
9
17
  end
10
18
  end
@@ -1,18 +1,26 @@
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
14
+ # Error raised when a `Super::Link` couldn't be found
9
15
  class LinkNotRegistered < Error; end
10
-
11
- class ClientError < Error; end
12
- class BadRequest < ClientError; end
13
- class Unauthorized < ClientError; end
14
- class Forbidden < ClientError; end
15
- class NotFound < ClientError; end
16
- class UnprocessableEntity < ClientError; end
16
+ # Error raised when rendering if `@view` wasn't set by the controller
17
+ class NothingToRender < Error
18
+ def initialize(basename)
19
+ super(
20
+ "Super's built-in `#{basename}.html.erb` requires `@view` to be set " \
21
+ "by the controller, but it wasn't set"
22
+ )
23
+ end
24
+ end
17
25
  end
18
26
  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