super 0.0.7 → 0.0.12

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +0 -6
  3. data/CONTRIBUTING.md +56 -0
  4. data/README.md +49 -61
  5. data/Rakefile +16 -14
  6. data/app/assets/javascripts/super/application.js +297 -97
  7. data/app/assets/stylesheets/super/application.css +5600 -0
  8. data/app/controllers/super/application_controller.rb +20 -14
  9. data/app/helpers/super/form_builder_helper.rb +25 -0
  10. data/app/views/layouts/super/application.html.erb +23 -9
  11. data/app/views/super/application/{_super_schema_display_actions.html.erb → _display_actions.html.erb} +0 -0
  12. data/app/views/super/application/{_super_schema_display_index.html.erb → _display_index.html.erb} +6 -6
  13. data/app/views/super/application/_display_rich_text.html.erb +1 -0
  14. data/app/views/super/application/_display_show.html.erb +8 -0
  15. data/app/views/super/application/_filter.html.erb +5 -13
  16. data/app/views/super/application/_filter_type_select.html.erb +9 -19
  17. data/app/views/super/application/_filter_type_text.html.erb +7 -11
  18. data/app/views/super/application/_filter_type_timestamp.html.erb +6 -17
  19. data/app/views/super/application/_form.html.erb +15 -0
  20. data/app/views/super/application/_form_field__destroy.html.erb +1 -9
  21. data/app/views/super/application/_form_field_checkbox.html.erb +1 -0
  22. data/app/views/super/application/_form_field_rich_text_area.html.erb +1 -0
  23. data/app/views/super/application/_form_field_select.html.erb +1 -23
  24. data/app/views/super/application/_form_field_text.html.erb +1 -13
  25. data/app/views/super/application/{_super_layout.html.erb → _layout.html.erb} +7 -7
  26. data/app/views/super/application/{_super_pagination.html.erb → _pagination.html.erb} +1 -1
  27. data/app/views/super/application/{_super_panel.html.erb → _panel.html.erb} +2 -2
  28. data/app/views/super/application/_query.html.erb +18 -0
  29. data/app/views/super/application/_sort.html.erb +18 -0
  30. data/app/views/super/application/_sort_expression.html.erb +25 -0
  31. data/app/views/super/application/edit.html.erb +1 -0
  32. data/app/views/super/application/index.html.erb +1 -0
  33. data/app/views/super/application/new.html.erb +1 -0
  34. data/app/views/super/application/show.html.erb +1 -0
  35. data/app/views/super/feather/README.md +1 -0
  36. data/app/views/super/feather/_x.html +15 -0
  37. data/config/routes.rb +2 -0
  38. data/docs/cheat.md +8 -8
  39. data/frontend/super-frontend/dist/application.css +5600 -0
  40. data/frontend/super-frontend/dist/application.js +297 -97
  41. data/lib/generators/super/action_text/USAGE +23 -0
  42. data/lib/generators/super/action_text/action_text_generator.rb +32 -0
  43. data/lib/generators/super/action_text/templates/pack_super_action_text.css +23 -0
  44. data/lib/generators/super/action_text/templates/pack_super_action_text.js +4 -0
  45. data/lib/generators/super/install/install_generator.rb +18 -7
  46. data/lib/generators/super/install/templates/base_controller.rb.tt +9 -1
  47. data/lib/generators/super/install/templates/initializer.rb.tt +9 -2
  48. data/lib/generators/super/resource/resource_generator.rb +107 -30
  49. data/lib/generators/super/resource/templates/resources_controller.rb.tt +3 -39
  50. data/lib/generators/super/webpacker/USAGE +5 -4
  51. data/lib/generators/super/webpacker/webpacker_generator.rb +5 -2
  52. data/lib/super.rb +17 -0
  53. data/lib/super/action_inquirer.rb +2 -0
  54. data/lib/super/assets.rb +114 -38
  55. data/lib/super/client_error.rb +2 -0
  56. data/lib/super/compatibility.rb +2 -0
  57. data/lib/super/configuration.rb +16 -79
  58. data/lib/super/controls.rb +11 -25
  59. data/lib/super/controls/optional.rb +75 -16
  60. data/lib/super/controls/steps.rb +36 -58
  61. data/lib/super/controls/view.rb +55 -0
  62. data/lib/super/display.rb +88 -0
  63. data/lib/super/display/guesser.rb +36 -0
  64. data/lib/super/display/schema_types.rb +80 -78
  65. data/lib/super/engine.rb +5 -1
  66. data/lib/super/error.rb +21 -0
  67. data/lib/super/filter.rb +7 -130
  68. data/lib/super/filter/form_object.rb +94 -0
  69. data/lib/super/filter/guesser.rb +32 -0
  70. data/lib/super/filter/operator.rb +2 -0
  71. data/lib/super/filter/schema_types.rb +3 -7
  72. data/lib/super/form.rb +29 -40
  73. data/lib/super/form/builder.rb +206 -0
  74. data/lib/super/form/guesser.rb +29 -0
  75. data/lib/super/form/inline_errors.rb +28 -0
  76. data/lib/super/form/schema_types.rb +31 -29
  77. data/lib/super/form/strong_params.rb +31 -0
  78. data/lib/super/layout.rb +3 -1
  79. data/lib/super/link.rb +7 -0
  80. data/lib/super/navigation/automatic.rb +4 -2
  81. data/lib/super/pagination.rb +13 -17
  82. data/lib/super/panel.rb +3 -1
  83. data/lib/super/partial.rb +2 -0
  84. data/lib/super/partial/resolving.rb +2 -0
  85. data/lib/super/plugin.rb +2 -0
  86. data/lib/super/query/form_object.rb +48 -0
  87. data/lib/super/schema.rb +2 -25
  88. data/lib/super/schema/common.rb +27 -0
  89. data/lib/super/schema/guesser.rb +79 -0
  90. data/lib/super/sort.rb +110 -0
  91. data/lib/super/useful/builder.rb +25 -0
  92. data/lib/super/useful/enum.rb +63 -0
  93. data/lib/super/version.rb +3 -1
  94. data/lib/super/view_helper.rb +2 -19
  95. metadata +63 -34
  96. data/app/views/super/application/_form_inline_errors.html.erb +0 -10
  97. data/app/views/super/application/_super_schema_display_show.html.erb +0 -8
  98. data/app/views/super/application/_super_schema_form.html.erb +0 -15
  99. data/docs/README.md +0 -6
  100. data/docs/faq.md +0 -44
  101. data/docs/quick_start.md +0 -45
  102. data/docs/webpacker.md +0 -17
  103. data/docs/yard_customizations.rb +0 -41
  104. data/frontend/super-frontend/build.js +0 -36
  105. data/frontend/super-frontend/package.json +0 -21
  106. data/frontend/super-frontend/postcss.config.js +0 -6
  107. data/frontend/super-frontend/src/javascripts/super/application.ts +0 -18
  108. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.ts +0 -19
  109. data/frontend/super-frontend/src/javascripts/super/rails__ujs.d.ts +0 -1
  110. data/frontend/super-frontend/src/javascripts/super/toggle_pending_destruction_controller.ts +0 -15
  111. data/frontend/super-frontend/src/stylesheets/super/application.css +0 -77
  112. data/frontend/super-frontend/tailwind.config.js +0 -15
  113. data/frontend/super-frontend/tsconfig.json +0 -13
  114. data/frontend/super-frontend/yarn.lock +0 -5448
  115. data/lib/super/controls/required.rb +0 -41
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  class Controls
3
5
  # Methods that are called by controller actions. All of these methods have
@@ -10,9 +12,7 @@ module Super
10
12
  # @param params [ActionController::Parameters]
11
13
  # @return [ActiveRecord::Relation]
12
14
  def load_records(action:, params:)
13
- default_for(:load_records, action: action, params: params) do
14
- scope(action: action)
15
- end
15
+ scope(action: action)
16
16
  end
17
17
 
18
18
  # Loads a record using `#scope`
@@ -21,9 +21,7 @@ module Super
21
21
  # @param params [ActionController::Parameters]
22
22
  # @return [ActiveRecord::Base]
23
23
  def load_record(action:, params:)
24
- default_for(:load_record, action: action, params: params) do
25
- scope(action: action).find(params[:id])
26
- end
24
+ scope(action: action).find(params[:id])
27
25
  end
28
26
 
29
27
  # Builds a record using `#scope`
@@ -31,9 +29,7 @@ module Super
31
29
  # @param action [ActionInquirer]
32
30
  # @return [ActiveRecord::Base]
33
31
  def build_record(action:)
34
- default_for(:build_record) do
35
- scope(action: action).build
36
- end
32
+ scope(action: action).build
37
33
  end
38
34
 
39
35
  # Builds and populates a record using `#scope`
@@ -42,31 +38,25 @@ module Super
42
38
  # @param params [ActionController::Parameters]
43
39
  # @return [ActiveRecord::Base]
44
40
  def build_record_with_params(action:, params:)
45
- default_for(:build_record_with_params, action: action, params: params) do
46
- scope(action: action).build(permitted_params(params, action: action))
47
- end
41
+ scope(action: action).build(permitted_params(params, action: action))
48
42
  end
49
43
 
50
44
  # Saves a record
51
45
  #
52
46
  # @param action [ActionInquirer]
53
47
  # @param params [ActionController::Parameters]
54
- # @return [ActiveRecord::Base]
48
+ # @return [true, false]
55
49
  def save_record(action:, record:, params:)
56
- default_for(:save_record, action: action, record: record, params: params) do
57
- record.save
58
- end
50
+ record.save
59
51
  end
60
52
 
61
53
  # Saves a record
62
54
  #
63
55
  # @param action [ActionInquirer]
64
56
  # @param params [ActionController::Parameters]
65
- # @return [ActiveRecord::Base]
57
+ # @return [true, false]
66
58
  def update_record(action:, record:, params:)
67
- default_for(:update_record, action: action, record: record, params: params) do
68
- record.update(permitted_params(params, action: action))
69
- end
59
+ record.update(permitted_params(params, action: action))
70
60
  end
71
61
 
72
62
  # Destroys a record
@@ -75,53 +65,41 @@ module Super
75
65
  # @param params [ActionController::Parameters]
76
66
  # @return [ActiveRecord::Base, false]
77
67
  def destroy_record(action:, record:, params:)
78
- default_for(:update_record, action: action, record: record, params: params) do
79
- record.destroy
80
- end
68
+ record.destroy
81
69
  end
82
70
 
83
- def build_index_view
84
- Super::Layout.new(
85
- mains: [
86
- Super::Panel.new(
87
- Super::Partial.new("collection_header"),
88
- :@display
89
- ),
90
- ]
71
+ def initialize_query_form(params:, current_path:)
72
+ Super::Query::FormObject.new(
73
+ model: model,
74
+ params: params,
75
+ namespace: :q,
76
+ current_path: current_path,
91
77
  )
92
78
  end
93
79
 
94
- def build_show_view
95
- Super::Layout.new(
96
- mains: [
97
- Super::Panel.new(
98
- Super::Partial.new("member_header"),
99
- :@display
100
- ),
101
- ]
102
- )
80
+ def apply_queries(query_form:, records:)
81
+ query_form.apply_changes(records)
103
82
  end
104
83
 
105
- def build_new_view
106
- Super::Layout.new(
107
- mains: [
108
- Super::Panel.new(
109
- Super::Partial.new("collection_header"),
110
- :@form
111
- ),
112
- ]
113
- )
84
+ def initialize_filter_form(query_form:)
85
+ if filters_enabled?
86
+ query_form.add(
87
+ Super::Filter::FormObject,
88
+ namespace: :f,
89
+ schema: filter_schema
90
+ )
91
+ end
114
92
  end
115
93
 
116
- def build_edit_view
117
- Super::Layout.new(
118
- mains: [
119
- Super::Panel.new(
120
- Super::Partial.new("member_header"),
121
- :@form
122
- ),
123
- ]
124
- )
94
+ def initialize_sort_form(query_form:)
95
+ if sort_enabled?
96
+ query_form.add(
97
+ Super::Sort::FormObject,
98
+ namespace: :s,
99
+ default: default_sort,
100
+ sortable_columns: sortable_columns
101
+ )
102
+ end
125
103
  end
126
104
  end
127
105
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Controls
5
+ # Methods for `Controls` that have a sane default implementation
6
+ module View
7
+ def index_view
8
+ Super::Layout.new(
9
+ mains: [
10
+ Super::Panel.new(
11
+ Super::Partial.new("collection_header"),
12
+ :@display
13
+ ),
14
+ ],
15
+ asides: [
16
+ :@query_form,
17
+ ]
18
+ )
19
+ end
20
+
21
+ def show_view
22
+ Super::Layout.new(
23
+ mains: [
24
+ Super::Panel.new(
25
+ Super::Partial.new("member_header"),
26
+ :@display
27
+ ),
28
+ ]
29
+ )
30
+ end
31
+
32
+ def new_view
33
+ Super::Layout.new(
34
+ mains: [
35
+ Super::Panel.new(
36
+ Super::Partial.new("collection_header"),
37
+ :@form
38
+ ),
39
+ ]
40
+ )
41
+ end
42
+
43
+ def edit_view
44
+ Super::Layout.new(
45
+ mains: [
46
+ Super::Panel.new(
47
+ Super::Partial.new("member_header"),
48
+ :@form
49
+ ),
50
+ ]
51
+ )
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ # This schema type is meant to be used for +#index+ or +#show+ actions to
5
+ # transform database fields into something that is human friendly.
6
+ #
7
+ # ```
8
+ # class MembersController::Controls
9
+ # # ...
10
+ #
11
+ # def show_schema
12
+ # Super::Display.new do |fields, type|
13
+ # fields[:name] = type.manual { |name| name }
14
+ # fields[:rank] = type.manual { |rank| rank }
15
+ # fields[:position] = type.manual { |position| position }
16
+ # fields[:ship] = type.manual { |ship| "#{ship.name} (Ship ##{ship.id})" }
17
+ # fields[:created_at] = type.manual { |created_at| created_at.iso8601 }
18
+ # fields[:updated_at] = type.manual { |updated_at| updated_at.iso8601 }
19
+ # end
20
+ # end
21
+ #
22
+ # # ...
23
+ # end
24
+ # ```
25
+ class Display
26
+ include Schema::Common
27
+
28
+ def initialize
29
+ @fields = Super::Schema::Fields.new
30
+ @schema_types = SchemaTypes.new(fields: @fields)
31
+
32
+ yield(@fields, @schema_types)
33
+ end
34
+
35
+ def apply(action:)
36
+ @action_inquirer = action
37
+ return self if !@action_inquirer.index?
38
+ return self if @schema_types.actions_called?
39
+ @fields[:actions] = @schema_types.actions
40
+ self
41
+ end
42
+
43
+ def to_partial_path
44
+ if @action_inquirer.nil?
45
+ raise Super::Error::Initalization,
46
+ "You must call the `#apply` method after instantiating Super::Display"
47
+ elsif @action_inquirer.index?
48
+ "display_index"
49
+ elsif @action_inquirer.show?
50
+ "display_show"
51
+ else
52
+ "display_#{@action_inquirer.action}"
53
+ end
54
+ end
55
+
56
+ # @private
57
+ def render_attribute(template:, record:, column:)
58
+ formatter = @fields[column]
59
+ formatter = formatter.build if formatter.respond_to?(:build)
60
+
61
+ formatted =
62
+ SchemaTypes::TYPES
63
+ .case(formatter.type)
64
+ .when(:record) do
65
+ formatter.present(column, record)
66
+ end
67
+ .when(:column) do
68
+ value = record.public_send(column)
69
+ formatter.present(column, value)
70
+ end
71
+ .when(:none) do
72
+ formatter.present(column)
73
+ end
74
+ .result
75
+
76
+ if formatted.respond_to?(:to_partial_path)
77
+ if formatted.respond_to?(:locals)
78
+ formatted.locals[:record] ||= record
79
+ template.render(formatted, formatted.locals)
80
+ else
81
+ template.render(formatted)
82
+ end
83
+ else
84
+ formatted
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Display
5
+ class Guesser
6
+ def initialize(model:, action:, fields:, type:)
7
+ @model = model
8
+ @action_inquirer = action
9
+ @fields = fields
10
+ @type = type
11
+ end
12
+
13
+ def call
14
+ Schema::Guesser
15
+ .new(model: @model, fields: @fields, type: @type)
16
+ .ignore_foreign_keys
17
+ .limit { 5 if @action_inquirer.index? }
18
+ .assign_type { |attribute_name| attribute_type_for(attribute_name) }
19
+ .call
20
+ end
21
+
22
+ private
23
+
24
+ def attribute_type_for(attribute_name)
25
+ type = @model.type_for_attribute(attribute_name).type
26
+
27
+ case type
28
+ when :datetime
29
+ @type.timestamp
30
+ else
31
+ @type.text
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,112 +1,114 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  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
5
  class SchemaTypes
25
- class Dynamic
26
- def initialize(transform_block)
27
- @transform_block = transform_block
28
- end
6
+ TYPES = Useful::Enum.new(:column, :record, :none)
7
+
8
+ class Builder
9
+ extend Useful::Builder
29
10
 
30
- def present(value)
31
- @transform_block.call(value)
11
+ builder_with_block def transform(&block)
12
+ @transform_block = block
32
13
  end
33
14
 
34
- def real?
35
- true
15
+ builder def real; @real = true; end
16
+ builder def computed; @real = false; end
17
+
18
+ builder def column; @type = :column; end
19
+ builder def record; @type = :record; end
20
+ builder def none; @type = :none; end
21
+
22
+ builder def ignore_nil; @ignore_nil = true; end
23
+
24
+ def build
25
+ Built.new(
26
+ real: @real,
27
+ type: @type,
28
+ ignore_nil: !!@ignore_nil,
29
+ &@transform_block
30
+ )
36
31
  end
37
32
  end
38
33
 
39
- class Bypass
40
- def initialize(partial:, real:)
41
- @partial = partial
34
+ class Built
35
+ def initialize(real:, type:, ignore_nil:, &transform_block)
42
36
  @real = real
37
+ @type = type
38
+ @ignore_nil = ignore_nil
39
+ @transform_block = transform_block
43
40
  end
44
41
 
45
- def present
46
- Partial.new(@partial)
47
- end
42
+ def real?; @real; end
43
+ attr_reader :type
44
+
45
+ def present(attribute_name, value = nil)
46
+ if @transform_block.nil?
47
+ if attribute_name
48
+ raise Error::ArgumentError, "Transformation block is not set for column: #{attribute_name}"
49
+ else
50
+ raise Error::ArgumentError, "Transformation block is not set!"
51
+ end
52
+ end
53
+
54
+ return nil if value.nil? && @ignore_nil
48
55
 
49
- def real?
50
- @real
56
+ if @type == :none
57
+ @transform_block.call
58
+ else
59
+ @transform_block.call(value)
60
+ end
51
61
  end
52
62
  end
53
63
 
54
- def initialize(action_inquirer)
55
- @action_inquirer = action_inquirer
64
+ def initialize(fields:)
56
65
  @actions_called = false
66
+ @fields = fields
57
67
  end
58
68
 
59
- def before_yield(fields:)
60
- @fields = fields
69
+ def real(type = :column, &transform_block)
70
+ TYPES
71
+ .case(type)
72
+ .when(:column) { Builder.new.real.ignore_nil.column.transform(&transform_block) }
73
+ .when(:record) { Builder.new.real.ignore_nil.record.transform(&transform_block) }
74
+ .when(:none) { Builder.new.real.ignore_nil.none.transform(&transform_block) }
75
+ .result
61
76
  end
62
77
 
63
- def after_yield
64
- return if !@action_inquirer.index?
65
- return if @actions_called
66
- @fields[:actions] = actions
78
+ def computed(type = :column, &transform_block)
79
+ TYPES
80
+ .case(type)
81
+ .when(:column) { Builder.new.computed.ignore_nil.column.transform(&transform_block) }
82
+ .when(:record) { Builder.new.computed.ignore_nil.record.transform(&transform_block) }
83
+ .when(:none) { Builder.new.computed.ignore_nil.none.transform(&transform_block) }
84
+ .result
67
85
  end
68
86
 
69
- def dynamic(&transform_block)
70
- Dynamic.new(transform_block)
87
+ def manual(&transform_block)
88
+ real(:column, &transform_block)
71
89
  end
72
90
 
73
- def actions
74
- @actions_called = true
75
- Bypass.new(partial: "super_schema_display_actions", real: false)
91
+ def string; real(&:to_s); end
92
+ alias text string
93
+
94
+ def timestamp; real(&:to_s); end
95
+
96
+ def rich_text
97
+ real do |value|
98
+ Partial.new("display_rich_text", locals: { rich_text: value })
99
+ end
76
100
  end
77
101
 
78
- def to_partial_path
79
- if @action_inquirer.index?
80
- "super_schema_display_index"
81
- elsif @action_inquirer.show?
82
- "super_schema_display_show"
83
- else
84
- "super_schema_display_#{@action_inquirer.action}"
102
+ def actions
103
+ @actions_called = true
104
+ Builder.new.computed.none.transform do
105
+ Partial.new("display_actions")
85
106
  end
86
107
  end
87
108
 
88
109
  # @private
89
- def render_field(template:, record:, column:)
90
- formatter = @fields[column]
91
-
92
- formatted =
93
- if formatter.real?
94
- value = record.public_send(column)
95
- formatter.present(value)
96
- else
97
- formatter.present
98
- end
99
-
100
- if formatted.respond_to?(:to_partial_path)
101
- if formatted.respond_to?(:locals)
102
- formatted.locals[:record] ||= record
103
- template.render(formatted, formatted.locals)
104
- else
105
- template.render(formatted)
106
- end
107
- else
108
- formatted
109
- end
110
+ def actions_called?
111
+ @actions_called
110
112
  end
111
113
  end
112
114
  end