super 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +165 -0
  3. data/README.md +102 -8
  4. data/app/assets/config/super_manifest.js +1 -0
  5. data/app/assets/javascripts/super/application.js +3483 -0
  6. data/app/assets/stylesheets/super/application.css +35265 -0
  7. data/app/controllers/super/application_controller.rb +84 -0
  8. data/app/views/layouts/super/application.html.erb +39 -0
  9. data/app/views/super/application/_form.html.erb +13 -0
  10. data/app/views/super/application/_form_field.html.erb +7 -0
  11. data/app/views/super/application/_form_generic_select.html.erb +19 -0
  12. data/app/views/super/application/_form_generic_text.html.erb +7 -0
  13. data/app/views/super/application/_index.html.erb +58 -0
  14. data/app/views/super/application/edit.html.erb +7 -0
  15. data/app/views/super/application/index.html.erb +1 -0
  16. data/app/views/super/application/new.html.erb +7 -0
  17. data/app/views/super/application/show.html.erb +10 -0
  18. data/app/views/super/feather/README.md +32 -0
  19. data/app/views/super/feather/_chevron_down.svg +1 -0
  20. data/frontend/super-frontend/build.js +34 -0
  21. data/frontend/super-frontend/dist/application.css +35265 -0
  22. data/frontend/super-frontend/dist/application.js +3483 -0
  23. data/frontend/super-frontend/package.json +14 -0
  24. data/frontend/super-frontend/postcss.config.js +6 -0
  25. data/frontend/super-frontend/src/javascripts/super/application.js +11 -0
  26. data/frontend/super-frontend/src/stylesheets/super/application.css +14 -0
  27. data/frontend/super-frontend/tailwind.config.js +7 -0
  28. data/frontend/super-frontend/yarn.lock +5355 -0
  29. data/lib/generators/super/install/USAGE +32 -0
  30. data/lib/generators/super/install/install_generator.rb +47 -0
  31. data/lib/generators/super/install/templates/base_controller.rb.tt +2 -0
  32. data/lib/generators/super/install/templates/initializer.rb.tt +5 -0
  33. data/lib/generators/super/resource/USAGE +9 -0
  34. data/lib/generators/super/resource/resource_generator.rb +54 -0
  35. data/lib/generators/super/resource/templates/resource_dashboard.rb.tt +65 -0
  36. data/lib/generators/super/resource/templates/resources_controller.rb.tt +9 -0
  37. data/lib/generators/super/webpacker/USAGE +14 -0
  38. data/lib/generators/super/webpacker/templates/pack_super_application.js.erb.tt +2 -0
  39. data/lib/generators/super/webpacker/webpacker_generator.rb +17 -0
  40. data/lib/super.rb +17 -4
  41. data/lib/super/assets.rb +62 -0
  42. data/lib/super/configuration.rb +88 -0
  43. data/lib/super/controls.rb +25 -0
  44. data/lib/super/display.rb +9 -0
  45. data/lib/super/display/schema_types.rb +41 -0
  46. data/lib/super/engine.rb +5 -0
  47. data/lib/super/error.rb +8 -0
  48. data/lib/super/form/schema_types.rb +47 -0
  49. data/lib/super/inline_callback.rb +82 -0
  50. data/lib/super/navigation/automatic.rb +69 -0
  51. data/lib/super/pagination.rb +59 -0
  52. data/lib/super/plugin.rb +89 -0
  53. data/lib/super/schema.rb +24 -0
  54. data/lib/super/test_support/fixtures/members.yml +336 -0
  55. data/lib/super/test_support/fixtures/ships.yml +10 -0
  56. data/lib/super/test_support/setup.rb +79 -0
  57. data/lib/super/test_support/starfleet_seeder.rb +49 -0
  58. data/lib/super/test_support/templates/20190216224956_create_members.rb +11 -0
  59. data/lib/super/test_support/templates/20190803143320_create_ships.rb +11 -0
  60. data/lib/super/test_support/templates/20190806014121_add_ship_to_members.rb +5 -0
  61. data/lib/super/test_support/templates/admin/members_controller.rb +9 -0
  62. data/lib/super/test_support/templates/admin/ships_controller.rb +9 -0
  63. data/lib/super/test_support/templates/admin_controller.rb +2 -0
  64. data/lib/super/test_support/templates/member.rb +16 -0
  65. data/lib/super/test_support/templates/member_dashboard.rb +90 -0
  66. data/lib/super/test_support/templates/routes.rb +10 -0
  67. data/lib/super/test_support/templates/seeds.rb +2 -0
  68. data/lib/super/test_support/templates/ship.rb +3 -0
  69. data/lib/super/test_support/templates/ship_dashboard.rb +79 -0
  70. data/lib/super/version.rb +1 -1
  71. data/lib/super/view.rb +25 -0
  72. metadata +131 -12
  73. data/LICENSE.txt +0 -40
@@ -0,0 +1,25 @@
1
+ module Super
2
+ class Controls
3
+ def initialize(dashboard)
4
+ @dashboard = dashboard
5
+ end
6
+
7
+ attr_reader :dashboard
8
+
9
+ def method_missing(method_name, *args)
10
+ if @dashboard.respond_to?(method_name)
11
+ @dashboard.public_send(method_name, *args)
12
+ else
13
+ super
14
+ end
15
+ end
16
+
17
+ def respond_to_missing?(method_name, _ = false)
18
+ if @dashboard.respond_to?(method_name)
19
+ true
20
+ else
21
+ super
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ module Super
2
+ class Display
3
+ def self.format(schema, resource, column)
4
+ value = resource.public_send(column)
5
+
6
+ schema.fields[column].present(value)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,41 @@
1
+ module Super
2
+ class Display
3
+ # This schema type is meant to be used for +#index+ or +#show+ actions to
4
+ # transform database fields into something that is human friendly.
5
+ #
6
+ # Note: The constants under "Defined Under Namespace" are considered
7
+ # private.
8
+ #
9
+ # class MemberDashboard
10
+ # # ...
11
+ #
12
+ # def show_schema
13
+ # Super::Schema.new(Super::Display::SchemaTypes.new) do |fields, type|
14
+ # fields[:name] = type.dynamic { |name| name }
15
+ # fields[:rank] = type.dynamic { |rank| rank }
16
+ # fields[:position] = type.dynamic { |position| position }
17
+ # fields[:ship] = type.dynamic { |ship| "#{ship.name} (Ship ##{ship.id})" }
18
+ # fields[:created_at] = type.dynamic { |created_at| created_at.iso8601 }
19
+ # fields[:updated_at] = type.dynamic { |updated_at| updated_at.iso8601 }
20
+ # end
21
+ # end
22
+ #
23
+ # # ...
24
+ # end
25
+ class SchemaTypes
26
+ class Dynamic
27
+ def initialize(transform_block)
28
+ @transform_block = transform_block
29
+ end
30
+
31
+ def present(field)
32
+ @transform_block.call(field)
33
+ end
34
+ end
35
+
36
+ def dynamic(&transform_block)
37
+ Dynamic.new(transform_block)
38
+ end
39
+ end
40
+ end
41
+ end
data/lib/super/engine.rb CHANGED
@@ -1,4 +1,9 @@
1
1
  module Super
2
2
  class Engine < ::Rails::Engine
3
+ initializer "super.assets.precompile" do |app|
4
+ if Super::Assets.sprockets_available?
5
+ app.config.assets.precompile << "config/super_manifest.js"
6
+ end
7
+ end
3
8
  end
4
9
  end
@@ -0,0 +1,8 @@
1
+ module Super
2
+ class Error < StandardError
3
+ class UnconfiguredConfiguration < Error; end
4
+ class InvalidConfiguration < Error; end
5
+ class InvalidStyle < Error; end
6
+ class InvalidPluginArgument < Error; end
7
+ end
8
+ end
@@ -0,0 +1,47 @@
1
+ module Super
2
+ class Form
3
+ # This schema type is used on your +#edit+ and +#new+ forms
4
+ #
5
+ # Note: The constants under "Defined Under Namespace" are considered
6
+ # private.
7
+ #
8
+ # class MemberDashboard
9
+ # # ...
10
+ #
11
+ # def new_schema
12
+ # Super::Schema.new(Super::Form::SchemaTypes.new) do |fields, type|
13
+ # fields[:name] = type.generic("write_type_text")
14
+ # fields[:rank] = type.generic("write_type_select", collection: Member.ranks.keys)
15
+ # fields[:position] = type.generic("write_type_text")
16
+ # fields[:ship_id] = type.generic(
17
+ # "write_type_select",
18
+ # collection: Ship.all.map { |s| ["#{s.name} (Ship ##{s.id})", s.id] },
19
+ # )
20
+ # end
21
+ # end
22
+ #
23
+ # # ...
24
+ # end
25
+ class SchemaTypes
26
+ class Generic
27
+ def initialize(partial_path:, extras:)
28
+ @partial_path = partial_path
29
+ @extras = extras
30
+ end
31
+
32
+ def to_partial_path
33
+ @partial_path
34
+ end
35
+
36
+ def [](key)
37
+ @extras[key]
38
+ end
39
+ end
40
+
41
+ def generic(partial_path, extras = nil)
42
+ extras ||= {}
43
+ Generic.new(partial_path: partial_path, extras: extras)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,82 @@
1
+ module Super
2
+ module InlineCallback
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ register_inline_callback(:yield, on: :index)
7
+ end
8
+
9
+ class_methods do
10
+ def inline_callback_registry
11
+ @inline_callback_registry ||= {}
12
+ end
13
+
14
+ def inline_callback_defined?(action)
15
+ action = action.to_sym
16
+
17
+ inline_callback_registry.key?(action)
18
+ end
19
+
20
+ def inline_callbacks_for(action, check_ancestors = true)
21
+ action = action.to_sym
22
+
23
+ inline_callback_registry[action] ||= {}
24
+
25
+ if check_ancestors
26
+ ancestors.each do |ancestor|
27
+ if ancestor.respond_to?(:inline_callback_defined?)
28
+ if ancestor.inline_callback_defined?(action)
29
+ parent_class_callbacks = ancestor.inline_callbacks_for(action, false)
30
+ inline_callback_registry[action] = parent_class_callbacks.merge(inline_callback_registry[action])
31
+ end
32
+ end
33
+
34
+ if ancestor == Super::ApplicationController
35
+ break
36
+ end
37
+ end
38
+ end
39
+
40
+ inline_callback_registry[action]
41
+ end
42
+
43
+ def register_inline_callback(callback, on:, after: nil)
44
+ action = on.to_sym
45
+ after = after.to_sym if after.respond_to?(:to_sym)
46
+
47
+ callbacks = inline_callbacks_for(action)
48
+ if !callbacks.key?(callback)
49
+ callbacks[callback] = []
50
+ end
51
+
52
+ if after
53
+ callbacks[callback].push(after)
54
+ end
55
+ end
56
+ end
57
+
58
+ def with_inline_callbacks
59
+ action = params[:action].to_sym
60
+
61
+ callbacks = self.class.inline_callbacks_for(action)
62
+
63
+ each_node = -> (&b) { callbacks.each_key(&b) }
64
+ each_child = -> (cb, &b) { callbacks[cb].each(&b) }
65
+
66
+ yield_called = false
67
+
68
+ TSort.tsort_each(each_node, each_child) do |callback|
69
+ if callback == :yield
70
+ yield_called = true
71
+ yield
72
+ else
73
+ send(callback)
74
+ end
75
+ end
76
+
77
+ if !yield_called
78
+ yield
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,69 @@
1
+ module Super
2
+ class Navigation
3
+ class Automatic
4
+ def initialize(route_namespace:)
5
+ route_namespace = route_namespace.to_s
6
+
7
+ if route_namespace.include?("/")
8
+ raise "Can't be nested namespace"
9
+ end
10
+
11
+ @route_namespace = route_namespace
12
+ end
13
+
14
+ def each
15
+ if !block_given?
16
+ return enum_for(:each)
17
+ end
18
+
19
+ navigable_namespace_routes = Rails.application.routes.routes.select do |route|
20
+ path_spec = route.path.spec
21
+
22
+ next if route.verb != "GET"
23
+ next if !path_spec.respond_to?(:right)
24
+ next if path_spec.right.left.to_s != @route_namespace
25
+ next if route.required_parts.any?
26
+ next if route.defaults.empty?
27
+
28
+ true
29
+ end
30
+
31
+ navigable_defaults = navigable_namespace_routes.map do |route|
32
+ controller_name = route.defaults[:controller] + "_controller"
33
+ _controller =
34
+ begin
35
+ controller_name.camelize.constantize
36
+ rescue NameError
37
+ warn("[super] Couldn't find controller: #{controller_name}")
38
+ next
39
+ end
40
+
41
+ route.defaults
42
+ end
43
+
44
+ grouped_navigable_defaults =
45
+ navigable_defaults.compact.uniq.group_by { |d| d[:controller] }
46
+
47
+ grouped_navigable_defaults.each do |controller, defaults|
48
+ actions = defaults.map { |d| [d[:action], d[:action]] }.to_h
49
+ action = actions["index"] || actions["show"]
50
+
51
+ next if action.nil?
52
+
53
+ path =
54
+ begin
55
+ Rails.application.routes.url_for(
56
+ controller: controller,
57
+ action: action,
58
+ only_path: true
59
+ )
60
+ rescue ActionController::UrlGenerationError
61
+ next
62
+ end
63
+
64
+ yield(controller.split("/").last.humanize, path)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,59 @@
1
+ module Super
2
+ class Pagination
3
+ include Enumerable
4
+
5
+ attr_reader :current_pageno
6
+ attr_reader :limit
7
+
8
+ def initialize(total_count:, limit:, query_params:, page_query_param:)
9
+ @total_count = total_count.to_i
10
+ @limit = limit.to_i
11
+ @query_params = query_params
12
+ @page_query_param = page_query_param
13
+
14
+ self.current_pageno = query_params[page_query_param]
15
+ end
16
+
17
+ def offset
18
+ limit * (current_pageno - 1)
19
+ end
20
+
21
+ def current_pageno=(pageno)
22
+ pageno = pageno.to_i
23
+
24
+ @current_pageno =
25
+ if pageno.zero?
26
+ 1
27
+ else
28
+ pageno
29
+ end
30
+ end
31
+
32
+ def each
33
+ if !block_given?
34
+ return enum_for(:each)
35
+ end
36
+
37
+ quotient, remainder = @total_count.divmod(@limit)
38
+ pages =
39
+ if remainder.zero?
40
+ quotient
41
+ else
42
+ quotient + 1
43
+ end
44
+
45
+ (1..pages).each do |pageno|
46
+ is_current_page = pageno == current_pageno
47
+ display = pageno.to_s
48
+ page_query_params = @query_params.dup
49
+ if pageno == 1
50
+ page_query_params.delete(:page)
51
+ else
52
+ page_query_params[@page_query_param] = pageno
53
+ end
54
+
55
+ yield(page_query_params, is_current_page, display)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,89 @@
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)
16
+ end
17
+ end
18
+ end
19
+
20
+ class PluginRegistry
21
+ class << self
22
+ def include_to(name, klass)
23
+ plugin_push(name.to_sym, :include, klass)
24
+ end
25
+
26
+ def prepend_to(name, klass)
27
+ plugin_push(name.to_sym, :prepend, klass)
28
+ end
29
+
30
+ def plugins_for(name)
31
+ name = name.to_sym
32
+
33
+ plugins.fetch(name) { {} }
34
+ end
35
+
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
47
+ end
48
+
49
+ def base_get(name)
50
+ name = name.to_sym
51
+
52
+ bases[name]
53
+ end
54
+
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"
61
+ end
62
+
63
+ if !klass.kind_of?(Module)
64
+ raise Error::InvalidPluginArgument,
65
+ "Received `#{klass}` which must be a module"
66
+ end
67
+
68
+ plugins[name] ||= {}
69
+ plugins[name][klass] = method_name
70
+
71
+ base = base_get(name)
72
+
73
+ if base
74
+ base.public_send(method_name, klass)
75
+ end
76
+
77
+ true
78
+ end
79
+
80
+ def plugins
81
+ @plugins ||= {}
82
+ end
83
+
84
+ def bases
85
+ @bases ||= {}
86
+ end
87
+ end
88
+ end
89
+ end