super 0.0.0 → 0.0.1

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 (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