super 0.0.7 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
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