super 0.0.4 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +13 -0
  3. data/CONTRIBUTING.md +56 -0
  4. data/README.md +60 -85
  5. data/STABILITY.md +50 -0
  6. data/app/assets/javascripts/super/application.js +1169 -359
  7. data/app/assets/stylesheets/super/application.css +86648 -30707
  8. data/app/controllers/super/application_controller.rb +44 -71
  9. data/app/views/layouts/super/application.html.erb +26 -6
  10. data/app/views/super/application/{_resources_header.html.erb → _collection_header.html.erb} +5 -6
  11. data/app/views/super/application/_display_rich_text.html.erb +1 -0
  12. data/app/views/super/application/_filter.html.erb +14 -0
  13. data/app/views/super/application/_filter_type_select.html.erb +31 -0
  14. data/app/views/super/application/_filter_type_text.html.erb +22 -0
  15. data/app/views/super/application/_filter_type_timestamp.html.erb +35 -0
  16. data/app/views/super/application/_flash.html.erb +13 -13
  17. data/app/views/super/application/_form_field__destroy.html.erb +6 -2
  18. data/app/views/super/application/_form_field_checkbox.html.erb +15 -0
  19. data/app/views/super/application/_form_field_generic.html.erb +19 -0
  20. data/app/views/super/application/_form_field_rich_text_area.html.erb +13 -0
  21. data/app/views/super/application/_form_field_select.html.erb +11 -5
  22. data/app/views/super/application/_form_field_text.html.erb +13 -5
  23. data/app/views/super/application/_form_has_many.html.erb +3 -3
  24. data/app/views/super/application/_form_inline_errors.html.erb +1 -1
  25. data/app/views/super/application/{_resource_header.html.erb → _member_header.html.erb} +6 -6
  26. data/app/views/super/application/_super_layout.html.erb +12 -17
  27. data/app/views/super/application/_super_pagination.html.erb +16 -0
  28. data/app/views/super/application/_super_panel.html.erb +3 -7
  29. data/app/views/super/application/_super_schema_display_actions.html.erb +5 -0
  30. data/app/views/super/application/_super_schema_display_index.html.erb +24 -0
  31. data/app/views/super/application/_super_schema_display_show.html.erb +8 -0
  32. data/app/views/super/application/{_form.html.erb → _super_schema_form.html.erb} +2 -4
  33. data/app/views/super/application/edit.html.erb +1 -1
  34. data/app/views/super/application/index.html.erb +1 -1
  35. data/app/views/super/application/new.html.erb +1 -1
  36. data/app/views/super/application/show.html.erb +1 -1
  37. data/app/views/super/feather/{_chevron_down.svg → _chevron_down.html} +0 -0
  38. data/config/locales/en.yml +5 -0
  39. data/docs/README.md +8 -0
  40. data/docs/action_text.md +48 -0
  41. data/docs/cheat.md +41 -0
  42. data/docs/faq.md +44 -0
  43. data/docs/installation.md +21 -0
  44. data/docs/quick_start.md +30 -0
  45. data/docs/webpacker.md +25 -0
  46. data/docs/yard_customizations.rb +41 -0
  47. data/frontend/super-frontend/build.js +1 -1
  48. data/frontend/super-frontend/dist/application.css +86648 -30707
  49. data/frontend/super-frontend/dist/application.js +1169 -359
  50. data/frontend/super-frontend/package.json +2 -3
  51. data/frontend/super-frontend/src/javascripts/super/{application.ts → application.js} +5 -8
  52. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.js +17 -0
  53. data/frontend/super-frontend/src/javascripts/super/{toggle_pending_destruction_controller.ts → toggle_pending_destruction_controller.js} +2 -2
  54. data/frontend/super-frontend/tailwind.config.js +7 -1
  55. data/frontend/super-frontend/yarn.lock +1368 -1391
  56. data/lib/generators/super/action_text/USAGE +23 -0
  57. data/lib/generators/super/action_text/action_text_generator.rb +30 -0
  58. data/lib/generators/super/action_text/templates/pack_super_action_text.css +23 -0
  59. data/lib/generators/super/action_text/templates/pack_super_action_text.js +4 -0
  60. data/lib/generators/super/install/install_generator.rb +16 -0
  61. data/lib/generators/super/resource/templates/resources_controller.rb.tt +1 -31
  62. data/lib/generators/super/webpacker/USAGE +5 -4
  63. data/lib/generators/super/webpacker/webpacker_generator.rb +10 -1
  64. data/lib/super.rb +22 -5
  65. data/lib/super/action_inquirer.rb +2 -2
  66. data/lib/super/assets.rb +112 -38
  67. data/lib/super/client_error.rb +43 -0
  68. data/lib/super/compatibility.rb +25 -0
  69. data/lib/super/configuration.rb +21 -69
  70. data/lib/super/controls.rb +9 -257
  71. data/lib/super/controls/optional.rb +79 -0
  72. data/lib/super/controls/required.rb +13 -0
  73. data/lib/super/controls/steps.rb +114 -0
  74. data/lib/super/display.rb +66 -3
  75. data/lib/super/display/guesser.rb +34 -0
  76. data/lib/super/display/schema_types.rb +61 -25
  77. data/lib/super/engine.rb +7 -1
  78. data/lib/super/error.rb +8 -9
  79. data/lib/super/filter.rb +12 -0
  80. data/lib/super/filter/form_object.rb +97 -0
  81. data/lib/super/filter/guesser.rb +30 -0
  82. data/lib/super/filter/operator.rb +103 -0
  83. data/lib/super/filter/plugin.rb +47 -0
  84. data/lib/super/filter/schema_types.rb +112 -0
  85. data/lib/super/form.rb +35 -0
  86. data/lib/super/form/builder.rb +48 -0
  87. data/lib/super/form/guesser.rb +27 -0
  88. data/lib/super/form/schema_types.rb +29 -22
  89. data/lib/super/form/strong_params.rb +29 -0
  90. data/lib/super/layout.rb +28 -0
  91. data/lib/super/link.rb +55 -32
  92. data/lib/super/pagination.rb +55 -0
  93. data/lib/super/panel.rb +13 -0
  94. data/lib/super/partial.rb +12 -0
  95. data/lib/super/partial/resolving.rb +24 -0
  96. data/lib/super/plugin.rb +34 -63
  97. data/lib/super/schema.rb +12 -22
  98. data/lib/super/schema/common.rb +25 -0
  99. data/lib/super/schema/guesser.rb +77 -0
  100. data/lib/super/version.rb +1 -1
  101. data/lib/super/view_helper.rb +43 -0
  102. metadata +138 -41
  103. data/app/helpers/super/application_helper.rb +0 -32
  104. data/app/views/super/application/_index.html.erb +0 -45
  105. data/app/views/super/application/_show.html.erb +0 -10
  106. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.ts +0 -21
  107. data/frontend/super-frontend/src/javascripts/super/rails__ujs.d.ts +0 -1
  108. data/frontend/super-frontend/tsconfig.json +0 -13
  109. data/lib/super/action.rb +0 -22
  110. data/lib/super/action/step.rb +0 -36
  111. data/lib/super/test_support/copy_app_templates/controllers/favorite_things_controller.rb +0 -50
  112. data/lib/super/test_support/copy_app_templates/controllers/members_controller.rb +0 -74
  113. data/lib/super/test_support/copy_app_templates/controllers/ships_controller.rb +0 -47
  114. data/lib/super/test_support/copy_app_templates/migrations/20190216224956_create_members.rb +0 -11
  115. data/lib/super/test_support/copy_app_templates/migrations/20190803143320_create_ships.rb +0 -11
  116. data/lib/super/test_support/copy_app_templates/migrations/20190806014121_add_ship_to_members.rb +0 -5
  117. data/lib/super/test_support/copy_app_templates/migrations/20191126050453_create_favorite_things.rb +0 -10
  118. data/lib/super/test_support/copy_app_templates/models/favorite_thing.rb +0 -7
  119. data/lib/super/test_support/copy_app_templates/models/member.rb +0 -23
  120. data/lib/super/test_support/copy_app_templates/models/ship.rb +0 -3
  121. data/lib/super/test_support/copy_app_templates/routes.rb +0 -11
  122. data/lib/super/test_support/copy_app_templates/seeds.rb +0 -2
  123. data/lib/super/test_support/copy_app_templates/views/members/_favorite_things.html.erb +0 -11
  124. data/lib/super/test_support/fixtures/favorite_things.yml +0 -9
  125. data/lib/super/test_support/fixtures/members.yml +0 -336
  126. data/lib/super/test_support/fixtures/ships.yml +0 -10
  127. data/lib/super/test_support/generate_copy_app.rb +0 -42
  128. data/lib/super/test_support/generate_dummy.rb +0 -93
  129. data/lib/super/test_support/starfleet_seeder.rb +0 -50
  130. data/lib/tasks/super_tasks.rake +0 -4
@@ -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,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
@@ -2,9 +2,15 @@ module Super
2
2
  # Configures the host Rails app to work with Super
3
3
  class Engine < ::Rails::Engine
4
4
  initializer "super.assets.precompile" do |app|
5
- if Super::Assets.sprockets_available?
5
+ if Super::Assets::Handler.sprockets_available?
6
6
  app.config.assets.precompile << "config/super_manifest.js"
7
7
  end
8
8
  end
9
+
10
+ config.to_prepare do
11
+ Super::Plugin::Registry.controller.ordered do |klass, method_name|
12
+ Super::ApplicationController.public_send(method_name, klass)
13
+ end
14
+ end
9
15
  end
10
16
  end
@@ -1,18 +1,17 @@
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
17
16
  end
18
17
  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