super 0.0.3 → 0.0.8

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