gearhead 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +220 -0
  4. data/Rakefile +32 -0
  5. data/app/controllers/gearhead/application_controller.rb +5 -0
  6. data/app/controllers/gearhead/gears_controller.rb +98 -0
  7. data/config/routes.rb +9 -0
  8. data/lib/gearhead.rb +44 -0
  9. data/lib/gearhead/actions/create.rb +30 -0
  10. data/lib/gearhead/actions/index.rb +88 -0
  11. data/lib/gearhead/actions/show.rb +22 -0
  12. data/lib/gearhead/actions/update.rb +25 -0
  13. data/lib/gearhead/configuration.rb +68 -0
  14. data/lib/gearhead/engine.rb +10 -0
  15. data/lib/gearhead/extensions/actions.rb +29 -0
  16. data/lib/gearhead/extensions/associations.rb +13 -0
  17. data/lib/gearhead/extensions/attributes.rb +20 -0
  18. data/lib/gearhead/extensions/custom_actions.rb +36 -0
  19. data/lib/gearhead/extensions/enabled_actions.rb +9 -0
  20. data/lib/gearhead/extensions/finder.rb +14 -0
  21. data/lib/gearhead/extensions/pagination.rb +26 -0
  22. data/lib/gearhead/extensions/permitted_params.rb +36 -0
  23. data/lib/gearhead/extensions/querying.rb +13 -0
  24. data/lib/gearhead/extensions/scoping.rb +18 -0
  25. data/lib/gearhead/extensions/serialization.rb +21 -0
  26. data/lib/gearhead/gear.rb +56 -0
  27. data/lib/gearhead/gear_lookup.rb +35 -0
  28. data/lib/gearhead/gearbox.rb +83 -0
  29. data/lib/gearhead/paginators/lookup.rb +27 -0
  30. data/lib/gearhead/paginators/paginator.rb +48 -0
  31. data/lib/gearhead/paginators/pagy_paginator.rb +17 -0
  32. data/lib/gearhead/paginators/will_paginate_paginator.rb +13 -0
  33. data/lib/gearhead/params_builder.rb +14 -0
  34. data/lib/gearhead/registry.rb +26 -0
  35. data/lib/gearhead/resource_finder.rb +20 -0
  36. data/lib/gearhead/router.rb +51 -0
  37. data/lib/gearhead/serializers/active_model_serializers/invalid_request_serializer.rb +7 -0
  38. data/lib/gearhead/serializers/active_model_serializers/invalid_resource_serializer.rb +7 -0
  39. data/lib/gearhead/serializers/active_model_serializers/resource_serializer.rb +16 -0
  40. data/lib/gearhead/serializers/fast_jsonapi/invalid_request_serializer.rb +7 -0
  41. data/lib/gearhead/serializers/fast_jsonapi/invalid_resource_serializer.rb +7 -0
  42. data/lib/gearhead/serializers/fast_jsonapi/resource_serializer.rb +16 -0
  43. data/lib/gearhead/serializers/invalid_request_serializer.rb +24 -0
  44. data/lib/gearhead/serializers/invalid_resource_serializer.rb +59 -0
  45. data/lib/gearhead/serializers/lookup.rb +27 -0
  46. data/lib/gearhead/version.rb +3 -0
  47. data/lib/generators/gearhead/gear/gear_generator.rb +14 -0
  48. data/lib/generators/gearhead/gear/templates/gear.rb.erb +3 -0
  49. data/lib/generators/gearhead/install/install_generator.rb +17 -0
  50. data/lib/generators/gearhead/install/templates/gearhead.rb.erb +61 -0
  51. 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,13 @@
1
+ module Gearhead
2
+ module Paginators
3
+ class WillPaginatePaginator < Paginator
4
+ def paginate
5
+ @collection = @records.paginate(page: page, per_page: per_page)
6
+ end
7
+
8
+ def serialization_options
9
+ abort
10
+ end
11
+ end
12
+ end
13
+ 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,7 @@
1
+ module Gearhead
2
+ module Serializers
3
+ module ActiveModelSerializers
4
+ class InvalidRequestSerializer < ::Gearhead::Serializers::InvalidRequestSerializer; end
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Gearhead
2
+ module Serializers
3
+ module ActiveModelSerializers
4
+ class InvalidResourceSerializer < ::Gearhead::Serializers::InvalidResourceSerializer; end
5
+ end
6
+ end
7
+ 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