super 0.0.6 → 0.0.11

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CONTRIBUTING.md +56 -0
  4. data/README.md +55 -57
  5. data/Rakefile +16 -14
  6. data/STABILITY.md +50 -0
  7. data/app/assets/javascripts/super/application.js +297 -97
  8. data/app/assets/stylesheets/super/application.css +5600 -0
  9. data/app/controllers/super/application_controller.rb +15 -6
  10. data/app/helpers/super/form_builder_helper.rb +25 -0
  11. data/app/views/layouts/super/application.html.erb +22 -6
  12. data/app/views/super/application/_display_rich_text.html.erb +1 -0
  13. data/app/views/super/application/_filter.html.erb +6 -0
  14. data/app/views/super/application/_filter_type_select.html.erb +21 -0
  15. data/app/views/super/application/_filter_type_text.html.erb +18 -0
  16. data/app/views/super/application/_filter_type_timestamp.html.erb +24 -0
  17. data/app/views/super/application/_form_field__destroy.html.erb +1 -9
  18. data/app/views/super/application/_form_field_checkbox.html.erb +1 -0
  19. data/app/views/super/application/_form_field_rich_text_area.html.erb +1 -0
  20. data/app/views/super/application/_form_field_select.html.erb +1 -23
  21. data/app/views/super/application/_form_field_text.html.erb +1 -13
  22. data/app/views/super/application/_query.html.erb +18 -0
  23. data/app/views/super/application/_sort.html.erb +18 -0
  24. data/app/views/super/application/_sort_expression.html.erb +25 -0
  25. data/app/views/super/application/_super_layout.html.erb +5 -5
  26. data/app/views/super/application/_super_pagination.html.erb +16 -0
  27. data/app/views/super/application/_super_panel.html.erb +2 -2
  28. data/app/views/super/application/_super_schema_display_index.html.erb +9 -24
  29. data/app/views/super/application/_super_schema_display_show.html.erb +4 -4
  30. data/app/views/super/application/_super_schema_form.html.erb +3 -3
  31. data/app/views/super/application/edit.html.erb +2 -6
  32. data/app/views/super/application/index.html.erb +2 -6
  33. data/app/views/super/application/new.html.erb +2 -6
  34. data/app/views/super/application/show.html.erb +2 -6
  35. data/app/views/super/feather/README.md +1 -0
  36. data/app/views/super/feather/_x.html +15 -0
  37. data/config/routes.rb +2 -0
  38. data/docs/README.md +4 -2
  39. data/docs/action_text.md +48 -0
  40. data/docs/cheat.md +8 -8
  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/docs/yard_customizations.rb +2 -0
  46. data/frontend/super-frontend/dist/application.css +5600 -0
  47. data/frontend/super-frontend/dist/application.js +297 -97
  48. data/lib/generators/super/action_text/USAGE +23 -0
  49. data/lib/generators/super/action_text/action_text_generator.rb +32 -0
  50. data/lib/generators/super/action_text/templates/pack_super_action_text.css +23 -0
  51. data/lib/generators/super/action_text/templates/pack_super_action_text.js +4 -0
  52. data/lib/generators/super/install/install_generator.rb +2 -0
  53. data/lib/generators/super/resource/resource_generator.rb +2 -0
  54. data/lib/generators/super/resource/templates/resources_controller.rb.tt +1 -31
  55. data/lib/generators/super/webpacker/USAGE +5 -4
  56. data/lib/generators/super/webpacker/webpacker_generator.rb +5 -2
  57. data/lib/super.rb +23 -2
  58. data/lib/super/action_inquirer.rb +2 -0
  59. data/lib/super/assets.rb +114 -38
  60. data/lib/super/client_error.rb +2 -0
  61. data/lib/super/compatibility.rb +15 -1
  62. data/lib/super/configuration.rb +22 -69
  63. data/lib/super/controls.rb +6 -25
  64. data/lib/super/controls/optional.rb +71 -24
  65. data/lib/super/controls/required.rb +3 -29
  66. data/lib/super/controls/steps.rb +44 -53
  67. data/lib/super/controls/view.rb +55 -0
  68. data/lib/super/display.rb +80 -0
  69. data/lib/super/display/guesser.rb +36 -0
  70. data/lib/super/display/schema_types.rb +28 -33
  71. data/lib/super/engine.rb +11 -1
  72. data/lib/super/error.rb +14 -0
  73. data/lib/super/filter.rb +14 -0
  74. data/lib/super/filter/form_object.rb +94 -0
  75. data/lib/super/filter/guesser.rb +32 -0
  76. data/lib/super/filter/operator.rb +105 -0
  77. data/lib/super/filter/schema_types.rb +114 -0
  78. data/lib/super/form.rb +29 -40
  79. data/lib/super/form/builder.rb +206 -0
  80. data/lib/super/form/guesser.rb +29 -0
  81. data/lib/super/form/inline_errors.rb +28 -0
  82. data/lib/super/form/schema_types.rb +31 -29
  83. data/lib/super/form/strong_params.rb +31 -0
  84. data/lib/super/layout.rb +2 -0
  85. data/lib/super/link.rb +2 -0
  86. data/lib/super/navigation/automatic.rb +2 -0
  87. data/lib/super/pagination.rb +57 -0
  88. data/lib/super/panel.rb +2 -0
  89. data/lib/super/partial.rb +14 -0
  90. data/lib/super/partial/resolving.rb +2 -0
  91. data/lib/super/plugin.rb +36 -63
  92. data/lib/super/query/form_object.rb +48 -0
  93. data/lib/super/schema.rb +2 -24
  94. data/lib/super/schema/common.rb +27 -0
  95. data/lib/super/schema/guesser.rb +79 -0
  96. data/lib/super/sort.rb +110 -0
  97. data/lib/super/version.rb +3 -1
  98. data/lib/super/view_helper.rb +2 -19
  99. metadata +74 -22
  100. data/app/helpers/super/application_helper.rb +0 -39
  101. data/app/views/super/application/_form_inline_errors.html.erb +0 -10
  102. data/frontend/super-frontend/build.js +0 -36
  103. data/frontend/super-frontend/package.json +0 -21
  104. data/frontend/super-frontend/postcss.config.js +0 -6
  105. data/frontend/super-frontend/src/javascripts/super/application.ts +0 -18
  106. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.ts +0 -19
  107. data/frontend/super-frontend/src/javascripts/super/rails__ujs.d.ts +0 -1
  108. data/frontend/super-frontend/src/javascripts/super/toggle_pending_destruction_controller.ts +0 -15
  109. data/frontend/super-frontend/src/stylesheets/super/application.css +0 -77
  110. data/frontend/super-frontend/tailwind.config.js +0 -15
  111. data/frontend/super-frontend/tsconfig.json +0 -13
  112. data/frontend/super-frontend/yarn.lock +0 -5448
data/lib/super/layout.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "super/partial/resolving"
2
4
 
3
5
  module Super
data/lib/super/link.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  # Links have three required attributes that are passed directly into Rails'
3
5
  # `link_to` helper
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  class Navigation
3
5
  # Traverses the defined Rails Routes and attempts to build a list of links.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  class Pagination
3
5
  include Enumerable
@@ -52,6 +54,10 @@ module Super
52
54
  end
53
55
  end
54
56
 
57
+ def to_partial_path
58
+ "super_pagination"
59
+ end
60
+
55
61
  private
56
62
 
57
63
  def pages
@@ -66,5 +72,56 @@ module Super
66
72
  end
67
73
  end
68
74
  end
75
+
76
+ module ControllerMethods
77
+ def index
78
+ super
79
+ @pagination = controls.initialize_pagination(action: action_inquirer, records: @records, query_params: request.GET)
80
+ @records = controls.paginate_records(action: action_inquirer, records: @records, pagination: @pagination)
81
+ @view.mains.first.parts.push(:@pagination)
82
+ end
83
+ end
84
+ end
85
+
86
+ class Controls
87
+ module Optional
88
+ # Specifies how many records to show per page
89
+ #
90
+ # @param action [ActionInquirer]
91
+ # @param query_params [Hash]
92
+ # @return [ActiveRecord::Relation]
93
+ def records_per_page(action:, query_params:)
94
+ Super.configuration.index_records_per_page
95
+ end
96
+ end
97
+
98
+ module Steps
99
+ # Sets up pagination
100
+ #
101
+ # @param action [ActionInquirer]
102
+ # @param records [ActiveRecord::Relation]
103
+ # @param query_params [Hash]
104
+ # @return [Pagination]
105
+ def initialize_pagination(action:, records:, query_params:)
106
+ Pagination.new(
107
+ total_count: records.size,
108
+ limit: records_per_page(action: action, query_params: query_params),
109
+ query_params: query_params,
110
+ page_query_param: :page
111
+ )
112
+ end
113
+
114
+ # Paginates
115
+ #
116
+ # @param action [ActionInquirer]
117
+ # @param records [ActiveRecord::Relation]
118
+ # @param pagination [Pagination]
119
+ # @return [ActiveRecord::Relation]
120
+ def paginate_records(action:, records:, pagination:)
121
+ records
122
+ .limit(pagination.limit)
123
+ .offset(pagination.offset)
124
+ end
125
+ end
69
126
  end
70
127
  end
data/lib/super/panel.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "super/partial/resolving"
2
4
 
3
5
  module Super
data/lib/super/partial.rb CHANGED
@@ -1,5 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  class Partial
5
+ def self.render(partialish, template:)
6
+ if partialish.respond_to?(:to_partial_path)
7
+ if partialish.respond_to?(:locals)
8
+ template.render(partialish, partialish.locals)
9
+ else
10
+ template.render(partialish)
11
+ end
12
+ else
13
+ partialish
14
+ end
15
+ end
16
+
3
17
  def initialize(path, locals: {})
4
18
  @to_partial_path = path
5
19
  @locals = locals
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  class Partial
3
5
  module Resolving
data/lib/super/plugin.rb CHANGED
@@ -1,88 +1,61 @@
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
1
+ # frozen_string_literal: true
9
2
 
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)
3
+ module Super
4
+ module Plugin
5
+ class Registry
6
+ class << self
7
+ def controller
8
+ @controller ||= Registry.new
9
+ end
16
10
  end
17
- end
18
- end
19
11
 
20
- class PluginRegistry
21
- class << self
22
- def include_to(name, klass)
23
- plugin_push(name.to_sym, :include, klass)
12
+ def initialize
13
+ @registry = {}
14
+ @ordering = Hash.new { |hash, key| hash[key] = [] }
24
15
  end
25
16
 
26
- def prepend_to(name, klass)
27
- plugin_push(name.to_sym, :prepend, klass)
17
+ def use(include: nil, prepend: nil)
18
+ register(include: include, prepend: prepend, before: [], after: [])
28
19
  end
29
20
 
30
- def plugins_for(name)
31
- name = name.to_sym
32
-
33
- plugins.fetch(name) { {} }
21
+ def insert_before(*before, include: nil, prepend: nil)
22
+ register(include: include, prepend: prepend, before: before, after: [])
34
23
  end
35
24
 
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
25
+ def insert_after(*after, include: nil, prepend: nil)
26
+ register(include: include, prepend: prepend, before: [], after: after)
47
27
  end
48
28
 
49
- def base_get(name)
50
- name = name.to_sym
29
+ def classes_ordered
30
+ each_node = -> (&b) { @ordering.each_key(&b) }
31
+ each_child = -> (cb, &b) { @ordering[cb].each(&b) }
51
32
 
52
- bases[name]
33
+ TSort.tsort(each_node, each_child)
53
34
  end
54
35
 
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"
36
+ def ordered
37
+ classes_ordered.each do |class_name|
38
+ yield class_name, @registry[class_name]
61
39
  end
40
+ end
62
41
 
63
- if !klass.kind_of?(Module)
64
- raise Error::InvalidPluginArgument,
65
- "Received `#{klass}` which must be a module"
66
- end
42
+ private
67
43
 
68
- plugins[name] ||= {}
69
- plugins[name][klass] = method_name
44
+ def register(include:, prepend:, before: [], after: [])
45
+ raise Error::InvalidPluginArgument, "only one of :include or :prepend can be filled out" if include && prepend
46
+ raise Error::InvalidPluginArgument, "at least one of :include or :prepend must be filled out" if include.nil? && prepend.nil?
70
47
 
71
- base = base_get(name)
48
+ klass = include || prepend
49
+ @registry[klass] = :include if include
50
+ @registry[klass] = :prepend if prepend
72
51
 
73
- if base
74
- base.public_send(method_name, klass)
52
+ before.each do |b|
53
+ @ordering[b].push(klass)
75
54
  end
76
55
 
77
- true
78
- end
79
-
80
- def plugins
81
- @plugins ||= {}
82
- end
56
+ @ordering[klass].push(*after)
83
57
 
84
- def bases
85
- @bases ||= {}
58
+ nil
86
59
  end
87
60
  end
88
61
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Query
5
+ class FormObject
6
+ def initialize(model:, params:, namespace:, current_path:)
7
+ @model = model
8
+ @all_params = params.dup
9
+ @namespace = namespace
10
+ @params = params[namespace] || ActionController::Parameters.new
11
+ @path = current_path
12
+ @addons = {}
13
+ end
14
+
15
+ attr_reader :namespace
16
+ attr_reader :path
17
+ attr_reader :addons
18
+
19
+ def add(klass, namespace:, **additional_initialization_arguments)
20
+ instance = klass.new(
21
+ model: @model,
22
+ params: params_dig(namespace),
23
+ **additional_initialization_arguments
24
+ )
25
+
26
+ addons[namespace] = instance
27
+ end
28
+
29
+ def apply_changes(records)
30
+ addons.each_value do |addon|
31
+ records = addon.apply_changes(records)
32
+ end
33
+
34
+ records
35
+ end
36
+
37
+ def to_partial_path
38
+ "query"
39
+ end
40
+
41
+ private
42
+
43
+ def params_dig(*keys)
44
+ @params.dig(*keys) || ActionController::Parameters.new
45
+ end
46
+ end
47
+ end
48
+ end
data/lib/super/schema.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  # The Schema is a general purpose container for describing the "schema"
3
5
  # for various purposes. It primarily exists to provide a cohesive user-facing
@@ -5,30 +7,6 @@ module Super
5
7
  #
6
8
  # The various "schema types" are likely of more interest
7
9
  class Schema
8
- # @param schema_type [Display::SchemaTypes, Form::SchemaTypes]
9
- def initialize(schema_type)
10
- @schema_type = schema_type
11
- @fields = Fields.new
12
-
13
- @schema_type.before_yield(fields: @fields)
14
-
15
- if block_given?
16
- yield(@fields, @schema_type)
17
- end
18
-
19
- @schema_type.after_yield
20
- end
21
-
22
- attr_reader :fields
23
-
24
- def field_keys
25
- fields.keys
26
- end
27
-
28
- def to_partial_path
29
- @schema_type.to_partial_path
30
- end
31
-
32
10
  # This class can be thought of as a Hash, where the keys usually refer to
33
11
  # the model's column name and the value refers to the column type. Note
34
12
  # though that this isn't always the case—different `SchemaTypes` can do
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Schema
5
+ module Common
6
+ def each_attribute_name
7
+ if block_given?
8
+ @fields.keys.each do |key|
9
+ yield(key)
10
+ end
11
+ end
12
+
13
+ enum_for(:each_attribute_name)
14
+ end
15
+
16
+ def each_attribute
17
+ if block_given?
18
+ @fields.each do |key, value|
19
+ yield(key, value)
20
+ end
21
+ end
22
+
23
+ enum_for(:each_attribute)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Schema
5
+ class Guesser
6
+ def initialize(model:, fields:, type:)
7
+ @model = model
8
+ @fields = fields
9
+ @type = type
10
+ @rejects = []
11
+ end
12
+
13
+ def limit
14
+ @limit = yield
15
+ self
16
+ end
17
+
18
+ def reject(&block)
19
+ @rejects.push(block)
20
+ self
21
+ end
22
+
23
+ def ignore_foreign_keys
24
+ @rejects.push(-> (attribute_name) { is_foreign_key[attribute_name] })
25
+ self
26
+ end
27
+
28
+ def assign_type(&block)
29
+ @assign_type = block
30
+ self
31
+ end
32
+
33
+ def call
34
+ result = sorted_attribute_names
35
+ result = @rejects.reduce(result) do |intermediary_result, rejection_proc|
36
+ intermediary_result.reject(&rejection_proc)
37
+ end
38
+
39
+ result = result.first(@limit) if @limit && @limit != Float::INFINITY
40
+
41
+ result.each do |name|
42
+ @fields[name] = @assign_type.call(name)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def sorted_attribute_names
49
+ @model
50
+ .attribute_names
51
+ .sort_by { |name| sort_weight[name] }
52
+ end
53
+
54
+ def sort_weight
55
+ @sort_weight ||= Hash.new do |hash, name|
56
+ hash[name] =
57
+ case name
58
+ when "id" then 0
59
+ when "title" then 1000
60
+ when "name" then 1300
61
+ when "created_at" then 9500
62
+ when "updated_at" then 9750
63
+ else
64
+ 2000 + hash.size
65
+ end
66
+ end
67
+ end
68
+
69
+ def is_foreign_key
70
+ @is_foreign_key ||=
71
+ @model
72
+ .reflect_on_all_associations
73
+ .select { |a| a.macro == :belongs_to }
74
+ .map { |a| [a.foreign_key, true] }
75
+ .to_h
76
+ end
77
+ end
78
+ end
79
+ end