super 0.0.5 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -1
  3. data/CONTRIBUTING.md +56 -0
  4. data/README.md +68 -38
  5. data/STABILITY.md +50 -0
  6. data/app/assets/javascripts/super/application.js +1170 -359
  7. data/app/assets/stylesheets/super/application.css +78105 -50441
  8. data/app/controllers/super/application_controller.rb +44 -52
  9. data/app/helpers/super/form_builder_helper.rb +23 -0
  10. data/app/views/layouts/super/application.html.erb +26 -6
  11. data/app/views/super/application/{_resources_header.html.erb → _collection_header.html.erb} +5 -6
  12. data/app/views/super/application/_display_rich_text.html.erb +1 -0
  13. data/app/views/super/application/_filter.html.erb +14 -0
  14. data/app/views/super/application/_filter_type_select.html.erb +18 -0
  15. data/app/views/super/application/_filter_type_text.html.erb +16 -0
  16. data/app/views/super/application/_filter_type_timestamp.html.erb +23 -0
  17. data/app/views/super/application/_flash.html.erb +13 -13
  18. data/app/views/super/application/_form_field__destroy.html.erb +1 -9
  19. data/app/views/super/application/_form_field_checkbox.html.erb +1 -0
  20. data/app/views/super/application/_form_field_rich_text_area.html.erb +1 -0
  21. data/app/views/super/application/_form_field_select.html.erb +1 -23
  22. data/app/views/super/application/_form_field_text.html.erb +1 -13
  23. data/app/views/super/application/_form_has_many.html.erb +1 -1
  24. data/app/views/super/application/{_resource_header.html.erb → _member_header.html.erb} +6 -6
  25. data/app/views/super/application/_super_layout.html.erb +12 -17
  26. data/app/views/super/application/_super_pagination.html.erb +16 -0
  27. data/app/views/super/application/_super_panel.html.erb +3 -7
  28. data/app/views/super/application/_super_schema_display_actions.html.erb +5 -0
  29. data/app/views/super/application/_super_schema_display_index.html.erb +24 -0
  30. data/app/views/super/application/_super_schema_display_show.html.erb +8 -0
  31. data/app/views/super/application/_super_schema_form.html.erb +15 -0
  32. data/app/views/super/application/edit.html.erb +2 -6
  33. data/app/views/super/application/index.html.erb +2 -6
  34. data/app/views/super/application/new.html.erb +2 -6
  35. data/app/views/super/application/show.html.erb +2 -6
  36. data/app/views/super/feather/{_chevron_down.svg → _chevron_down.html} +0 -0
  37. data/config/locales/en.yml +5 -0
  38. data/docs/README.md +4 -2
  39. data/docs/action_text.md +48 -0
  40. data/docs/cheat.md +41 -0
  41. data/docs/faq.md +3 -3
  42. data/docs/installation.md +21 -0
  43. data/docs/quick_start.md +1 -16
  44. data/docs/webpacker.md +13 -5
  45. data/frontend/super-frontend/dist/application.css +78105 -50441
  46. data/frontend/super-frontend/dist/application.js +1170 -359
  47. data/lib/generators/super/action_text/USAGE +23 -0
  48. data/lib/generators/super/action_text/action_text_generator.rb +30 -0
  49. data/lib/generators/super/action_text/templates/pack_super_action_text.css +23 -0
  50. data/lib/generators/super/action_text/templates/pack_super_action_text.js +4 -0
  51. data/lib/generators/super/install/install_generator.rb +16 -0
  52. data/lib/generators/super/resource/templates/resources_controller.rb.tt +1 -31
  53. data/lib/generators/super/webpacker/USAGE +5 -4
  54. data/lib/generators/super/webpacker/webpacker_generator.rb +3 -2
  55. data/lib/super.rb +21 -3
  56. data/lib/super/action_inquirer.rb +2 -2
  57. data/lib/super/assets.rb +112 -38
  58. data/lib/super/client_error.rb +43 -0
  59. data/lib/super/compatibility.rb +13 -1
  60. data/lib/super/configuration.rb +21 -69
  61. data/lib/super/controls.rb +9 -116
  62. data/lib/super/controls/optional.rb +79 -0
  63. data/lib/super/controls/required.rb +13 -0
  64. data/lib/super/controls/steps.rb +114 -0
  65. data/lib/super/display.rb +66 -3
  66. data/lib/super/display/guesser.rb +34 -0
  67. data/lib/super/display/schema_types.rb +61 -25
  68. data/lib/super/engine.rb +9 -1
  69. data/lib/super/error.rb +17 -9
  70. data/lib/super/filter.rb +12 -0
  71. data/lib/super/filter/form_object.rb +97 -0
  72. data/lib/super/filter/guesser.rb +30 -0
  73. data/lib/super/filter/operator.rb +103 -0
  74. data/lib/super/filter/plugin.rb +47 -0
  75. data/lib/super/filter/schema_types.rb +112 -0
  76. data/lib/super/form.rb +35 -0
  77. data/lib/super/form/builder.rb +204 -0
  78. data/lib/super/form/guesser.rb +27 -0
  79. data/lib/super/form/inline_errors.rb +26 -0
  80. data/lib/super/form/schema_types.rb +29 -22
  81. data/lib/super/form/strong_params.rb +29 -0
  82. data/lib/super/layout.rb +28 -0
  83. data/lib/super/link.rb +55 -32
  84. data/lib/super/pagination.rb +55 -0
  85. data/lib/super/panel.rb +13 -0
  86. data/lib/super/partial.rb +12 -0
  87. data/lib/super/partial/resolving.rb +24 -0
  88. data/lib/super/plugin.rb +34 -63
  89. data/lib/super/schema.rb +12 -22
  90. data/lib/super/schema/common.rb +25 -0
  91. data/lib/super/schema/guesser.rb +77 -0
  92. data/lib/super/version.rb +1 -1
  93. data/lib/super/view_helper.rb +1 -20
  94. metadata +90 -33
  95. data/app/helpers/super/application_helper.rb +0 -32
  96. data/app/views/super/application/_form.html.erb +0 -17
  97. data/app/views/super/application/_form_inline_errors.html.erb +0 -10
  98. data/app/views/super/application/_index.html.erb +0 -45
  99. data/app/views/super/application/_show.html.erb +0 -10
  100. data/docs/controls.md +0 -39
  101. data/frontend/super-frontend/build.js +0 -36
  102. data/frontend/super-frontend/package.json +0 -21
  103. data/frontend/super-frontend/postcss.config.js +0 -6
  104. data/frontend/super-frontend/src/javascripts/super/application.ts +0 -18
  105. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.ts +0 -21
  106. data/frontend/super-frontend/src/javascripts/super/rails__ujs.d.ts +0 -1
  107. data/frontend/super-frontend/src/javascripts/super/toggle_pending_destruction_controller.ts +0 -15
  108. data/frontend/super-frontend/src/stylesheets/super/application.css +0 -77
  109. data/frontend/super-frontend/tailwind.config.js +0 -9
  110. data/frontend/super-frontend/tsconfig.json +0 -13
  111. data/frontend/super-frontend/yarn.lock +0 -5540
  112. data/lib/super/step.rb +0 -36
  113. data/lib/tasks/super_tasks.rake +0 -4
@@ -1,26 +1,5 @@
1
1
  module Super
2
2
  class Form
3
- # This schema type is used on your +#edit+ and +#new+ forms
4
- #
5
- # ```ruby
6
- # class MembersController::Controls
7
- # # ...
8
- #
9
- # def new_schema
10
- # Super::Schema.new(Super::Form::SchemaTypes.new) do |fields, type|
11
- # fields[:name] = type.generic("form_field_text")
12
- # fields[:rank] = type.generic("form_field_select", collection: Member.ranks.keys)
13
- # fields[:position] = type.generic("form_field_text")
14
- # fields[:ship_id] = type.generic(
15
- # "form_field_select",
16
- # collection: Ship.all.map { |s| ["#{s.name} (Ship ##{s.id})", s.id] },
17
- # )
18
- # end
19
- # end
20
- #
21
- # # ...
22
- # end
23
- # ```
24
3
  class SchemaTypes
25
4
  class Generic
26
5
  def initialize(partial_path:, extras:, nested:)
@@ -31,6 +10,16 @@ module Super
31
10
 
32
11
  attr_reader :nested_fields
33
12
 
13
+ def each_attribute
14
+ if block_given?
15
+ @nested_fields.each do |key, value|
16
+ yield(key, value)
17
+ end
18
+ end
19
+
20
+ enum_for(:each_attribute)
21
+ end
22
+
34
23
  # This takes advantage of a feature of Rails. If the value of
35
24
  # `#to_partial_path` is `my_form_field`, Rails renders
36
25
  # `app/views/super/application/_my_form_field.html.erb`, and this
@@ -69,7 +58,7 @@ module Super
69
58
  end
70
59
  end
71
60
 
72
- def setup(fields:)
61
+ def initialize(fields:)
73
62
  @fields = fields
74
63
  end
75
64
 
@@ -77,6 +66,24 @@ module Super
77
66
  Generic.new(partial_path: partial_path, extras: extras, nested: {})
78
67
  end
79
68
 
69
+ def select(**extras)
70
+ Generic.new(partial_path: "form_field_select", extras: extras, nested: {})
71
+ end
72
+
73
+ def string(**extras)
74
+ Generic.new(partial_path: "form_field_text", extras: extras, nested: {})
75
+ end
76
+
77
+ alias text string
78
+
79
+ def rich_text_area(**extras)
80
+ Generic.new(partial_path: "form_field_rich_text_area", extras: extras, nested: {})
81
+ end
82
+
83
+ def checkbox(**extras)
84
+ Generic.new(partial_path: "form_field_checkbox", extras: extras, nested: {})
85
+ end
86
+
80
87
  def has_many(reader, **extras)
81
88
  nested = @fields.nested do
82
89
  yield
@@ -0,0 +1,29 @@
1
+ module Super
2
+ class Form
3
+ class StrongParams
4
+ def initialize(form_schema)
5
+ @form_schema = form_schema
6
+ end
7
+
8
+ def require(model)
9
+ model.model_name.singular
10
+ end
11
+
12
+ def permit
13
+ unfurl(@form_schema)
14
+ end
15
+
16
+ private
17
+
18
+ def unfurl(responds_to_each_attribute)
19
+ responds_to_each_attribute.each_attribute.map do |name, type|
20
+ if type.nested_fields&.any?
21
+ { name => [:id, *unfurl(type)] }
22
+ else
23
+ name
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,5 +1,9 @@
1
+ require "super/partial/resolving"
2
+
1
3
  module Super
2
4
  class Layout
5
+ include Super::Partial::Resolving
6
+
3
7
  def initialize(headers: nil, asides: nil, mains: nil, footers: nil)
4
8
  @headers = Array(headers).compact
5
9
  @asides = Array(asides).compact
@@ -15,5 +19,29 @@ module Super
15
19
  def to_partial_path
16
20
  "super_layout"
17
21
  end
22
+
23
+ def resolve(template)
24
+ @resolved_headers = resolve_for_rendering(template, headers, nil)
25
+ @resolved_asides = resolve_for_rendering(template, asides, nil)
26
+ @resolved_mains = resolve_for_rendering(template, mains, nil)
27
+ @resolved_footers = resolve_for_rendering(template, footers, nil)
28
+ self
29
+ end
30
+
31
+ def resolved_headers
32
+ @resolved_headers || []
33
+ end
34
+
35
+ def resolved_asides
36
+ @resolved_asides || []
37
+ end
38
+
39
+ def resolved_mains
40
+ @resolved_mains || []
41
+ end
42
+
43
+ def resolved_footers
44
+ @resolved_footers || []
45
+ end
18
46
  end
19
47
  end
@@ -2,7 +2,11 @@ module Super
2
2
  # Links have three required attributes that are passed directly into Rails'
3
3
  # `link_to` helper
4
4
  class Link
5
- def self.resolve(link)
5
+ def self.find_all(*links)
6
+ links.map { |link| find(link) }
7
+ end
8
+
9
+ def self.find(link)
6
10
  if link.kind_of?(self)
7
11
  return link
8
12
  end
@@ -16,61 +20,61 @@ module Super
16
20
 
17
21
  def self.registry
18
22
  @registry ||= {
19
- new: -> (params:) {
20
- new(
21
- "New",
23
+ new: new(
24
+ "New",
25
+ -> (params:) {
22
26
  Rails.application.routes.url_for(
23
27
  controller: params[:controller],
24
28
  action: :new,
25
29
  only_path: true
26
30
  )
27
- )
28
- },
29
- index: -> (params:) {
30
- new(
31
- "Index",
31
+ }
32
+ ),
33
+ index: new(
34
+ "Index",
35
+ -> (params:) {
32
36
  Rails.application.routes.url_for(
33
37
  controller: params[:controller],
34
38
  action: :index,
35
39
  only_path: true
36
40
  )
37
- )
38
- },
39
- show: -> (resource, params:) {
40
- new(
41
- "View",
41
+ }
42
+ ),
43
+ show: new(
44
+ "View",
45
+ -> (record:, params:) {
42
46
  Rails.application.routes.url_for(
43
47
  controller: params[:controller],
44
48
  action: :show,
45
- id: resource,
49
+ id: record,
46
50
  only_path: true
47
51
  )
48
- )
49
- },
50
- edit: -> (resource, params:) {
51
- new(
52
- "Edit",
52
+ }
53
+ ),
54
+ edit: new(
55
+ "Edit",
56
+ -> (record:, params:) {
53
57
  Rails.application.routes.url_for(
54
58
  controller: params[:controller],
55
59
  action: :edit,
56
- id: resource,
60
+ id: record,
57
61
  only_path: true
58
62
  )
59
- )
60
- },
61
- destroy: -> (resource, params:) {
62
- new(
63
- "Delete",
63
+ }
64
+ ),
65
+ destroy: new(
66
+ "Delete",
67
+ -> (record:, params:) {
64
68
  Rails.application.routes.url_for(
65
69
  controller: params[:controller],
66
70
  action: :destroy,
67
- id: resource,
71
+ id: record,
68
72
  only_path: true
69
- ),
70
- method: :delete,
71
- data: { confirm: "Really delete?" }
72
- )
73
- },
73
+ )
74
+ },
75
+ method: :delete,
76
+ data: { confirm: "Really delete?" }
77
+ ),
74
78
  }
75
79
  end
76
80
 
@@ -80,8 +84,27 @@ module Super
80
84
  @options = options
81
85
  end
82
86
 
87
+ def to_s(default_options: nil, **proc_arguments)
88
+ default_options ||= {}
89
+ ActionController::Base.helpers.link_to(
90
+ value(text, proc_arguments),
91
+ value(href, proc_arguments),
92
+ default_options.deep_merge(value(options, proc_arguments))
93
+ )
94
+ end
95
+
96
+ private
97
+
83
98
  attr_reader :text
84
99
  attr_reader :href
85
100
  attr_reader :options
101
+
102
+ def value(proc_or_value, proc_arguments)
103
+ if proc_or_value.kind_of?(Proc)
104
+ proc_or_value.call(**proc_arguments)
105
+ else
106
+ proc_or_value
107
+ end
108
+ end
86
109
  end
87
110
  end
@@ -52,6 +52,10 @@ module Super
52
52
  end
53
53
  end
54
54
 
55
+ def to_partial_path
56
+ "super_pagination"
57
+ end
58
+
55
59
  private
56
60
 
57
61
  def pages
@@ -66,5 +70,56 @@ module Super
66
70
  end
67
71
  end
68
72
  end
73
+
74
+ module ControllerMethods
75
+ def index
76
+ super
77
+ @pagination = controls.initialize_pagination(action: action_inquirer, records: @records, query_params: request.GET)
78
+ @records = controls.paginate_records(action: action_inquirer, records: @records, pagination: @pagination)
79
+ @view.mains.first.parts.push(:@pagination)
80
+ end
81
+ end
82
+ end
83
+
84
+ class Controls
85
+ module Optional
86
+ # Specifies how many records to show per page
87
+ #
88
+ # @param action [ActionInquirer]
89
+ # @param query_params [Hash]
90
+ # @return [ActiveRecord::Relation]
91
+ def records_per_page(action:, query_params:)
92
+ Super.configuration.index_records_per_page
93
+ end
94
+ end
95
+
96
+ module Steps
97
+ # Sets up pagination
98
+ #
99
+ # @param action [ActionInquirer]
100
+ # @param records [ActiveRecord::Relation]
101
+ # @param query_params [Hash]
102
+ # @return [Pagination]
103
+ def initialize_pagination(action:, records:, query_params:)
104
+ Pagination.new(
105
+ total_count: records.size,
106
+ limit: records_per_page(action: action, query_params: query_params),
107
+ query_params: query_params,
108
+ page_query_param: :page
109
+ )
110
+ end
111
+
112
+ # Paginates
113
+ #
114
+ # @param action [ActionInquirer]
115
+ # @param records [ActiveRecord::Relation]
116
+ # @param pagination [Pagination]
117
+ # @return [ActiveRecord::Relation]
118
+ def paginate_records(action:, records:, pagination:)
119
+ records
120
+ .limit(pagination.limit)
121
+ .offset(pagination.offset)
122
+ end
123
+ end
69
124
  end
70
125
  end
@@ -1,5 +1,9 @@
1
+ require "super/partial/resolving"
2
+
1
3
  module Super
2
4
  class Panel
5
+ include Super::Partial::Resolving
6
+
3
7
  def initialize(*parts)
4
8
  if block_given?
5
9
  @parts = Array.new(yield)
@@ -10,6 +14,15 @@ module Super
10
14
 
11
15
  attr_reader :parts
12
16
 
17
+ def resolve(template, block)
18
+ @resolved_parts ||= resolve_for_rendering(template, parts, block)
19
+ self
20
+ end
21
+
22
+ def resolved_parts
23
+ @resolved_parts || []
24
+ end
25
+
13
26
  def to_partial_path
14
27
  "super_panel"
15
28
  end
@@ -1,5 +1,17 @@
1
1
  module Super
2
2
  class Partial
3
+ def self.render(partialish, template:)
4
+ if partialish.respond_to?(:to_partial_path)
5
+ if partialish.respond_to?(:locals)
6
+ template.render(partialish, partialish.locals)
7
+ else
8
+ template.render(partialish)
9
+ end
10
+ else
11
+ partialish
12
+ end
13
+ end
14
+
3
15
  def initialize(path, locals: {})
4
16
  @to_partial_path = path
5
17
  @locals = locals
@@ -0,0 +1,24 @@
1
+ module Super
2
+ class Partial
3
+ module Resolving
4
+ module_function
5
+
6
+ def resolve_for_rendering(template, partials, block)
7
+ if block
8
+ partials = [block.call, *parts]
9
+ end
10
+
11
+ partials =
12
+ partials.map do |partial|
13
+ if partial.is_a?(Symbol)
14
+ template.instance_variable_get(partial)
15
+ else
16
+ partial
17
+ end
18
+ end
19
+
20
+ partials.compact
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,88 +1,59 @@
1
1
  module Super
2
- class Pluggable < Module
3
- def initialize(registry_name)
4
- @registry_name = registry_name
5
- end
6
-
7
- def included(base)
8
- super
9
-
10
- PluginRegistry.base_set(@registry_name, base)
11
-
12
- plugins = PluginRegistry.plugins_for(@registry_name)
13
-
14
- plugins.each do |klass, method_name|
15
- base.public_send(method_name, klass)
2
+ module Plugin
3
+ class Registry
4
+ class << self
5
+ def controller
6
+ @controller ||= Registry.new
7
+ end
16
8
  end
17
- end
18
- end
19
9
 
20
- class PluginRegistry
21
- class << self
22
- def include_to(name, klass)
23
- plugin_push(name.to_sym, :include, klass)
10
+ def initialize
11
+ @registry = {}
12
+ @ordering = Hash.new { |hash, key| hash[key] = [] }
24
13
  end
25
14
 
26
- def prepend_to(name, klass)
27
- plugin_push(name.to_sym, :prepend, klass)
15
+ def use(include: nil, prepend: nil)
16
+ register(include: include, prepend: prepend, before: [], after: [])
28
17
  end
29
18
 
30
- def plugins_for(name)
31
- name = name.to_sym
32
-
33
- plugins.fetch(name) { {} }
19
+ def insert_before(*before, include: nil, prepend: nil)
20
+ register(include: include, prepend: prepend, before: before, after: [])
34
21
  end
35
22
 
36
- def base_set(name, klass)
37
- name = name.to_sym
38
-
39
- if !klass.kind_of?(Class)
40
- raise Error::InvalidPluginArgument,
41
- "Received `#{klass}` which must be a class"
42
- end
43
-
44
- bases[name] = klass
45
-
46
- true
23
+ def insert_after(*after, include: nil, prepend: nil)
24
+ register(include: include, prepend: prepend, before: [], after: after)
47
25
  end
48
26
 
49
- def base_get(name)
50
- name = name.to_sym
27
+ def classes_ordered
28
+ each_node = -> (&b) { @ordering.each_key(&b) }
29
+ each_child = -> (cb, &b) { @ordering[cb].each(&b) }
51
30
 
52
- bases[name]
31
+ TSort.tsort(each_node, each_child)
53
32
  end
54
33
 
55
- private
56
-
57
- def plugin_push(name, method_name, klass)
58
- if ![:include, :prepend].include?(method_name)
59
- raise Error::InvalidPluginArgument,
60
- "Received `#{method_name.inspect}`, must be either :include or :prepend"
34
+ def ordered
35
+ classes_ordered.each do |class_name|
36
+ yield class_name, @registry[class_name]
61
37
  end
38
+ end
62
39
 
63
- if !klass.kind_of?(Module)
64
- raise Error::InvalidPluginArgument,
65
- "Received `#{klass}` which must be a module"
66
- end
40
+ private
67
41
 
68
- plugins[name] ||= {}
69
- plugins[name][klass] = method_name
42
+ def register(include:, prepend:, before: [], after: [])
43
+ raise Error::InvalidPluginArgument, "only one of :include or :prepend can be filled out" if include && prepend
44
+ raise Error::InvalidPluginArgument, "at least one of :include or :prepend must be filled out" if include.nil? && prepend.nil?
70
45
 
71
- base = base_get(name)
46
+ klass = include || prepend
47
+ @registry[klass] = :include if include
48
+ @registry[klass] = :prepend if prepend
72
49
 
73
- if base
74
- base.public_send(method_name, klass)
50
+ before.each do |b|
51
+ @ordering[b].push(klass)
75
52
  end
76
53
 
77
- true
78
- end
79
-
80
- def plugins
81
- @plugins ||= {}
82
- end
54
+ @ordering[klass].push(*after)
83
55
 
84
- def bases
85
- @bases ||= {}
56
+ nil
86
57
  end
87
58
  end
88
59
  end