super 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +56 -0
  3. data/README.md +6 -0
  4. data/STABILITY.md +50 -0
  5. data/app/assets/javascripts/super/application.js +12 -7
  6. data/app/controllers/super/application_controller.rb +1 -1
  7. data/app/views/layouts/super/application.html.erb +14 -6
  8. data/app/views/super/application/_super_panel.html.erb +1 -1
  9. data/app/views/super/application/_super_schema_display_index.html.erb +4 -4
  10. data/app/views/super/application/_super_schema_display_show.html.erb +3 -3
  11. data/app/views/super/application/_super_schema_form.html.erb +1 -1
  12. data/frontend/super-frontend/dist/application.js +12 -7
  13. data/frontend/super-frontend/src/javascripts/super/application.ts +5 -8
  14. data/lib/generators/super/resource/templates/resources_controller.rb.tt +1 -31
  15. data/lib/generators/super/webpacker/webpacker_generator.rb +3 -2
  16. data/lib/super.rb +11 -0
  17. data/lib/super/assets.rb +108 -38
  18. data/lib/super/configuration.rb +18 -73
  19. data/lib/super/controls.rb +2 -25
  20. data/lib/super/controls/optional.rb +41 -16
  21. data/lib/super/controls/required.rb +1 -29
  22. data/lib/super/controls/steps.rb +9 -23
  23. data/lib/super/display.rb +72 -0
  24. data/lib/super/display/guesser.rb +34 -0
  25. data/lib/super/display/schema_types.rb +19 -62
  26. data/lib/super/engine.rb +1 -1
  27. data/lib/super/filter.rb +5 -130
  28. data/lib/super/filter/form_object.rb +97 -0
  29. data/lib/super/filter/guesser.rb +30 -0
  30. data/lib/super/filter/plugin.rb +47 -0
  31. data/lib/super/filter/schema_types.rb +1 -7
  32. data/lib/super/form.rb +27 -40
  33. data/lib/super/form/builder.rb +48 -0
  34. data/lib/super/form/guesser.rb +27 -0
  35. data/lib/super/form/schema_types.rb +19 -29
  36. data/lib/super/form/strong_params.rb +29 -0
  37. data/lib/super/pagination.rb +10 -16
  38. data/lib/super/schema.rb +0 -25
  39. data/lib/super/schema/common.rb +25 -0
  40. data/lib/super/schema/guesser.rb +77 -0
  41. data/lib/super/version.rb +1 -1
  42. metadata +14 -2
@@ -19,84 +19,29 @@ module Super
19
19
  # end
20
20
  # ```
21
21
  class Configuration
22
- module ConfigurationLogic # @api private
23
- def self.included(base)
24
- base.extend(ClassMethods)
25
- end
22
+ def initialize
23
+ self.title = "Super Admin"
24
+ self.index_records_per_page = 20
25
+ self.controller_namespace = "admin"
26
+ self.route_namespace = :admin
26
27
 
27
- def initialize
28
- self.class.defaults.each do |key, value|
29
- if value.respond_to?(:call)
30
- value = value.call
31
- end
28
+ controller_plugins.use(prepend: Super::Filter::ControllerMethods)
29
+ controller_plugins.use(prepend: Super::Pagination::ControllerMethods)
32
30
 
33
- public_send("#{key}=", value)
34
- end
35
-
36
- Plugin::Registry.controller.use(prepend: Super::Filter::ControllerMethods)
37
- Plugin::Registry.controller.use(prepend: Super::Pagination::ControllerMethods)
38
- end
39
-
40
- def configured?(attr)
41
- instance_variable_defined?("@#{attr}")
42
- end
43
-
44
- module ClassMethods
45
- def defaults
46
- @defaults ||= {}
47
- end
48
-
49
- def wraps
50
- @wraps ||= {}
51
- end
52
-
53
- def configure(attr, wrap: nil, enum: nil, **kwargs)
54
- if kwargs.key?(:default)
55
- defaults[attr] = kwargs[:default]
56
- end
57
-
58
- define_method(attr) do
59
- if !configured?(attr)
60
- raise Error::UnconfiguredConfiguration, "unconfigured: #{attr}"
61
- end
62
-
63
- result = instance_variable_get("@#{attr}")
64
-
65
- if wrap.nil?
66
- result
67
- else
68
- wrap.call(result)
69
- end
70
- end
71
-
72
- define_method("#{attr}=") do |value|
73
- if enum.is_a?(Array)
74
- if !enum.include?(value)
75
- raise Error::InvalidConfiguration,
76
- "tried to set `#{attr}` to `#{value.inspect}`, " \
77
- "expected: #{enum.join(", ")}"
78
- end
79
- end
80
-
81
- instance_variable_set("@#{attr}", value)
82
- value
83
- end
84
- end
85
- end
31
+ self.javascripts = [Super::Assets.auto("super/application")]
32
+ self.stylesheets = [Super::Assets.auto("super/application")]
86
33
  end
87
34
 
88
- include ConfigurationLogic
35
+ attr_accessor :title
36
+ attr_accessor :index_records_per_page
37
+ attr_accessor :controller_namespace
38
+ attr_writer :route_namespace
39
+ def route_namespace
40
+ [@route_namespace].flatten
41
+ end
89
42
 
90
- # @!attribute [rw]
91
- configure :title
92
- # @!attribute [rw]
93
- configure :index_records_per_page, default: 20
94
- # @!attribute [rw]
95
- configure :controller_namespace, default: "admin"
96
- # @!attribute [rw]
97
- configure :route_namespace, default: :admin, wrap: -> (val) { [val].flatten }
98
- # @!attribute [rw]
99
- configure :asset_handler, default: -> { Super::Assets.auto }
43
+ attr_accessor :javascripts
44
+ attr_accessor :stylesheets
100
45
 
101
46
  def controller_plugins
102
47
  Plugin::Registry.controller
@@ -3,34 +3,11 @@ require "super/controls/required"
3
3
  require "super/controls/steps"
4
4
 
5
5
  module Super
6
- # A wrapper around the per-controller Controls classes. This class often
7
- # directly delegates to the per-controller classes, but it can also provide
8
- # some default implementation.
6
+ # The base Controls class. Most parts of Super can be configured by
7
+ # customizing its methods.
9
8
  class Controls
10
9
  include Required
11
10
  include Optional
12
11
  include Steps
13
-
14
- def initialize(actual)
15
- @actual = actual
16
- end
17
-
18
- attr_reader :actual
19
-
20
- private
21
-
22
- def default_for(method_name, *args, **kwargs)
23
- if @actual.respond_to?(method_name)
24
- if args.empty? && kwargs.empty?
25
- return @actual.public_send(method_name)
26
- elsif args.empty?
27
- return @actual.public_send(method_name, **kwargs)
28
- else
29
- return @actual.public_send(method_name, *args)
30
- end
31
- end
32
-
33
- yield
34
- end
35
12
  end
36
13
  end
@@ -6,9 +6,7 @@ module Super
6
6
  #
7
7
  # @return [String]
8
8
  def title
9
- default_for(:title) do
10
- model.name.pluralize
11
- end
9
+ model.name.pluralize
12
10
  end
13
11
 
14
12
  # Configures what database records are visible on load. This is an optional
@@ -17,20 +15,49 @@ module Super
17
15
  # @param action [ActionInquirer]
18
16
  # @return [ActiveRecord::Relation]
19
17
  def scope(action:)
20
- default_for(:scope, action: action) do
21
- model.all
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
22
29
  end
23
30
  end
24
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
+
25
54
  # Configures the actions linked to on the index page. This is an optional
26
55
  # method
27
56
  #
28
57
  # @param action [ActionInquirer]
29
58
  # @return [Array<Link>]
30
59
  def collection_actions(action:)
31
- default_for(:collection_actions, action: action) do
32
- Super::Link.find_all(:new)
33
- end
60
+ Super::Link.find_all(:new)
34
61
  end
35
62
 
36
63
  # Configures the actions linked to on the show page as well as each row of
@@ -39,14 +66,12 @@ module Super
39
66
  # @param action [ActionInquirer]
40
67
  # @return [Array<Link>]
41
68
  def member_actions(action:)
42
- default_for(:member_actions, action: action) do
43
- if action.show?
44
- Super::Link.find_all(:edit, :destroy)
45
- elsif action.edit?
46
- Super::Link.find_all(:show, :destroy)
47
- else
48
- Super::Link.find_all(:show, :edit, :destroy)
49
- end
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)
50
75
  end
51
76
  end
52
77
  end
@@ -6,35 +6,7 @@ module Super
6
6
  #
7
7
  # @return [ActiveRecord::Base]
8
8
  def model
9
- @actual.model
10
- end
11
-
12
- # Configures which parameters could be written to the database. This is a
13
- # required method
14
- #
15
- # @param params [ActionController::Parameters]
16
- # @param action [ActionInquirer]
17
- # @return [ActionController::Parameters]
18
- def permitted_params(params, action:)
19
- @actual.permitted_params(params, action: action)
20
- end
21
-
22
- # Configures the fields that are displayed on the index and show actions.
23
- # This is a required method
24
- #
25
- # @param action [ActionInquirer]
26
- # @return [Schema]
27
- def display_schema(action:)
28
- @actual.display_schema(action: action)
29
- end
30
-
31
- # Configures the editable fields on the new and edit actions. This is a
32
- # required method
33
- #
34
- # @param action [ActionInquirer]
35
- # @return [Schema]
36
- def form_schema(action:)
37
- @actual.form_schema(action: action)
9
+ raise NotImplementedError
38
10
  end
39
11
  end
40
12
  end
@@ -10,9 +10,7 @@ module Super
10
10
  # @param params [ActionController::Parameters]
11
11
  # @return [ActiveRecord::Relation]
12
12
  def load_records(action:, params:)
13
- default_for(:load_records, action: action, params: params) do
14
- scope(action: action)
15
- end
13
+ scope(action: action)
16
14
  end
17
15
 
18
16
  # Loads a record using `#scope`
@@ -21,9 +19,7 @@ module Super
21
19
  # @param params [ActionController::Parameters]
22
20
  # @return [ActiveRecord::Base]
23
21
  def load_record(action:, params:)
24
- default_for(:load_record, action: action, params: params) do
25
- scope(action: action).find(params[:id])
26
- end
22
+ scope(action: action).find(params[:id])
27
23
  end
28
24
 
29
25
  # Builds a record using `#scope`
@@ -31,9 +27,7 @@ module Super
31
27
  # @param action [ActionInquirer]
32
28
  # @return [ActiveRecord::Base]
33
29
  def build_record(action:)
34
- default_for(:build_record) do
35
- scope(action: action).build
36
- end
30
+ scope(action: action).build
37
31
  end
38
32
 
39
33
  # Builds and populates a record using `#scope`
@@ -42,31 +36,25 @@ module Super
42
36
  # @param params [ActionController::Parameters]
43
37
  # @return [ActiveRecord::Base]
44
38
  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
39
+ scope(action: action).build(permitted_params(params, action: action))
48
40
  end
49
41
 
50
42
  # Saves a record
51
43
  #
52
44
  # @param action [ActionInquirer]
53
45
  # @param params [ActionController::Parameters]
54
- # @return [ActiveRecord::Base]
46
+ # @return [true, false]
55
47
  def save_record(action:, record:, params:)
56
- default_for(:save_record, action: action, record: record, params: params) do
57
- record.save
58
- end
48
+ record.save
59
49
  end
60
50
 
61
51
  # Saves a record
62
52
  #
63
53
  # @param action [ActionInquirer]
64
54
  # @param params [ActionController::Parameters]
65
- # @return [ActiveRecord::Base]
55
+ # @return [true, false]
66
56
  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
57
+ record.update(permitted_params(params, action: action))
70
58
  end
71
59
 
72
60
  # Destroys a record
@@ -75,9 +63,7 @@ module Super
75
63
  # @param params [ActionController::Parameters]
76
64
  # @return [ActiveRecord::Base, false]
77
65
  def destroy_record(action:, record:, params:)
78
- default_for(:update_record, action: action, record: record, params: params) do
79
- record.destroy
80
- end
66
+ record.destroy
81
67
  end
82
68
 
83
69
  def build_index_view
@@ -0,0 +1,72 @@
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
+ # ```
23
+ class Display
24
+ include Schema::Common
25
+
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
70
+ end
71
+ end
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