gearhead 0.1.0 → 0.2.0

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