super 0.0.9 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +0 -9
  3. data/README.md +40 -52
  4. data/app/assets/javascripts/super/application.js +5671 -3669
  5. data/app/assets/stylesheets/super/application.css +114693 -71486
  6. data/app/controllers/super/application_controller.rb +24 -13
  7. data/app/helpers/super/form_builder_helper.rb +25 -0
  8. data/app/views/layouts/super/application.html.erb +15 -14
  9. data/app/views/super/application/{_super_schema_display_actions.html.erb → _display_actions.html.erb} +0 -0
  10. data/app/views/super/application/{_super_schema_display_index.html.erb → _display_index.html.erb} +5 -5
  11. data/app/views/super/application/_display_show.html.erb +8 -0
  12. data/app/views/super/application/_filter.html.erb +5 -13
  13. data/app/views/super/application/_filter_type_select.html.erb +9 -19
  14. data/app/views/super/application/_filter_type_text.html.erb +7 -11
  15. data/app/views/super/application/_filter_type_timestamp.html.erb +6 -17
  16. data/app/views/super/application/_form.html.erb +15 -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 -15
  19. data/app/views/super/application/_form_field_flatpickr_date.html.erb +8 -0
  20. data/app/views/super/application/_form_field_flatpickr_datetime.html.erb +8 -0
  21. data/app/views/super/application/_form_field_flatpickr_time.html.erb +8 -0
  22. data/app/views/super/application/_form_field_rich_text_area.html.erb +1 -13
  23. data/app/views/super/application/_form_field_select.html.erb +1 -23
  24. data/app/views/super/application/_form_field_text.html.erb +1 -13
  25. data/app/views/super/application/{_super_layout.html.erb → _layout.html.erb} +8 -8
  26. data/app/views/super/application/{_super_pagination.html.erb → _pagination.html.erb} +1 -1
  27. data/app/views/super/application/{_super_panel.html.erb → _panel.html.erb} +2 -2
  28. data/app/views/super/application/_query.html.erb +18 -0
  29. data/app/views/super/application/_sort.html.erb +18 -0
  30. data/app/views/super/application/_sort_expression.html.erb +25 -0
  31. data/app/views/super/application/edit.html.erb +1 -0
  32. data/app/views/super/application/index.html.erb +1 -0
  33. data/app/views/super/application/new.html.erb +1 -0
  34. data/app/views/super/application/show.html.erb +1 -0
  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/frontend/super-frontend/dist/application.css +114693 -71486
  39. data/frontend/super-frontend/dist/application.js +5671 -3669
  40. data/lib/generators/super/action_text/action_text_generator.rb +2 -0
  41. data/lib/generators/super/install/install_generator.rb +18 -7
  42. data/lib/generators/super/install/templates/base_controller.rb.tt +9 -1
  43. data/lib/generators/super/install/templates/initializer.rb.tt +9 -2
  44. data/lib/generators/super/resource/resource_generator.rb +107 -30
  45. data/lib/generators/super/resource/templates/resources_controller.rb.tt +3 -9
  46. data/lib/generators/super/webpacker/webpacker_generator.rb +11 -5
  47. data/lib/super.rb +9 -2
  48. data/lib/super/action_inquirer.rb +2 -0
  49. data/lib/super/assets.rb +46 -23
  50. data/lib/super/cheat.rb +17 -0
  51. data/lib/super/client_error.rb +2 -0
  52. data/lib/super/compatibility.rb +21 -0
  53. data/lib/super/configuration.rb +16 -24
  54. data/lib/super/controls.rb +11 -2
  55. data/lib/super/controls/optional.rb +35 -1
  56. data/lib/super/controls/steps.rb +27 -35
  57. data/lib/super/controls/view.rb +55 -0
  58. data/lib/super/display.rb +29 -13
  59. data/lib/super/display/guesser.rb +4 -0
  60. data/lib/super/display/schema_types.rb +73 -33
  61. data/lib/super/engine.rb +4 -0
  62. data/lib/super/error.rb +21 -0
  63. data/lib/super/filter.rb +2 -0
  64. data/lib/super/filter/form_object.rb +5 -8
  65. data/lib/super/filter/guesser.rb +2 -0
  66. data/lib/super/filter/operator.rb +2 -0
  67. data/lib/super/filter/schema_types.rb +2 -0
  68. data/lib/super/form.rb +3 -1
  69. data/lib/super/form/builder.rb +289 -39
  70. data/lib/super/form/guesser.rb +12 -1
  71. data/lib/super/form/inline_errors.rb +28 -0
  72. data/lib/super/form/schema_types.rb +15 -0
  73. data/lib/super/form/strong_params.rb +2 -0
  74. data/lib/super/layout.rb +3 -1
  75. data/lib/super/link.rb +44 -31
  76. data/lib/super/link_builder.rb +58 -0
  77. data/lib/super/navigation.rb +164 -0
  78. data/lib/super/pagination.rb +3 -1
  79. data/lib/super/panel.rb +3 -1
  80. data/lib/super/partial.rb +2 -0
  81. data/lib/super/partial/resolving.rb +2 -0
  82. data/lib/super/plugin.rb +2 -0
  83. data/lib/super/query/form_object.rb +48 -0
  84. data/lib/super/schema.rb +2 -0
  85. data/lib/super/schema/common.rb +2 -0
  86. data/lib/super/schema/guesser.rb +2 -0
  87. data/lib/super/sort.rb +110 -0
  88. data/lib/super/useful/builder.rb +25 -0
  89. data/lib/super/useful/enum.rb +63 -0
  90. data/lib/super/version.rb +3 -1
  91. data/lib/super/view_helper.rb +2 -19
  92. data/lib/tasks/super/cheat.rake +9 -0
  93. metadata +53 -40
  94. data/CONTRIBUTING.md +0 -56
  95. data/Rakefile +0 -34
  96. data/STABILITY.md +0 -50
  97. data/app/views/super/application/_form_field_generic.html.erb +0 -19
  98. data/app/views/super/application/_form_inline_errors.html.erb +0 -10
  99. data/app/views/super/application/_super_schema_display_show.html.erb +0 -8
  100. data/app/views/super/application/_super_schema_form.html.erb +0 -15
  101. data/docs/README.md +0 -8
  102. data/docs/action_text.md +0 -48
  103. data/docs/faq.md +0 -44
  104. data/docs/installation.md +0 -21
  105. data/docs/quick_start.md +0 -30
  106. data/docs/webpacker.md +0 -25
  107. data/docs/yard_customizations.rb +0 -41
  108. data/frontend/super-frontend/build.js +0 -36
  109. data/frontend/super-frontend/package.json +0 -20
  110. data/frontend/super-frontend/postcss.config.js +0 -6
  111. data/frontend/super-frontend/src/javascripts/super/application.js +0 -15
  112. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.js +0 -17
  113. data/frontend/super-frontend/src/javascripts/super/toggle_pending_destruction_controller.js +0 -15
  114. data/frontend/super-frontend/src/stylesheets/super/application.css +0 -77
  115. data/frontend/super-frontend/tailwind.config.js +0 -15
  116. data/frontend/super-frontend/yarn.lock +0 -5443
  117. data/lib/super/controls/required.rb +0 -13
  118. data/lib/super/filter/plugin.rb +0 -47
  119. data/lib/super/navigation/automatic.rb +0 -71
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  class Form
3
5
  class Guesser
@@ -20,7 +22,16 @@ module Super
20
22
  private
21
23
 
22
24
  def attribute_type_for(attribute_name)
23
- @type.string
25
+ case @model.type_for_attribute(attribute_name).type
26
+ when :datetime
27
+ @type.flatpickr_datetime
28
+ when :time
29
+ @type.flatpickr_time
30
+ when :date
31
+ @type.flatpickr_date
32
+ else
33
+ @type.string
34
+ end
24
35
  end
25
36
  end
26
37
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Form
5
+ module InlineErrors
6
+ module_function
7
+
8
+ def error_messages(model_instance, column_or_association)
9
+ errable_fields(model_instance, column_or_association)
10
+ .flat_map { |field| Compatability.errable_fields(field) }
11
+ .flat_map { |field| model_instance.errors.full_messages_for(field) }
12
+ .uniq
13
+ end
14
+
15
+ def errable_fields(model_instance, column_or_association)
16
+ column_or_association = column_or_association.to_s
17
+ reflection = model_instance.class.reflect_on_association(column_or_association)
18
+ reflection ||= model_instance.class.reflections.values.find { |r| r.foreign_key == column_or_association }
19
+
20
+ if reflection
21
+ [reflection.name.to_s, reflection.foreign_key.to_s]
22
+ else
23
+ [column_or_association]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  class Form
3
5
  class SchemaTypes
@@ -9,6 +11,7 @@ module Super
9
11
  end
10
12
 
11
13
  attr_reader :nested_fields
14
+ attr_reader :extras
12
15
 
13
16
  def each_attribute
14
17
  if block_given?
@@ -84,6 +87,18 @@ module Super
84
87
  Generic.new(partial_path: "form_field_checkbox", extras: extras, nested: {})
85
88
  end
86
89
 
90
+ def flatpickr_date(**extras)
91
+ Generic.new(partial_path: "form_field_flatpickr_date", extras: extras, nested: {})
92
+ end
93
+
94
+ def flatpickr_datetime(**extras)
95
+ Generic.new(partial_path: "form_field_flatpickr_datetime", extras: extras, nested: {})
96
+ end
97
+
98
+ def flatpickr_time(**extras)
99
+ Generic.new(partial_path: "form_field_flatpickr_time", extras: extras, nested: {})
100
+ end
101
+
87
102
  def has_many(reader, **extras)
88
103
  nested = @fields.nested do
89
104
  yield
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Super
2
4
  class Form
3
5
  class StrongParams
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
@@ -17,7 +19,7 @@ module Super
17
19
  attr_reader :footers
18
20
 
19
21
  def to_partial_path
20
- "super_layout"
22
+ "layout"
21
23
  end
22
24
 
23
25
  def resolve(template)
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
@@ -20,57 +22,57 @@ module Super
20
22
 
21
23
  def self.registry
22
24
  @registry ||= {
23
- new: new(
25
+ new: LinkBuilder.new(
24
26
  "New",
25
27
  -> (params:) {
26
- Rails.application.routes.url_for(
28
+ {
27
29
  controller: params[:controller],
28
30
  action: :new,
29
31
  only_path: true
30
- )
32
+ }
31
33
  }
32
34
  ),
33
- index: new(
35
+ index: LinkBuilder.new(
34
36
  "Index",
35
37
  -> (params:) {
36
- Rails.application.routes.url_for(
38
+ {
37
39
  controller: params[:controller],
38
40
  action: :index,
39
41
  only_path: true
40
- )
42
+ }
41
43
  }
42
44
  ),
43
- show: new(
45
+ show: LinkBuilder.new(
44
46
  "View",
45
47
  -> (record:, params:) {
46
- Rails.application.routes.url_for(
48
+ {
47
49
  controller: params[:controller],
48
50
  action: :show,
49
51
  id: record,
50
52
  only_path: true
51
- )
53
+ }
52
54
  }
53
55
  ),
54
- edit: new(
56
+ edit: LinkBuilder.new(
55
57
  "Edit",
56
58
  -> (record:, params:) {
57
- Rails.application.routes.url_for(
59
+ {
58
60
  controller: params[:controller],
59
61
  action: :edit,
60
62
  id: record,
61
63
  only_path: true
62
- )
64
+ }
63
65
  }
64
66
  ),
65
- destroy: new(
67
+ destroy: LinkBuilder.new(
66
68
  "Delete",
67
69
  -> (record:, params:) {
68
- Rails.application.routes.url_for(
70
+ {
69
71
  controller: params[:controller],
70
72
  action: :destroy,
71
73
  id: record,
72
74
  only_path: true
73
- )
75
+ }
74
76
  },
75
77
  method: :delete,
76
78
  data: { confirm: "Really delete?" }
@@ -78,33 +80,44 @@ module Super
78
80
  }
79
81
  end
80
82
 
83
+ def self.polymorphic_parts(*parts_tail)
84
+ parts_head = Super.configuration.path.strip.gsub(%r{\A/+}, "").gsub(%r{/+\z}, "").strip.split("/")
85
+ parts_head + parts_tail
86
+ end
87
+
81
88
  def initialize(text, href, **options)
82
89
  @text = text
83
90
  @href = href
84
91
  @options = options
85
92
  end
86
93
 
87
- def to_s(default_options: nil, **proc_arguments)
94
+ attr_reader :text
95
+ attr_reader :options
96
+
97
+ def href
98
+ if @href.is_a?(String)
99
+ return @href
100
+ end
101
+
102
+ if @href.is_a?(Hash)
103
+ @href = Rails.application.routes.url_for(**@href)
104
+ return @href
105
+ end
106
+
107
+ @href = Super::Compatability.polymorphic_path_container.polymorphic_path(@href)
108
+ end
109
+
110
+ def to_s(default_options: nil)
88
111
  default_options ||= {}
89
112
  ActionController::Base.helpers.link_to(
90
- value(text, proc_arguments),
91
- value(href, proc_arguments),
92
- default_options.deep_merge(value(options, proc_arguments))
113
+ text,
114
+ href,
115
+ default_options.deep_merge(options)
93
116
  )
94
117
  end
95
118
 
96
- private
97
-
98
- attr_reader :text
99
- attr_reader :href
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
119
+ def ==(other)
120
+ self.class == other.class && text == other.text && href == other.href && options == other.options
108
121
  end
109
122
  end
110
123
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class LinkBuilder
5
+ def initialize(text, href, **options)
6
+ @text = text
7
+ @href = href
8
+ @options = options
9
+ @requirements = []
10
+ @requirements += gather_requirements(text)
11
+ @requirements += gather_requirements(href)
12
+ @requirements += gather_requirements(options)
13
+
14
+ unknown_arguments = @requirements - known_requirements
15
+ if unknown_arguments.any?
16
+ raise Error::ArgumentError, "Unknown arguments: #{unknown_arguments.join(", ")}"
17
+ end
18
+ end
19
+
20
+ attr_reader :requirements
21
+
22
+ def to_s(default_options: nil, **kwargs)
23
+ resolve(**kwargs).to_s(default_options: default_options)
24
+ end
25
+
26
+ def resolve(**kwargs)
27
+ Link.new(
28
+ into_value(@text, kwargs),
29
+ into_value(@href, kwargs),
30
+ **into_value(@options, kwargs),
31
+ )
32
+ end
33
+
34
+ private
35
+
36
+ def known_requirements
37
+ %i[params record].freeze
38
+ end
39
+
40
+ def gather_requirements(value_or_proc)
41
+ return [] if !value_or_proc.is_a?(Proc)
42
+
43
+ requirements =
44
+ value_or_proc
45
+ .parameters
46
+ .select { |(kind, name)| kind = :keyreq }
47
+ .map(&:last)
48
+ end
49
+
50
+ def into_value(value_or_proc, kwargs)
51
+ if value_or_proc.kind_of?(Proc)
52
+ value_or_proc.call(**kwargs)
53
+ else
54
+ value_or_proc
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Navigation
5
+ def initialize
6
+ @builder = Builder.new
7
+ @definition = yield @builder
8
+ if !@definition.is_a?(Array)
9
+ @definition = [@definition]
10
+ end
11
+ end
12
+
13
+ def definition
14
+ return @defs if instance_variable_defined?(:@defs)
15
+
16
+ searcher = RouteFormatterButReallySearcher.new
17
+ inspector = ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes)
18
+ inspector.format(searcher)
19
+ all_matches = searcher.matches
20
+ unused_matches = all_matches.each_with_object({}) { |match, hash| hash[match] = true }
21
+
22
+ defs = expand_proc_syntax_sugar(@definition)
23
+ defs = validate_and_determine_explicit_links(defs, unused_matches)
24
+ @defs = expand_directives(defs, all_matches, unused_matches.keys)
25
+ end
26
+
27
+ private
28
+
29
+ # This expands the syntax sugar that allows `nav.menu("Name")[nav.link(Item)]`
30
+ def expand_proc_syntax_sugar(definition)
31
+ definition.map do |link_or_menu_or_rest_or_menuproc|
32
+ link_or_menu_or_rest =
33
+ if link_or_menu_or_rest_or_menuproc.is_a?(Proc)
34
+ link_or_menu_or_rest_or_menuproc.call
35
+ else
36
+ link_or_menu_or_rest_or_menuproc
37
+ end
38
+
39
+ if link_or_menu_or_rest.is_a?(Menu)
40
+ link_or_menu_or_rest.links = link_or_menu_or_rest.links.map do |menu_item|
41
+ if menu_item.is_a?(Proc)
42
+ menu_item.call
43
+ else
44
+ menu_item
45
+ end
46
+ end
47
+ end
48
+
49
+ link_or_menu_or_rest
50
+ end
51
+ end
52
+
53
+ def validate_and_determine_explicit_links(definition, unused_links)
54
+ definition.each do |link_or_menu_or_rest|
55
+ if link_or_menu_or_rest.is_a?(Super::Link)
56
+ unused_links.delete(link_or_menu_or_rest.href)
57
+ elsif link_or_menu_or_rest.is_a?(Menu)
58
+ link_or_menu_or_rest.links.each do |link_or_rest|
59
+ if link_or_rest.is_a?(Menu)
60
+ raise Super::Error::ArgumentError, "Navigation menus can't be nested"
61
+ end
62
+
63
+ if link_or_rest.is_a?(Link)
64
+ unused_links.delete(link_or_rest.href)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def expand_directives(defs, all_hrefs, rest_hrefs)
72
+ defs.flat_map do |link_or_menu_or_rest|
73
+ if link_or_menu_or_rest.is_a?(Menu)
74
+ link_or_menu_or_rest.links = link_or_menu_or_rest.links.flat_map do |link_or_rest|
75
+ if link_or_rest == ALL
76
+ linkify_hrefs(all_hrefs)
77
+ elsif link_or_rest == REST
78
+ linkify_hrefs(rest_hrefs)
79
+ else
80
+ link_or_rest
81
+ end
82
+ end
83
+
84
+ link_or_menu_or_rest
85
+ elsif link_or_menu_or_rest == ALL
86
+ linkify_hrefs(all_hrefs)
87
+ elsif link_or_menu_or_rest == REST
88
+ linkify_hrefs(rest_hrefs)
89
+ else
90
+ link_or_menu_or_rest
91
+ end
92
+ end
93
+ end
94
+
95
+ def linkify_hrefs(hrefs)
96
+ hrefs.map do |href|
97
+ Super::Link.new(href.split("/").last.humanize, href)
98
+ end
99
+ end
100
+
101
+ ALL = Object.new
102
+ REST = Object.new
103
+ Menu = Struct.new(:title, :links)
104
+
105
+ class Builder
106
+ def link(model, **kwargs)
107
+ text = model.model_name.human.pluralize
108
+ parts = Super::Link.polymorphic_parts(model)
109
+
110
+ Super::Link.new(text, parts, **kwargs)
111
+ end
112
+
113
+ def link_to(*args, **kwargs)
114
+ Super::Link.new(*args, **kwargs)
115
+ end
116
+
117
+ def menu(title, *links)
118
+ menu = Menu.new(title, links)
119
+ proc do |*more_links|
120
+ menu.links += more_links
121
+ menu
122
+ end
123
+ end
124
+
125
+ def rest
126
+ REST
127
+ end
128
+
129
+ def all
130
+ ALL
131
+ end
132
+ end
133
+
134
+ class RouteFormatterButReallySearcher
135
+ def initialize(route_namespace: Super.configuration.path)
136
+ @route_namespace = route_namespace.strip.gsub(%r{\A/+}, "").gsub(%r{/+\z}, "").strip
137
+ @route_namespace = "/#{@route_namespace}/"
138
+ @matches = []
139
+ end
140
+
141
+ def matches
142
+ @matches.map do |route|
143
+ route[:path].sub(/\(.*\)\Z/, "")
144
+ end
145
+ end
146
+
147
+ def section(routes)
148
+ @matches += routes.select do |route|
149
+ next false unless route[:verb] == "GET" || route[:verb] == ""
150
+ next false unless route[:path].start_with?(@route_namespace)
151
+ next false if route[:path].include?("/:")
152
+ next false if route[:reqs].end_with?("#new")
153
+
154
+ true
155
+ end
156
+ end
157
+
158
+ def header(routes); end
159
+ def no_routes(routes, filter); end
160
+ def result; end
161
+ def section_title(title); end
162
+ end
163
+ end
164
+ end