caprese 0.3.19.3 → 0.3.20

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
  SHA1:
3
- metadata.gz: 52dc5d95f51d80297a0e75ced5e685de0596c360
4
- data.tar.gz: dd5682687744ecf3c6effa11ca8e3474c53d0f8e
3
+ metadata.gz: f20459249bd9aa768296fff0603eacaec3d53c99
4
+ data.tar.gz: 667f7a0cd853d8f69099a78ecfaa76c48d15f5de
5
5
  SHA512:
6
- metadata.gz: f5ab75ad8385ba8bb431d2de68e13cb7c324975bf5a7c4f9f32bc88d52ce831f4ef15be010d573217995ff9cf9be876a7381e2ffbc40a9ee6a60ccbbe7a798ae
7
- data.tar.gz: e4dc0068ec675cd58827d1f8222fc75a624fc38922befbf979859aea1bae472868a797577712d347438e8f525d1ec33bf868068ac866746989a42e3da803ed7e
6
+ metadata.gz: 17f2e85ce1abeddd71ee15ce76c68e425b7f6cc8a4e2f71732cdebcd37df424dd8927716a6927cdb05ed861bd09cc3b1fdddb0ad8f8f39ba18b7cf1b905e6a17
7
+ data.tar.gz: f936557de63859192d37419ba50df905949835df4359e0ccfde5ea058b7093f4a7b4f538a5e76496accc17e8dfc55ff1f169a56950685fe47bddb7254cd76b79
data/CHANGELOG.md CHANGED
@@ -111,3 +111,9 @@
111
111
  ## 0.3.19.3
112
112
 
113
113
  * Fix use of `ActiveRecord::Validations::AssociatedValidator` in `Caprese::AssociatedValidator`
114
+
115
+ ## 0.3.20
116
+
117
+ * Add `Caprese::Controller#resource_type_aliases` method that returns object mapping type aliases to actual types for the application
118
+ * Add `Caprese::Record.caprese_field_aliases` method that returns object mapping field aliases to actual fields for the record
119
+ * Add `Caprese::Record.caprese_type` method that returns singular symbol indicating the type to use when serializing the record. (`Caprese::Serializer.json_api_key` now uses this.)
@@ -425,7 +425,8 @@ module Caprese
425
425
  allow_wildcard: true
426
426
  )
427
427
  serializer.associations(include_directive).each_with_object({}) do |association, hash|
428
- hash[association.key] = Relationship.new(
428
+ aliased_key = serializer.object.class.caprese_alias_field(association.key)
429
+ hash[aliased_key] = Relationship.new(
429
430
  serializer,
430
431
  instance_options,
431
432
  association,
@@ -0,0 +1,65 @@
1
+ require 'active_support/concern'
2
+ require 'caprese/errors'
3
+
4
+ module Caprese
5
+ module Aliasing
6
+ extend ActiveSupport::Concern
7
+
8
+ # Specifies specific resource models that have types that are aliased.
9
+ # @note The `caprese_type` class variable of the model should also be set to the new type
10
+ # @example
11
+ # {
12
+ # questions: attributes
13
+ # }
14
+ def resource_type_aliases
15
+ {}
16
+ end
17
+
18
+ # Checks resource_type_aliases for a type alias, or returns the type already stated
19
+ #
20
+ # @param [Symbol] type the type to search for an alias for
21
+ # @return [Symbol] the actual type for the type alias
22
+ def actual_type(type)
23
+ resource_type_aliases[type] || type
24
+ end
25
+
26
+ # Gets the actual field name for the controller_record_class for any given field requested
27
+ #
28
+ # @param [Symbol,String] field the field that was requested
29
+ # @param [Class] klass the klass to get field aliases for
30
+ # @return [Symbol] the actual field name for the field requested
31
+ def actual_field(field, klass = controller_record_class)
32
+ klass.caprese_unalias_field(field.to_sym)
33
+ end
34
+
35
+ # Takes in a set of possibly aliased includes and converts them to their actual names
36
+ #
37
+ # @param [String] the CSV string of possibly aliased includes
38
+ # @return [Array<String>] the actual includes
39
+ def actual_includes(includes)
40
+ includes.split(',').map do |i|
41
+ if(i = i.split('.')).size > 1
42
+ klass = nil
43
+ i.map do |i2|
44
+ actual = actual_field(i2, klass)
45
+ klass = klass.reflections[actual].klass
46
+ actual
47
+ end.join('.')
48
+ else
49
+ actual_field(i)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Takes in a set of possibly aliased fields with types and converts them to their actual
55
+ # types and fields
56
+ #
57
+ # @param [Hash<Array>] the hash of possibly aliased resource types with their possibly aliased field specifier arrays
58
+ # @return [Hash<Array>] the actual resource type and fields
59
+ def actual_fields(fields)
60
+ Hash[*fields.each do |type, fields|
61
+ [actual_type(type), fields.map { |f| actual_field(f, record_class(type)) }]
62
+ end]
63
+ end
64
+ end
65
+ end
@@ -220,12 +220,20 @@ module Caprese
220
220
  # @param [Array] permitted_params the permitted params for the action
221
221
  # @param [Parameters] data the data sent to the server to construct and assign to the record
222
222
  def assign_record_attributes(record, permitted_params, data)
223
- attributes = data[:attributes].try(:permit, *permitted_params) || {}
223
+ attributes = data[:attributes].try(:permit, *permitted_params).try(:inject, {}) do |out, (attr, val)|
224
+ out[actual_field(attr, record.class)] = val
225
+ out
226
+ end || {}
224
227
 
225
228
  data[:relationships]
226
229
  .try(:slice, *flattened_keys_for(permitted_params))
227
230
  .try(:each) do |relationship_name, relationship_data|
228
- attributes[relationship_name] = records_for_relationship(
231
+ actual_relationship_name = actual_field(relationship_name, record.class)
232
+
233
+ # TODO: Add checkme for relationship_name to ensure that format is correct (not Array when actually Record, vice versa)
234
+ # No relationship exists as well
235
+
236
+ attributes[actual_relationship_name] = records_for_relationship(
229
237
  record,
230
238
  nested_params_for(relationship_name, permitted_params),
231
239
  relationship_name,
@@ -134,9 +134,9 @@ module Caprese
134
134
  query_params[:sort].each do |sort_field|
135
135
  ordering = ordering.merge(
136
136
  if sort_field[0] == '-' # EX: -created_at, sort by created_at descending
137
- { sort_field[1..-1] => :desc }
137
+ { actual_field(sort_field[1..-1]) => :desc }
138
138
  else
139
- { sort_field => :asc }
139
+ { actual_field(sort_field) => :asc }
140
140
  end
141
141
  )
142
142
  end
@@ -172,9 +172,9 @@ module Caprese
172
172
  scope = record_scope(unversion(params[:controller]).to_sym)
173
173
 
174
174
  if scope.any? && query_params[:filter].try(:any?)
175
- if (valid_filters = query_params[:filter].select { |k, _| scope.column_names.include? k }).present?
175
+ if (valid_filters = query_params[:filter].select { |k, _| scope.column_names.include? actual_field(k).to_s }).present?
176
176
  valid_filters.each do |k, v|
177
- scope = scope.where(k => v)
177
+ scope = scope.where(actual_field(k) => v)
178
178
  end
179
179
  end
180
180
  end
@@ -207,24 +207,26 @@ module Caprese
207
207
  )
208
208
  end
209
209
 
210
+ relationship_name = queried_association.reflection.name
211
+
210
212
  successful =
211
213
  case queried_association.reflection.macro
212
214
  when :has_many
213
215
  if request.patch?
214
- queried_record.send("#{params[:relationship]}=", relationship_resources)
216
+ queried_record.send("#{relationship_name}=", relationship_resources)
215
217
  elsif request.post?
216
- queried_record.send(params[:relationship]).push relationship_resources
218
+ queried_record.send(relationship_name).push relationship_resources
217
219
  elsif request.delete?
218
- queried_record.send(params[:relationship]).delete relationship_resources
220
+ queried_record.send(relationship_name).delete relationship_resources
219
221
  end
220
222
  when :has_one
221
223
  if request.patch?
222
- queried_record.send("#{params[:relationship]}=", relationship_resources[0])
224
+ queried_record.send("#{relationship_name}=", relationship_resources[0])
223
225
  objects[0].save
224
226
  end
225
227
  when :belongs_to
226
228
  if request.patch?
227
- queried_record.send("#{params[:relationship]}=", relationship_resources[0])
229
+ queried_record.send("#{relationship_name}=", relationship_resources[0])
228
230
  queried_record.save
229
231
  end
230
232
  end
@@ -247,7 +249,7 @@ module Caprese
247
249
  def queried_association
248
250
  unless @queried_association
249
251
  begin
250
- @queried_association = queried_record.association(params[:relationship])
252
+ @queried_association = queried_record.association(actual_field(params[:relationship]))
251
253
  rescue ActiveRecord::AssociationNotFoundError => e
252
254
  fail AssociationNotFoundError.new(params[:relationship])
253
255
  end
@@ -16,7 +16,15 @@ module Caprese
16
16
  # @param [Symbol] type the record type to get the class for
17
17
  # @return [Class] the class for a given record type
18
18
  def record_class(type)
19
- type.to_s.classify.constantize
19
+ begin
20
+ type.to_s.classify.constantize
21
+ rescue NameError => e
22
+ if resource_type_aliases[type.to_sym]
23
+ record_class(resource_type_aliases[type.to_sym].to_sym)
24
+ else
25
+ raise e
26
+ end
27
+ end
20
28
  end
21
29
 
22
30
  # Gets the record class for the current controller
@@ -1,6 +1,7 @@
1
1
  require 'action_controller'
2
2
  require 'active_support/configurable'
3
3
  require 'caprese/concerns/versioning'
4
+ require 'caprese/controller/concerns/aliasing'
4
5
  require 'caprese/controller/concerns/callbacks'
5
6
  require 'caprese/controller/concerns/errors'
6
7
  require 'caprese/controller/concerns/persistence'
@@ -13,7 +14,10 @@ module Caprese
13
14
  # TODO: Convert to ActionController::API with Rails 5
14
15
  class Controller < ActionController::Base
15
16
  include ActiveSupport::Configurable
17
+ include Aliasing
16
18
  include Callbacks
19
+ # FIXME: Be careful about including `Errors` in certain order, because it has `rescue_from Exception` and this affects
20
+ # control flow with other rescue handlers if included after their modules
17
21
  include Errors
18
22
  include Persistence
19
23
  include Query
@@ -0,0 +1,59 @@
1
+ require 'active_support/concern'
2
+
3
+ module Caprese
4
+ module Record
5
+ module Aliasing
6
+ extend ActiveSupport::Concern
7
+
8
+ # Provides an intermediary helper method on records that defines non-column attributes for records
9
+ # @note This exists so there is a method by which to state that a non-column attribute should
10
+ # have an error source pointer like `/data/attributes/[name]` instead of `/data/relationships/[name]`
11
+ def caprese_is_attribute?(attribute_name)
12
+ false
13
+ end
14
+
15
+ # Checks that any field provided is either an attribute on the record, or an aliased field, or none
16
+ #
17
+ # @param [String,Symbol] field the field to check for on this record
18
+ # @return [Boolean] whether or not the field is on the record
19
+ def caprese_is_field?(field)
20
+ respond_to?(field = field.to_sym) || caprese_is_attribute?(field) || self.class.caprese_field_aliases[field]
21
+ end
22
+
23
+ module ClassMethods
24
+ # Given an actual field, convert to its appropriate field alias for the class
25
+ # @note The reason this is useful is because ActiveRecord validations must validate the actual field name of a
26
+ # model, but when we add errors they should always have aliased fields
27
+ #
28
+ # @param [String,Symbol] field the actual field name to alias
29
+ # @return [Symbol] the aliased field name, or the original name symbolized
30
+ def caprese_alias_field(field)
31
+ caprese_field_aliases.invert[field = field.to_sym] || field
32
+ end
33
+
34
+ # Given an aliased field, convert to its actual field for the class
35
+ #
36
+ # @param [String,Symbol] field the actual field name to alias
37
+ # @return [Symbol] the aliased field name, or the original name symbolized
38
+ def caprese_unalias_field(field)
39
+ caprese_field_aliases[field = field.to_sym] || field
40
+ end
41
+
42
+ # Provides the ability to display an aliased field name to the consumer of the API, and then map that name
43
+ # to its real name on the server
44
+ # @example
45
+ # {
46
+ # alias: :actual
47
+ # }
48
+ def caprese_field_aliases
49
+ {}
50
+ end
51
+
52
+ # The type that is serialized and responded with for this class
53
+ def caprese_type
54
+ self.name.underscore
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -24,7 +24,7 @@ module Caprese
24
24
 
25
25
  e = Error.new(
26
26
  model: @base.class.name.underscore.downcase,
27
- field: attribute == :base ? nil : attribute,
27
+ field: attribute == :base ? nil : @base.class.caprese_alias_field(attribute),
28
28
  code: code,
29
29
  t: options[:t]
30
30
  )
@@ -3,13 +3,13 @@ require 'active_support/concern'
3
3
  require 'active_support/dependencies'
4
4
  require 'caprese/errors'
5
5
  require 'caprese/record/associated_validator'
6
- require 'caprese/record/attribute_aliasing'
6
+ require 'caprese/record/aliasing'
7
7
  require 'caprese/record/errors'
8
8
 
9
9
  module Caprese
10
10
  module Record
11
11
  extend ActiveSupport::Concern
12
- include AttributeAliasing
12
+ include Aliasing
13
13
 
14
14
  mattr_accessor :caprese_style_errors
15
15
  @@caprese_style_errors = true
@@ -0,0 +1,17 @@
1
+ require 'active_support/concern'
2
+
3
+ module Caprese
4
+ class Serializer < ActiveModel::Serializer
5
+ module Aliasing
6
+ extend ActiveSupport::Concern
7
+
8
+ # Override so we can get the values for serialization of aliased attributes just like unaliased
9
+ #
10
+ # @param [String,Symbol] attribute the attribute (aliased or not) to read for serialization
11
+ # @return [Value] the value of the attribute
12
+ def read_attribute_for_serialization(attribute)
13
+ super(self.object.class.caprese_unalias_field(attribute))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -80,6 +80,8 @@ module Caprese
80
80
  def build_association_block(reflection_name)
81
81
  primary_key = Caprese.config.resource_primary_key
82
82
 
83
+ reflection_name = reflection_name.to_sym
84
+
83
85
  Proc.new do |serializer|
84
86
  link :self do
85
87
  url = "relationship_definition_#{serializer.version_name("#{object.class.name.underscore}_url")}"
@@ -1,5 +1,6 @@
1
1
  require 'active_model_serializers'
2
2
  require 'caprese/concerns/versioning'
3
+ require 'caprese/serializer/concerns/aliasing'
3
4
  require 'caprese/serializer/concerns/links'
4
5
  require 'caprese/serializer/concerns/lookup'
5
6
  require 'caprese/serializer/concerns/relationships'
@@ -8,12 +9,13 @@ module Caprese
8
9
  class Serializer < ActiveModel::Serializer
9
10
  extend Versioning
10
11
  include Versioning
12
+ include Aliasing
11
13
  include Links
12
14
  include Lookup
13
15
  include Relationships
14
16
 
15
17
  def json_key
16
- unversion(self.class.name).gsub('Serializer', '').underscore
18
+ object.class.caprese_type
17
19
  end
18
20
  end
19
21
  end
@@ -1,3 +1,3 @@
1
1
  module Caprese
2
- VERSION = '0.3.19.3'
2
+ VERSION = '0.3.20'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caprese
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.19.3
4
+ version: 0.3.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Landgrebe
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2017-04-05 00:00:00.000000000 Z
13
+ date: 2017-04-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: active_model_serializers
@@ -224,6 +224,7 @@ files:
224
224
  - lib/caprese/adapter/json_api/resource_identifier.rb
225
225
  - lib/caprese/concerns/versioning.rb
226
226
  - lib/caprese/controller.rb
227
+ - lib/caprese/controller/concerns/aliasing.rb
227
228
  - lib/caprese/controller/concerns/callbacks.rb
228
229
  - lib/caprese/controller/concerns/errors.rb
229
230
  - lib/caprese/controller/concerns/persistence.rb
@@ -234,11 +235,12 @@ files:
234
235
  - lib/caprese/error.rb
235
236
  - lib/caprese/errors.rb
236
237
  - lib/caprese/record.rb
238
+ - lib/caprese/record/aliasing.rb
237
239
  - lib/caprese/record/associated_validator.rb
238
- - lib/caprese/record/attribute_aliasing.rb
239
240
  - lib/caprese/record/errors.rb
240
241
  - lib/caprese/routing/caprese_resources.rb
241
242
  - lib/caprese/serializer.rb
243
+ - lib/caprese/serializer/concerns/aliasing.rb
242
244
  - lib/caprese/serializer/concerns/links.rb
243
245
  - lib/caprese/serializer/concerns/lookup.rb
244
246
  - lib/caprese/serializer/concerns/relationships.rb
@@ -1,12 +0,0 @@
1
- module Caprese
2
- module Record
3
- module AttributeAliasing
4
- # Provides an intermediary helper method on records that defines non-column attributes for records
5
- # @note This exists so there is a method by which to state that a non-column attribute should
6
- # have an error source pointer like `/data/attributes/[name]` instead of `/data/relationships/[name]`
7
- def caprese_is_attribute?(attribute_name)
8
- false
9
- end
10
- end
11
- end
12
- end