super 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,10 +5,14 @@ module Super
5
5
  source_root File.expand_path("templates", __dir__)
6
6
 
7
7
  def copy_the_pack_file
8
- template(
9
- "pack_super_application.js.erb",
10
- "app/javascript/packs/super/application.js.erb"
11
- )
8
+ path =
9
+ if Gem::Dependency.new("webpacker", ">= 6.0.0.beta2", "!= 6.0.0.pre1", "!= 6.0.0.pre2").matching_specs.any?
10
+ "app/packs/entrypoints/super/application.js.erb"
11
+ else
12
+ "app/javascript/packs/super/application.js.erb"
13
+ end
14
+
15
+ template("pack_super_application.js.erb", path)
12
16
  end
13
17
 
14
18
  def set_asset_handler_to_webpacker
@@ -16,7 +20,7 @@ module Super
16
20
  "config/initializers/super.rb",
17
21
  " c.javascripts = Super::Assets.use_webpacker(c.javascripts)\n" \
18
22
  " c.stylesheets = Super::Assets.use_webpacker(c.stylesheets)\n",
19
- before: /\bend\b/
23
+ before: /^end\b/
20
24
  )
21
25
  end
22
26
 
data/lib/super.rb CHANGED
@@ -31,7 +31,8 @@ require "super/form/schema_types"
31
31
  require "super/form/strong_params"
32
32
  require "super/layout"
33
33
  require "super/link"
34
- require "super/navigation/automatic"
34
+ require "super/link_builder"
35
+ require "super/navigation"
35
36
  require "super/pagination"
36
37
  require "super/panel"
37
38
  require "super/partial"
data/lib/super/assets.rb CHANGED
@@ -21,7 +21,7 @@ module Super
21
21
  assets.map do |asset|
22
22
  grep_matches = grep && asset === grep
23
23
  if grep_matches || !grep
24
- asset.instance_variable_set(:@handler, Handler.webpacker)
24
+ asset.send(:handler=, Handler.webpacker)
25
25
  end
26
26
 
27
27
  asset
@@ -34,7 +34,7 @@ module Super
34
34
  assets.map do |asset|
35
35
  grep_matches = grep && asset === grep
36
36
  if grep_matches || !grep
37
- asset.instance_variable_set(:@asset_handler, Handler.sprockets)
37
+ asset.send(:handler=, Handler.sprockets)
38
38
  end
39
39
 
40
40
  asset
@@ -52,6 +52,14 @@ module Super
52
52
  attr_reader :path
53
53
  attr_reader :arguments
54
54
 
55
+ def ==(other)
56
+ return false if handler != other.handler
57
+ return false if path != other.path
58
+ return false if arguments != other.arguments
59
+
60
+ true
61
+ end
62
+
55
63
  def ===(other)
56
64
  return true if path == other
57
65
  return true if other.is_a?(Regexp) && path.match?(other)
@@ -61,6 +69,10 @@ module Super
61
69
 
62
70
  false
63
71
  end
72
+
73
+ private
74
+
75
+ attr_writer :handler
64
76
  end
65
77
 
66
78
  def self.dist(gem_name, package_name)
@@ -74,29 +86,34 @@ module Super
74
86
  end
75
87
 
76
88
  class Handler
77
- def self.auto
78
- @auto ||=
79
- if Gem::Dependency.new("sprockets", "~> 4.0").matching_specs.any?
80
- sprockets
81
- elsif Gem::Dependency.new("sprockets", "~> 3.0").matching_specs.any?
82
- sprockets
83
- elsif Gem::Dependency.new("sprockets", "~> 2.0").matching_specs.any?
84
- sprockets
85
- elsif Gem::Dependency.new("webpacker", "~> 6.0").matching_specs.any?
86
- webpacker
87
- elsif Gem::Dependency.new("webpacker", "~> 5.0").matching_specs.any?
88
- webpacker
89
- elsif Gem::Dependency.new("webpacker", "~> 4.0").matching_specs.any?
90
- webpacker
91
- elsif Gem::Dependency.new("webpacker", "~> 3.0").matching_specs.any?
92
- webpacker
93
- else
94
- none
89
+ class << self
90
+ def auto
91
+ sprockets_spec = gem_specification("sprockets")
92
+ if sprockets_spec
93
+ major = sprockets_spec.version.segments.first
94
+ if major >= 2
95
+ return sprockets
96
+ end
97
+ end
98
+
99
+ webpacker_spec = gem_specification("webpacker")
100
+ if webpacker_spec
101
+ major = webpacker_spec.version.segments.first
102
+ if major >= 4
103
+ return webpacker
104
+ end
95
105
  end
96
- end
97
106
 
98
- def self.sprockets_available?
99
- Gem::Dependency.new("sprockets").matching_specs.any? && defined?(Sprockets)
107
+ none
108
+ end
109
+
110
+ def sprockets_available?
111
+ !gem_specification("sprockets").nil? && defined?(Sprockets)
112
+ end
113
+
114
+ def gem_specification(gem_name)
115
+ Gem::Dependency.new(gem_name).matching_specs&.sort_by(&:version)&.first
116
+ end
100
117
  end
101
118
 
102
119
  def self.sprockets
@@ -115,6 +132,10 @@ module Super
115
132
  @asset_handler = asset_handler
116
133
  end
117
134
 
135
+ def ==(other)
136
+ to_sym == other.to_sym
137
+ end
138
+
118
139
  def sprockets?
119
140
  @asset_handler == :sprockets
120
141
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Cheat
5
+ def controls
6
+ paths = %w[controls.rb controls/optional.rb controls/steps.rb controls/view.rb]
7
+ methods =
8
+ paths
9
+ .map { |f| File.read(File.join(__dir__, f)) }
10
+ .flat_map { |content| content.scan(/^\s+def .*$/) }
11
+ .map { |method| method.strip.sub(/^def /, "#") }
12
+
13
+ puts "== Super::Controls"
14
+ puts methods.join("\n")
15
+ end
16
+ end
17
+ end
@@ -23,5 +23,24 @@ module Super
23
23
  ActiveRecord::Base.sanitize_sql_like(query)
24
24
  end
25
25
  end
26
+
27
+ def polymorphic_path_container
28
+ if Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR == 0
29
+ @polymorphic_path_container ||=
30
+ begin
31
+ klass = Class.new do
32
+ include ActionDispatch::Routing::PolymorphicRoutes
33
+
34
+ def method_missing(method_name)
35
+ Rails.application.routes.url_helpers.public_send(method_name)
36
+ end
37
+ end
38
+
39
+ klass.new
40
+ end
41
+ else
42
+ Rails.application.routes.url_helpers
43
+ end
44
+ end
26
45
  end
27
46
  end
data/lib/super/link.rb CHANGED
@@ -22,57 +22,57 @@ module Super
22
22
 
23
23
  def self.registry
24
24
  @registry ||= {
25
- new: new(
25
+ new: LinkBuilder.new(
26
26
  "New",
27
27
  -> (params:) {
28
- Rails.application.routes.url_for(
28
+ {
29
29
  controller: params[:controller],
30
30
  action: :new,
31
31
  only_path: true
32
- )
32
+ }
33
33
  }
34
34
  ),
35
- index: new(
35
+ index: LinkBuilder.new(
36
36
  "Index",
37
37
  -> (params:) {
38
- Rails.application.routes.url_for(
38
+ {
39
39
  controller: params[:controller],
40
40
  action: :index,
41
41
  only_path: true
42
- )
42
+ }
43
43
  }
44
44
  ),
45
- show: new(
45
+ show: LinkBuilder.new(
46
46
  "View",
47
47
  -> (record:, params:) {
48
- Rails.application.routes.url_for(
48
+ {
49
49
  controller: params[:controller],
50
50
  action: :show,
51
51
  id: record,
52
52
  only_path: true
53
- )
53
+ }
54
54
  }
55
55
  ),
56
- edit: new(
56
+ edit: LinkBuilder.new(
57
57
  "Edit",
58
58
  -> (record:, params:) {
59
- Rails.application.routes.url_for(
59
+ {
60
60
  controller: params[:controller],
61
61
  action: :edit,
62
62
  id: record,
63
63
  only_path: true
64
- )
64
+ }
65
65
  }
66
66
  ),
67
- destroy: new(
67
+ destroy: LinkBuilder.new(
68
68
  "Delete",
69
69
  -> (record:, params:) {
70
- Rails.application.routes.url_for(
70
+ {
71
71
  controller: params[:controller],
72
72
  action: :destroy,
73
73
  id: record,
74
74
  only_path: true
75
- )
75
+ }
76
76
  },
77
77
  method: :delete,
78
78
  data: { confirm: "Really delete?" }
@@ -91,27 +91,33 @@ module Super
91
91
  @options = options
92
92
  end
93
93
 
94
- 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)
95
111
  default_options ||= {}
96
112
  ActionController::Base.helpers.link_to(
97
- value(text, proc_arguments),
98
- value(href, proc_arguments),
99
- default_options.deep_merge(value(options, proc_arguments))
113
+ text,
114
+ href,
115
+ default_options.deep_merge(options)
100
116
  )
101
117
  end
102
118
 
103
- private
104
-
105
- attr_reader :text
106
- attr_reader :href
107
- attr_reader :options
108
-
109
- def value(proc_or_value, proc_arguments)
110
- if proc_or_value.kind_of?(Proc)
111
- proc_or_value.call(**proc_arguments)
112
- else
113
- proc_or_value
114
- end
119
+ def ==(other)
120
+ self.class == other.class && text == other.text && href == other.href && options == other.options
115
121
  end
116
122
  end
117
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