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
@@ -1,41 +1,13 @@
1
+ require "super/controls/optional"
2
+ require "super/controls/required"
3
+ require "super/controls/steps"
4
+
1
5
  module Super
2
- # A wrapper around the per-controller Controls classes. This class often
3
- # directly delegates to the per-controller classes, but it can also provide
4
- # some default implementation.
6
+ # The base Controls class. Most parts of Super can be configured by
7
+ # customizing its methods.
5
8
  class Controls
6
- def initialize(actual)
7
- @actual = actual
8
- end
9
-
10
- attr_reader :actual
11
-
12
- def title
13
- @actual.title
14
- end
15
-
16
- def model
17
- @actual.model
18
- end
19
-
20
- # @param action [ActionInquirer]
21
- def scope(action:)
22
- @actual.scope(action: action)
23
- end
24
-
25
- # @param params [ActionController::Parameters]
26
- # @param action [ActionInquirer]
27
- def permitted_params(params, action:)
28
- @actual.permitted_params(params, action: action)
29
- end
30
-
31
- # @param action [ActionInquirer]
32
- def display_schema(action:)
33
- @actual.display_schema(action: action)
34
- end
35
-
36
- # @param action [ActionInquirer]
37
- def form_schema(action:)
38
- @actual.form_schema(action: action)
39
- end
9
+ include Required
10
+ include Optional
11
+ include Steps
40
12
  end
41
13
  end
@@ -0,0 +1,79 @@
1
+ module Super
2
+ class Controls
3
+ # Methods for `Controls` that have a sane default implementation
4
+ module Optional
5
+ # This is an optional method
6
+ #
7
+ # @return [String]
8
+ def title
9
+ model.name.pluralize
10
+ end
11
+
12
+ # Configures what database records are visible on load. This is an optional
13
+ # method, it defaults to "`all`" methods
14
+ #
15
+ # @param action [ActionInquirer]
16
+ # @return [ActiveRecord::Relation]
17
+ def scope(action:)
18
+ model.all
19
+ end
20
+
21
+ # Configures the fields that are displayed on the index and show actions.
22
+ # This is a required method
23
+ #
24
+ # @param action [ActionInquirer]
25
+ # @return [Display]
26
+ def display_schema(action:)
27
+ Display.new(action: action) do |fields, type|
28
+ Display::Guesser.new(model: model, action: action, fields: fields, type: type).call
29
+ end
30
+ end
31
+
32
+ # Configures the editable fields on the new and edit actions. This is a
33
+ # required method
34
+ #
35
+ # @param action [ActionInquirer]
36
+ # @return [Form]
37
+ def form_schema(action:)
38
+ Form.new do |fields, type|
39
+ Form::Guesser.new(model: model, fields: fields, type: type).call
40
+ end
41
+ end
42
+
43
+ # Configures which parameters could be written to the database. This is a
44
+ # required method
45
+ #
46
+ # @param params [ActionController::Parameters]
47
+ # @param action [ActionInquirer]
48
+ # @return [ActionController::Parameters]
49
+ def permitted_params(params, action:)
50
+ strong_params = Super::Form::StrongParams.new(form_schema(action: action))
51
+ params.require(strong_params.require(model)).permit(strong_params.permit)
52
+ end
53
+
54
+ # Configures the actions linked to on the index page. This is an optional
55
+ # method
56
+ #
57
+ # @param action [ActionInquirer]
58
+ # @return [Array<Link>]
59
+ def collection_actions(action:)
60
+ Super::Link.find_all(:new)
61
+ end
62
+
63
+ # Configures the actions linked to on the show page as well as each row of
64
+ # the table on the index page. This is an optional method
65
+ #
66
+ # @param action [ActionInquirer]
67
+ # @return [Array<Link>]
68
+ def member_actions(action:)
69
+ if action.show?
70
+ Super::Link.find_all(:edit, :destroy)
71
+ elsif action.edit?
72
+ Super::Link.find_all(:show, :destroy)
73
+ else
74
+ Super::Link.find_all(:show, :edit, :destroy)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,13 @@
1
+ module Super
2
+ class Controls
3
+ # Methods for `Controls` that must be defined for Super to work properly
4
+ module Required
5
+ # Specifies the model. This is a required method
6
+ #
7
+ # @return [ActiveRecord::Base]
8
+ def model
9
+ raise NotImplementedError
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,114 @@
1
+ module Super
2
+ class Controls
3
+ # Methods that are called by controller actions. All of these methods have
4
+ # a default implementation, but feel free to override as needed.
5
+ module Steps
6
+ # Tells the controller how to load records in the index action using
7
+ # `#scope`
8
+ #
9
+ # @param action [ActionInquirer]
10
+ # @param params [ActionController::Parameters]
11
+ # @return [ActiveRecord::Relation]
12
+ def load_records(action:, params:)
13
+ scope(action: action)
14
+ end
15
+
16
+ # Loads a record using `#scope`
17
+ #
18
+ # @param action [ActionInquirer]
19
+ # @param params [ActionController::Parameters]
20
+ # @return [ActiveRecord::Base]
21
+ def load_record(action:, params:)
22
+ scope(action: action).find(params[:id])
23
+ end
24
+
25
+ # Builds a record using `#scope`
26
+ #
27
+ # @param action [ActionInquirer]
28
+ # @return [ActiveRecord::Base]
29
+ def build_record(action:)
30
+ scope(action: action).build
31
+ end
32
+
33
+ # Builds and populates a record using `#scope`
34
+ #
35
+ # @param action [ActionInquirer]
36
+ # @param params [ActionController::Parameters]
37
+ # @return [ActiveRecord::Base]
38
+ def build_record_with_params(action:, params:)
39
+ scope(action: action).build(permitted_params(params, action: action))
40
+ end
41
+
42
+ # Saves a record
43
+ #
44
+ # @param action [ActionInquirer]
45
+ # @param params [ActionController::Parameters]
46
+ # @return [true, false]
47
+ def save_record(action:, record:, params:)
48
+ record.save
49
+ end
50
+
51
+ # Saves a record
52
+ #
53
+ # @param action [ActionInquirer]
54
+ # @param params [ActionController::Parameters]
55
+ # @return [true, false]
56
+ def update_record(action:, record:, params:)
57
+ record.update(permitted_params(params, action: action))
58
+ end
59
+
60
+ # Destroys a record
61
+ #
62
+ # @param action [ActionInquirer]
63
+ # @param params [ActionController::Parameters]
64
+ # @return [ActiveRecord::Base, false]
65
+ def destroy_record(action:, record:, params:)
66
+ record.destroy
67
+ end
68
+
69
+ def build_index_view
70
+ Super::Layout.new(
71
+ mains: [
72
+ Super::Panel.new(
73
+ Super::Partial.new("collection_header"),
74
+ :@display
75
+ ),
76
+ ]
77
+ )
78
+ end
79
+
80
+ def build_show_view
81
+ Super::Layout.new(
82
+ mains: [
83
+ Super::Panel.new(
84
+ Super::Partial.new("member_header"),
85
+ :@display
86
+ ),
87
+ ]
88
+ )
89
+ end
90
+
91
+ def build_new_view
92
+ Super::Layout.new(
93
+ mains: [
94
+ Super::Panel.new(
95
+ Super::Partial.new("collection_header"),
96
+ :@form
97
+ ),
98
+ ]
99
+ )
100
+ end
101
+
102
+ def build_edit_view
103
+ Super::Layout.new(
104
+ mains: [
105
+ Super::Panel.new(
106
+ Super::Partial.new("member_header"),
107
+ :@form
108
+ ),
109
+ ]
110
+ )
111
+ end
112
+ end
113
+ end
114
+ end
@@ -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,69 @@
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
32
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 manual(&transform_block)
52
+ Dynamic.new(&transform_block)
33
53
  end
34
54
 
35
55
  def dynamic(&transform_block)
36
- Dynamic.new(transform_block)
56
+ Dynamic.new(&transform_block)
57
+ end
58
+
59
+ def actions
60
+ @actions_called = true
61
+ Bypass.new(partial: "super_schema_display_actions", real: false)
62
+ end
63
+
64
+ # @private
65
+ def actions_called?
66
+ @actions_called
37
67
  end
38
68
  end
39
69
  end