gearhead 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b2840550719a44e41f271d9dd420ac4c848f5a914214e1125ccae0db63f0e460
4
- data.tar.gz: 4efb4442da94ebdd53b4d55d865866cdf516315906ed7df74c090f46b8071db0
3
+ metadata.gz: 8da15acd51e6492411698015103ce3dc8f35e47f9c209b65624aa5d74d876141
4
+ data.tar.gz: 1f13b739558ced09c7b145c0e00a40557a07437b1e058ccc76074889cb242d2c
5
5
  SHA512:
6
- metadata.gz: 5c0c1f1fa96578021c7e52653f689442a35f840f5e883f69ac0fc0f1a07fcb8fdc7efbd9b9857a30ea457c59860cb38806678678d67c088a7b2d3c5dcac00e37
7
- data.tar.gz: bdc1d6d0d3b5b93f8fbf7c727a80141720036960d3d162475e04b3ebbbdd29e79e5b0a2f74fe05550f06608fe0c797fc0744465e1a81ddcc4243a93c494c65f6
6
+ metadata.gz: b4257c5b8dbf26a9c5b306f07ba1ca20f37fa3c6bc56ff75424067a67aaa98a1473ee6f37c28bb5908d8c73849cbe6d988d6bc82da9d3a0cd60a75001f869899
7
+ data.tar.gz: 15167236c5ecb3bf004650d12ace55a98548f678471cfd371688377357cdd72c656d431b37c6cde9221031e167133125e92226190aef4b52eb9e964f31ab2aa5
data/README.md CHANGED
@@ -1,6 +1,11 @@
1
- # Gearhead test application
1
+ # Gearhead
2
2
 
3
- Gearhead turns your database into a RESTful API. It's like if ActiveAdmin and Grape had a baby.
3
+ Gearhead turns your database into a RESTful API. It's like if ActiveAdmin, InheritedResources, and Grape had a baby.
4
+
5
+ ## Purpose
6
+
7
+ For internal projects I was always standing up API endpoints that were separate from my regular controllers. The boilerplate
8
+ got old real fast. This eliminates boilerplate and leaves enough configuration for you to make it your own.
4
9
 
5
10
  ## Installation
6
11
 
@@ -28,7 +33,7 @@ app/gears/.keep
28
33
  and modifies your routes:
29
34
 
30
35
  ```ruby
31
- Gearbox.routes(self)
36
+ Gearhead.routes(self)
32
37
  ```
33
38
 
34
39
  ## Configuration
@@ -177,10 +182,22 @@ Just define what attributes you want exposed:
177
182
 
178
183
  ```ruby
179
184
  Gearhead.register Post do
180
- attributes :id, :name
185
+ attributes :id, :title
181
186
  end
182
187
  ```
183
188
 
189
+ And customize one-off attributes:
190
+
191
+ ```ruby
192
+ Gearhead.register Post do
193
+ attributes :id, :title
194
+
195
+ attribute :excerpt do |resource|
196
+ resource.content.first(100)
197
+ end
198
+ end
199
+ ```
200
+
184
201
  ### Finding resources
185
202
 
186
203
  Change your finder:
@@ -193,6 +210,12 @@ Gearhead.register Post do
193
210
  end
194
211
  ```
195
212
 
213
+ ### Querying
214
+
215
+ Uses Ransack under the hood. Just send the normal `q` in the query, like:
216
+
217
+ `GET /gearhead/posts?q[user_id]=1`
218
+
196
219
  ### Permitting params
197
220
 
198
221
  Works just like the params you're used to:
@@ -21,7 +21,7 @@ module Gearhead
21
21
  @collection = apply_query
22
22
  @collection = apply_pagination
23
23
  @collection = apply_serializer
24
- @collection
24
+ @collection.to_json
25
25
  end
26
26
 
27
27
  private
@@ -70,7 +70,7 @@ module Gearhead
70
70
  end
71
71
 
72
72
  def apply_serializer
73
- @gear.serializer_class.new(@collection, serialization_options)
73
+ @gear.collection_serializer.for(@collection, @gear.serializer_class, serialization_options)
74
74
  end
75
75
 
76
76
  def serialization_options
@@ -6,5 +6,7 @@ module Gearhead
6
6
  end
7
7
  Rails.autoloaders.main.ignore(Rails.root.join('app/gears'))
8
8
  end
9
+
10
+ paths["app/controllers"] = "lib"
9
11
  end
10
12
  end
@@ -2,6 +2,7 @@ module Gearhead
2
2
  module Extensions
3
3
  module Attributes
4
4
  def self.included(klass)
5
+ klass.define_gear_setting :custom_attributes, []
5
6
  end
6
7
 
7
8
  def _gear_attributes
@@ -15,6 +16,10 @@ module Gearhead
15
16
  def default_attributes
16
17
  @resource.columns_hash.keys.map(&:to_sym)
17
18
  end
19
+
20
+ def attribute(name, &block)
21
+ @_gear_custom_attributes << [name, block]
22
+ end
18
23
  end
19
24
  end
20
25
  end
@@ -2,6 +2,11 @@ module Gearhead
2
2
  module Extensions
3
3
  module Serialization
4
4
  def self.included(klass)
5
+ klass.define_gear_setting :serializer_adapter, Gearhead.config.serialization.adapter
6
+ end
7
+
8
+ def serializer_adapter(adapter)
9
+ @_gear_serializer_adapter = adapter
5
10
  end
6
11
 
7
12
  def serializer(klass)
@@ -9,13 +14,17 @@ module Gearhead
9
14
  end
10
15
 
11
16
  def serializer_class
12
- real_serializer = Serializers::Lookup.for(:resource)
17
+ real_serializer = Serializers::Lookup.for(:resource, @_gear_serializer_adapter)
13
18
  if real_serializer.respond_to?(:for)
14
19
  real_serializer.for(self)
15
20
  else
16
21
  real_serializer
17
22
  end
18
23
  end
24
+
25
+ def collection_serializer
26
+ Serializers::Lookup.for(:collection, @_gear_serializer_adapter)
27
+ end
19
28
  end
20
29
  end
21
30
  end
@@ -1,11 +1,8 @@
1
1
  module Gearhead
2
2
  class Gearbox
3
- def initialize
4
- end
3
+ def initialize; end
5
4
 
6
- def setup!
7
- # nothing
8
- end
5
+ def setup!; end
9
6
 
10
7
  def prepare!
11
8
  ActiveSupport::Dependencies.autoload_paths -= load_paths
@@ -54,7 +51,7 @@ module Gearhead
54
51
  unload_gearhead = -> { Gearhead.gearbox.unload! }
55
52
 
56
53
  if app.config.reload_classes_only_on_change
57
- ActiveSupport::Reloader.to_prepare(prepend: true, &unload_gearhead)
54
+ ActiveSupport::Reloader.to_prepare(prepend: true, &unload_gearhead)
58
55
  else
59
56
  ActiveSupport::Reloader.to_complete(&unload_gearhead)
60
57
  end
@@ -72,9 +69,9 @@ module Gearhead
72
69
  app.reloaders << routes_reloader
73
70
 
74
71
  ActiveSupport::Reloader.to_prepare do
75
- unless Gearhead.gearbox.loaded?
72
+ unless Gearhead.gearbox.loaded?
76
73
  routes_reloader.execute_if_updated
77
- Gearhead.application.load!
74
+ Gearhead.gearbox.load!
78
75
  end
79
76
  end
80
77
  end
@@ -1,16 +1,14 @@
1
- # todo:
2
- if defined?(ActiveModelSerializers)
3
- ActiveModelSerializers.config.serializer_lookup_enabled = false
4
- end
1
+ # frozen_string_literal: true
5
2
 
3
+ ActiveModelSerializers.config.serializer_lookup_enabled = false
6
4
  module Gearhead
7
5
  class GearsController < ::Gearhead.config.base_controller.constantize
8
6
  before_action :find_gear!
9
- before_action :ensure_action_enabled!, only: [:index, :create, :show, :update, :destroy]
10
- before_action :find_resource!, except: [:index, :create, :collection_action]
7
+ before_action :ensure_action_enabled!, only: %i[index create show update destroy]
8
+ before_action :find_resource!, except: %i[index create collection_action]
11
9
 
12
10
  def index
13
- render json: Gearhead::Actions::Index.build(@gear, request), adapter: false
11
+ render json: Gearhead::Actions::Index.build(@gear, request)
14
12
  end
15
13
 
16
14
  def create
@@ -18,7 +16,7 @@ module Gearhead
18
16
  if @resource.save
19
17
  render json: @gear.serializer_class.new(@resource)
20
18
  else
21
- render json: { errors: @resource.errors }
19
+ render json: { errors: @resource.errors }, status: :unprocessable_entity
22
20
  end
23
21
  end
24
22
 
@@ -32,7 +30,7 @@ module Gearhead
32
30
  if @resource.save
33
31
  render json: @gear.serializer_class.new(@resource)
34
32
  else
35
- render json: { errors: @resource.errors }
33
+ render json: { errors: @resource.errors }, status: :unprocessable_entity
36
34
  end
37
35
  end
38
36
 
@@ -40,21 +38,21 @@ module Gearhead
40
38
  if @resource.destroy
41
39
  render json: @gear.serializer_class.new(resource)
42
40
  else
43
- render json: { errors: resource.errors }
41
+ render json: { errors: @resource.errors }, status: :unprocessable_entity
44
42
  end
45
43
  end
46
44
 
47
45
  def member_action
48
46
  action = @gear._gear_member_actions[params[:member_action].to_sym]
49
- if action && action.verbs.include?(request.request_method.to_sym.downcase)
50
- return render json: instance_exec(&action.block)
47
+ if action&.verbs&.include?(request.request_method.to_sym.downcase)
48
+ render json: instance_exec(&action.block)
51
49
  end
52
50
  end
53
51
 
54
52
  def collection_action
55
53
  action = @gear._gear_collection_actions[params[:collection_action]]
56
- if action && action.verbs.include?(request.request_method.to_sym.downcase)
57
- return render json: instance_exec(&action.block)
54
+ if action&.verbs&.include?(request.request_method.to_sym.downcase)
55
+ render json: instance_exec(&action.block)
58
56
  end
59
57
  end
60
58
 
@@ -64,35 +62,31 @@ module Gearhead
64
62
  @gear = Gearhead.gear_for(request)
65
63
  return @gear if @gear
66
64
 
67
- if @gear.nil?
68
- error!("Can't find or infer gear.", 404)
69
- end
70
- if @gear == false
71
- error!("Gear already mounted somewhere else.", 500)
72
- end
65
+ error!("Can't find or infer gear.", 404) if @gear.nil?
66
+ error!('Gear already mounted somewhere else.', 500) if @gear == false
73
67
  end
74
68
 
75
69
  # remember that request method is from rack so GET/POST/etc.
76
70
  def check_collection_actions!
77
71
  action = @gear._gear_collection_actions[request.request_method][params[:resource_id].to_sym]
78
- if action
79
- return render json: instance_exec(&action)
80
- end
72
+ return render json: instance_exec(&action) if action
81
73
  end
82
74
 
83
75
  def find_resource!
84
76
  @resource = ::Gearhead::ResourceFinder.for(@gear, params)
85
- error!("#{@gear.resource.name} not found for #{@gear._gear_param_key} #{params[:resource_id]}") if @resource.nil?
77
+ if @resource.nil?
78
+ error!("#{@gear.resource.name} not found for #{@gear._gear_param_key} #{params[:resource_id]}")
79
+ end
86
80
  end
87
81
 
88
82
  def ensure_action_enabled!
89
83
  unless @gear.action_enabled?(action_name.to_sym)
90
- error!("Action not allowed for #{@gear.resource.name}##{action_name}", 405)
84
+ error!("Action not allowed for #{@gear.resource.name}##{action_name}", :method_not_allowed)
91
85
  end
92
86
  end
93
87
 
94
88
  def error!(msg, code = 400)
95
- render json: Serializers::Lookup.for(:invalid_request).new(request, msg, code), serializer: nil
89
+ render json: Serializers::Lookup.for(:invalid_request).new(request, msg, code)
96
90
  end
97
91
  end
98
92
  end
@@ -0,0 +1,12 @@
1
+ module Gearhead
2
+ module Serializers
3
+ module ActiveModelSerializers
4
+ class CollectionSerializer
5
+ def self.for(records, serializer, options = {})
6
+ options[:each_serializer] = serializer
7
+ ::ActiveModelSerializers::SerializableResource.new(records, options)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -3,10 +3,16 @@ module Gearhead
3
3
  module ActiveModelSerializers
4
4
  class ResourceSerializer
5
5
  def self.for(page)
6
- if defined?(ActiveModelSerializers::Serializer)
7
- klass = Class.new(ActiveModelSerializers::Serializer)
6
+ if defined?(ActiveModel::Serializer)
7
+ klass = Class.new(ActiveModel::Serializer)
8
8
  klass.attributes *(page._gear_attributes.presence || page.default_attributes)
9
9
 
10
+ page._gear_custom_attributes.each do |attr, block|
11
+ klass.attribute attr do |klass|
12
+ block.call(klass.object)
13
+ end
14
+ end
15
+
10
16
  klass
11
17
  end
12
18
  end
@@ -0,0 +1,11 @@
1
+ module Gearhead
2
+ module Serializers
3
+ module ActiveModelSerializers
4
+ class CollectionSerializer
5
+ def self.for(records, serializer, options = {})
6
+ serializer.new(records, options)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -8,6 +8,12 @@ module Gearhead
8
8
  klass.set_type page.resource.model_name.singular_route_key.to_param
9
9
  klass.attributes *(page._gear_attributes.presence || page.default_attributes)
10
10
 
11
+ page._gear_custom_attributes.each do |attr, block|
12
+ klass.attribute attr do |object|
13
+ block.call(object)
14
+ end
15
+ end
16
+
11
17
  klass
12
18
  end
13
19
  end
@@ -1,26 +1,24 @@
1
1
  module Gearhead
2
2
  module Serializers
3
3
  class Lookup
4
- def self.for(action)
5
- new(action).serializer
4
+ def self.for(type, adapter_name = ::Gearhead.config.serialization.adapter)
5
+ new(type, adapter_name).serializer
6
6
  end
7
7
 
8
- def initialize(action)
9
- @action = action.to_s
8
+ attr_reader :adapter_name
9
+ def initialize(type, adapter_name)
10
+ @type = type.to_s
11
+ @adapter_name = adapter_name
10
12
  end
11
13
 
12
14
  def serializer
13
- "::Gearhead::Serializers::#{adapter}::#{@action.classify}Serializer".constantize
15
+ "::Gearhead::Serializers::#{adapter}::#{@type.classify}Serializer".constantize
14
16
  end
15
17
 
16
18
  private
17
19
 
18
- def adapter_name
19
- ::Gearhead.config.serialization.adapter
20
- end
21
-
22
20
  def adapter
23
- adapter_name.to_s.classify
21
+ adapter_name.to_s.camelcase
24
22
  end
25
23
  end
26
24
  end
@@ -1,3 +1,3 @@
1
1
  module Gearhead
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gearhead
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-14 00:00:00.000000000 Z
11
+ date: 2020-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -148,28 +148,28 @@ dependencies:
148
148
  requirements:
149
149
  - - "~>"
150
150
  - !ruby/object:Gem::Version
151
- version: '1.17'
151
+ version: '2'
152
152
  type: :runtime
153
153
  prerelease: false
154
154
  version_requirements: !ruby/object:Gem::Requirement
155
155
  requirements:
156
156
  - - "~>"
157
157
  - !ruby/object:Gem::Version
158
- version: '1.17'
158
+ version: '2'
159
159
  - !ruby/object:Gem::Dependency
160
160
  name: pagy
161
161
  requirement: !ruby/object:Gem::Requirement
162
162
  requirements:
163
163
  - - "~>"
164
164
  - !ruby/object:Gem::Version
165
- version: '0.19'
165
+ version: '3.5'
166
166
  type: :runtime
167
167
  prerelease: false
168
168
  version_requirements: !ruby/object:Gem::Requirement
169
169
  requirements:
170
170
  - - "~>"
171
171
  - !ruby/object:Gem::Version
172
- version: '0.19'
172
+ version: '3.5'
173
173
  - !ruby/object:Gem::Dependency
174
174
  name: zeitwerk
175
175
  requirement: !ruby/object:Gem::Requirement
@@ -194,9 +194,6 @@ files:
194
194
  - MIT-LICENSE
195
195
  - README.md
196
196
  - Rakefile
197
- - app/controllers/gearhead/application_controller.rb
198
- - app/controllers/gearhead/gears_controller.rb
199
- - config/routes.rb
200
197
  - lib/gearhead.rb
201
198
  - lib/gearhead/actions/create.rb
202
199
  - lib/gearhead/actions/index.rb
@@ -218,6 +215,7 @@ files:
218
215
  - lib/gearhead/gear.rb
219
216
  - lib/gearhead/gear_lookup.rb
220
217
  - lib/gearhead/gearbox.rb
218
+ - lib/gearhead/gears_controller.rb
221
219
  - lib/gearhead/paginators/lookup.rb
222
220
  - lib/gearhead/paginators/paginator.rb
223
221
  - lib/gearhead/paginators/pagy_paginator.rb
@@ -226,9 +224,11 @@ files:
226
224
  - lib/gearhead/registry.rb
227
225
  - lib/gearhead/resource_finder.rb
228
226
  - lib/gearhead/router.rb
227
+ - lib/gearhead/serializers/active_model_serializers/collection_serializer.rb
229
228
  - lib/gearhead/serializers/active_model_serializers/invalid_request_serializer.rb
230
229
  - lib/gearhead/serializers/active_model_serializers/invalid_resource_serializer.rb
231
230
  - lib/gearhead/serializers/active_model_serializers/resource_serializer.rb
231
+ - lib/gearhead/serializers/fast_jsonapi/collection_serializer.rb
232
232
  - lib/gearhead/serializers/fast_jsonapi/invalid_request_serializer.rb
233
233
  - lib/gearhead/serializers/fast_jsonapi/invalid_resource_serializer.rb
234
234
  - lib/gearhead/serializers/fast_jsonapi/resource_serializer.rb
@@ -1,5 +0,0 @@
1
- module Gearhead
2
- class ApplicationController < ActionController::Base
3
- protect_from_forgery with: :exception
4
- end
5
- end
@@ -1,9 +0,0 @@
1
- Gearhead::Engine.routes.draw do
2
- scope module: :gearhead do
3
- # resources :gears, path: ':resource_class', param: :resource_id do
4
- # member do
5
- # match ':member_action' => 'gears#member_action', via: :all
6
- # end
7
- # end
8
- end
9
- end