gearhead 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|