gearhead 0.1.0
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +220 -0
- data/Rakefile +32 -0
- data/app/controllers/gearhead/application_controller.rb +5 -0
- data/app/controllers/gearhead/gears_controller.rb +98 -0
- data/config/routes.rb +9 -0
- data/lib/gearhead.rb +44 -0
- data/lib/gearhead/actions/create.rb +30 -0
- data/lib/gearhead/actions/index.rb +88 -0
- data/lib/gearhead/actions/show.rb +22 -0
- data/lib/gearhead/actions/update.rb +25 -0
- data/lib/gearhead/configuration.rb +68 -0
- data/lib/gearhead/engine.rb +10 -0
- data/lib/gearhead/extensions/actions.rb +29 -0
- data/lib/gearhead/extensions/associations.rb +13 -0
- data/lib/gearhead/extensions/attributes.rb +20 -0
- data/lib/gearhead/extensions/custom_actions.rb +36 -0
- data/lib/gearhead/extensions/enabled_actions.rb +9 -0
- data/lib/gearhead/extensions/finder.rb +14 -0
- data/lib/gearhead/extensions/pagination.rb +26 -0
- data/lib/gearhead/extensions/permitted_params.rb +36 -0
- data/lib/gearhead/extensions/querying.rb +13 -0
- data/lib/gearhead/extensions/scoping.rb +18 -0
- data/lib/gearhead/extensions/serialization.rb +21 -0
- data/lib/gearhead/gear.rb +56 -0
- data/lib/gearhead/gear_lookup.rb +35 -0
- data/lib/gearhead/gearbox.rb +83 -0
- data/lib/gearhead/paginators/lookup.rb +27 -0
- data/lib/gearhead/paginators/paginator.rb +48 -0
- data/lib/gearhead/paginators/pagy_paginator.rb +17 -0
- data/lib/gearhead/paginators/will_paginate_paginator.rb +13 -0
- data/lib/gearhead/params_builder.rb +14 -0
- data/lib/gearhead/registry.rb +26 -0
- data/lib/gearhead/resource_finder.rb +20 -0
- data/lib/gearhead/router.rb +51 -0
- data/lib/gearhead/serializers/active_model_serializers/invalid_request_serializer.rb +7 -0
- data/lib/gearhead/serializers/active_model_serializers/invalid_resource_serializer.rb +7 -0
- data/lib/gearhead/serializers/active_model_serializers/resource_serializer.rb +16 -0
- data/lib/gearhead/serializers/fast_jsonapi/invalid_request_serializer.rb +7 -0
- data/lib/gearhead/serializers/fast_jsonapi/invalid_resource_serializer.rb +7 -0
- data/lib/gearhead/serializers/fast_jsonapi/resource_serializer.rb +16 -0
- data/lib/gearhead/serializers/invalid_request_serializer.rb +24 -0
- data/lib/gearhead/serializers/invalid_resource_serializer.rb +59 -0
- data/lib/gearhead/serializers/lookup.rb +27 -0
- data/lib/gearhead/version.rb +3 -0
- data/lib/generators/gearhead/gear/gear_generator.rb +14 -0
- data/lib/generators/gearhead/gear/templates/gear.rb.erb +3 -0
- data/lib/generators/gearhead/install/install_generator.rb +17 -0
- data/lib/generators/gearhead/install/templates/gearhead.rb.erb +61 -0
- metadata +266 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Gearhead
|
2
|
+
module Extensions
|
3
|
+
module Serialization
|
4
|
+
def self.included(klass)
|
5
|
+
end
|
6
|
+
|
7
|
+
def serializer(klass)
|
8
|
+
@_gear_serializer = klass
|
9
|
+
end
|
10
|
+
|
11
|
+
def serializer_class
|
12
|
+
real_serializer = Serializers::Lookup.for(:resource)
|
13
|
+
if real_serializer.respond_to?(:for)
|
14
|
+
real_serializer.for(self)
|
15
|
+
else
|
16
|
+
real_serializer
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Gearhead
|
2
|
+
module Settings
|
3
|
+
def self.included(klass)
|
4
|
+
klass.extend ClassMethods
|
5
|
+
klass.class_attribute :settings, default: {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def define_gear_setting(name, default = nil)
|
9
|
+
instance_variable_set("@_gear_#{name}", default)
|
10
|
+
ivar = "@_gear_#{name}"
|
11
|
+
self.class.define_method :"_gear_#{name}" do
|
12
|
+
return instance_variable_get(ivar) if instance_variable_defined?(ivar)
|
13
|
+
instance_variable_get("@_gear_#{name}")
|
14
|
+
end
|
15
|
+
self.class.define_method :"_gear_#{name}=" do |value|
|
16
|
+
instance_variable_set("@_gear_#{name}", value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def define_gear_setting(name, default = nil)
|
22
|
+
settings[name] = default
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
class Gear
|
27
|
+
include Settings
|
28
|
+
|
29
|
+
include Extensions::Actions
|
30
|
+
include Extensions::Attributes
|
31
|
+
include Extensions::Associations
|
32
|
+
include Extensions::CustomActions
|
33
|
+
include Extensions::Finder
|
34
|
+
include Extensions::Pagination
|
35
|
+
include Extensions::PermittedParams
|
36
|
+
include Extensions::Querying
|
37
|
+
include Extensions::Scoping
|
38
|
+
include Extensions::Serialization
|
39
|
+
|
40
|
+
attr_reader :resource
|
41
|
+
|
42
|
+
def initialize(resource_class, options = {})
|
43
|
+
@resource_class = resource_class
|
44
|
+
@resource = @resource_class.to_s.constantize
|
45
|
+
@options = options
|
46
|
+
|
47
|
+
self.class.settings.each do |k,v|
|
48
|
+
define_gear_setting k, v
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def path
|
53
|
+
@options[:path] || @resource.model_name.route_key
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Gearhead
|
2
|
+
class GearLookup
|
3
|
+
def self.for(request)
|
4
|
+
new(request).gear
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :request
|
8
|
+
def initialize(request)
|
9
|
+
@request = request
|
10
|
+
end
|
11
|
+
|
12
|
+
# if the resource is already mounted, we don't want to expose it twice. return false
|
13
|
+
# return nil if we don't know the class
|
14
|
+
def gear
|
15
|
+
registered_gear = Gearhead.registry.find(request.params[:resource_class])
|
16
|
+
return registered_gear if registered_gear
|
17
|
+
return nil if inferred_resource_class.nil?
|
18
|
+
return false if Gearhead.registry.for_resource(inferred_resource_class.name)
|
19
|
+
|
20
|
+
default_gear
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def default_gear
|
26
|
+
return nil unless Gearhead.config.automount.enabled?
|
27
|
+
|
28
|
+
Gear.new(inferred_resource_class)
|
29
|
+
end
|
30
|
+
|
31
|
+
def inferred_resource_class
|
32
|
+
request.params[:resource_class].classify.safe_constantize
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Gearhead
|
2
|
+
class Gearbox
|
3
|
+
def initialize
|
4
|
+
end
|
5
|
+
|
6
|
+
def setup!
|
7
|
+
# nothing
|
8
|
+
end
|
9
|
+
|
10
|
+
def prepare!
|
11
|
+
ActiveSupport::Dependencies.autoload_paths -= load_paths
|
12
|
+
Rails.application.config.eager_load_paths -= load_paths
|
13
|
+
attach_reloader
|
14
|
+
end
|
15
|
+
|
16
|
+
# Event that gets triggered on load of Active Admin
|
17
|
+
BeforeLoadEvent = "gearbox.application.before_load".freeze
|
18
|
+
AfterLoadEvent = "gearbox.application.after_load".freeze
|
19
|
+
|
20
|
+
def load!
|
21
|
+
unless loaded?
|
22
|
+
ActiveSupport::Notifications.publish BeforeLoadEvent, self # before_load hook
|
23
|
+
files.each { |file| load file } # load files
|
24
|
+
ActiveSupport::Notifications.publish AfterLoadEvent, self # after_load hook
|
25
|
+
@@loaded = true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def files
|
30
|
+
load_paths.flatten.compact.uniq.flat_map { |path| Dir["#{path}/**/*.rb"] }.sort
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_paths
|
34
|
+
[File.expand_path("app/gears", Rails.root)]
|
35
|
+
end
|
36
|
+
|
37
|
+
def loaded?
|
38
|
+
@@loaded ||= false
|
39
|
+
end
|
40
|
+
|
41
|
+
def unload!
|
42
|
+
@@loaded = false
|
43
|
+
end
|
44
|
+
|
45
|
+
def routes(rails_router)
|
46
|
+
load!
|
47
|
+
Router.new(router: rails_router).apply
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def attach_reloader
|
53
|
+
Rails.application.config.after_initialize do |app|
|
54
|
+
unload_gearhead = -> { Gearhead.gearbox.unload! }
|
55
|
+
|
56
|
+
if app.config.reload_classes_only_on_change
|
57
|
+
ActiveSupport::Reloader.to_prepare(prepend: true, &unload_gearhead)
|
58
|
+
else
|
59
|
+
ActiveSupport::Reloader.to_complete(&unload_gearhead)
|
60
|
+
end
|
61
|
+
|
62
|
+
admin_dirs = {}
|
63
|
+
|
64
|
+
load_paths.each do |path|
|
65
|
+
admin_dirs[path] = [:rb]
|
66
|
+
end
|
67
|
+
|
68
|
+
routes_reloader = app.config.file_watcher.new([], admin_dirs) do
|
69
|
+
app.reload_routes!
|
70
|
+
end
|
71
|
+
|
72
|
+
app.reloaders << routes_reloader
|
73
|
+
|
74
|
+
ActiveSupport::Reloader.to_prepare do
|
75
|
+
unless Gearhead.gearbox.loaded?
|
76
|
+
routes_reloader.execute_if_updated
|
77
|
+
Gearhead.application.load!
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Gearhead
|
2
|
+
module Paginators
|
3
|
+
class Lookup
|
4
|
+
def self.for(action)
|
5
|
+
new(action).paginator
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(action)
|
9
|
+
@action = action.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def paginator
|
13
|
+
"::Gearhead::Paginators::#{adapter}Paginator".constantize
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def adapter_name
|
19
|
+
::Gearhead.config.pagination.adapter
|
20
|
+
end
|
21
|
+
|
22
|
+
def adapter
|
23
|
+
adapter_name.to_s.classify
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Gearhead
|
2
|
+
module Paginators
|
3
|
+
class Paginator
|
4
|
+
def self.paginate(records, action)
|
5
|
+
::Gearhead::Paginators::Lookup.for(action).new(records, action)
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :collection
|
9
|
+
def initialize(records, action)
|
10
|
+
@records = records
|
11
|
+
@collection = nil
|
12
|
+
@action = action
|
13
|
+
end
|
14
|
+
|
15
|
+
# should set @collection
|
16
|
+
def paginate
|
17
|
+
raise NotImplementedError, "define paginate and set @collection"
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
paginate
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def serialization_options
|
26
|
+
{}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def per_page
|
32
|
+
gear._gear_per_page
|
33
|
+
end
|
34
|
+
|
35
|
+
def page
|
36
|
+
request.params[:page] || 1
|
37
|
+
end
|
38
|
+
|
39
|
+
def gear
|
40
|
+
@action.gear
|
41
|
+
end
|
42
|
+
|
43
|
+
def request
|
44
|
+
@action.request
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'pagy/extras/metadata'
|
2
|
+
|
3
|
+
module Gearhead
|
4
|
+
module Paginators
|
5
|
+
class PagyPaginator < Paginator
|
6
|
+
include Pagy::Backend
|
7
|
+
|
8
|
+
def paginate
|
9
|
+
@pagy, @collection = pagy(@records, items: per_page, page: page)
|
10
|
+
end
|
11
|
+
|
12
|
+
def serialization_options
|
13
|
+
pagy_metadata(@pagy)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Gearhead
|
2
|
+
class ParamsBuilder
|
3
|
+
def initialize(page)
|
4
|
+
@page = page
|
5
|
+
end
|
6
|
+
|
7
|
+
def for(action)
|
8
|
+
params = @page.gear.permitted_params(action).map.with_object({}) do |key, obj|
|
9
|
+
obj[key] = @page.request.params[key]
|
10
|
+
end
|
11
|
+
params
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Gearhead
|
2
|
+
class Registry
|
3
|
+
def initialize
|
4
|
+
@resource_map = {}
|
5
|
+
@router = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def register(gear)
|
9
|
+
@resource_map[gear.resource.name] = gear
|
10
|
+
@router[gear.path] = gear
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def for_resource(resource)
|
15
|
+
@resource_map[resource]
|
16
|
+
end
|
17
|
+
|
18
|
+
def find(path)
|
19
|
+
@router[path]
|
20
|
+
end
|
21
|
+
|
22
|
+
def all
|
23
|
+
@resource_map.values
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Gearhead
|
2
|
+
class ResourceFinder
|
3
|
+
def self.for(gear, params)
|
4
|
+
new(gear, params).resource
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(gear, params)
|
8
|
+
@gear = gear
|
9
|
+
@params = params
|
10
|
+
end
|
11
|
+
|
12
|
+
def resource
|
13
|
+
if @gear._gear_finder.present?
|
14
|
+
@gear._gear_finder.call(@params)
|
15
|
+
else
|
16
|
+
@gear.resource.find_by(@gear._gear_param_key => @params[:resource_id])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Gearhead
|
2
|
+
class Router
|
3
|
+
attr_reader :router
|
4
|
+
|
5
|
+
def initialize(router:)
|
6
|
+
@router = router
|
7
|
+
end
|
8
|
+
|
9
|
+
def apply
|
10
|
+
define_gear_routes
|
11
|
+
end
|
12
|
+
|
13
|
+
def define_gear_routes
|
14
|
+
::Gearhead.registry.all.each do |thing|
|
15
|
+
@router.resources thing.resource.model_name.route_key, path: [::Gearhead.config.endpoint, thing.path].join("/"), controller: "gearhead/gears", param: :resource_id, defaults: { resource_class: thing.resource.model_name.route_key } do
|
16
|
+
define_actions(thing)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def define_actions(gear)
|
22
|
+
@router.member do
|
23
|
+
gear._gear_member_actions.values.each do |custom_action|
|
24
|
+
custom_action.verbs.each do |verb|
|
25
|
+
args = [custom_action.name, to: 'gearhead/gears#member_action', defaults: { member_action: custom_action.name }]
|
26
|
+
router.send(verb, *args)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@router.collection do
|
32
|
+
gear._gear_collection_actions.values.each do |custom_action|
|
33
|
+
|
34
|
+
custom_action.verbs.each do |verb|
|
35
|
+
|
36
|
+
args = [custom_action.name, to: 'gearhead/gears#collection_action', defaults: { collection_action: custom_action.name }]
|
37
|
+
router.send(verb, *args)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_action(action)
|
44
|
+
build_route(action.verbs, action.name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_route(verbs, *args)
|
48
|
+
Array.wrap(verbs).each { |verb| router.send(verb, *args) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Gearhead
|
2
|
+
module Serializers
|
3
|
+
module ActiveModelSerializers
|
4
|
+
class ResourceSerializer
|
5
|
+
def self.for(page)
|
6
|
+
if defined?(ActiveModelSerializers::Serializer)
|
7
|
+
klass = Class.new(ActiveModelSerializers::Serializer)
|
8
|
+
klass.attributes *(page._gear_attributes.presence || page.default_attributes)
|
9
|
+
|
10
|
+
klass
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|