graphiti 1.0.alpha.4 → 1.0.alpha.5

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
  SHA1:
3
- metadata.gz: 736b8601bf09408a0fa6ed8f2a0c148a79f7c285
4
- data.tar.gz: 9b0adbc770aea3e609f15804db1b33ddba1dee13
3
+ metadata.gz: a97ae8d131e23bc5acf8f8a4fcb97e7cdce25fdc
4
+ data.tar.gz: 04ae2c8cc7d56ffea5dc0f8a3fec1a7506a5baeb
5
5
  SHA512:
6
- metadata.gz: 467e32b7154d37819d6bc4598872e66f3a138120ea2781253238ce4b6fc426b416184c79e5ce58a6d9b596b5754cc87bf7ffb2fc02ab2867bdc6eb00edbe8939
7
- data.tar.gz: c8ec00841469671ef261283f3dd6f556c095742be04d31f1bfa9d633ee5066cd009a790560bc65a61845b6d2db366cff5aa2da7b0226e0a4a2ff7146677c2b78
6
+ metadata.gz: 254a66e4e0bd21eef9038bc8e756b2195cf0d4f0b817944e2f5c71ca139c40da3d0c3a99d7d540b92f77b4bdb5a6acade53efce5ffb839f61ca1a6dcfd01d46e
7
+ data.tar.gz: 7790a1eb890eeff8bdf7690df749b3841fbff88487d6ef39dc7f3e9679bdabb9854898ad833e9ed8c5427712f14af416ff6dee4d9ba92cecfe34f773997b1adc
@@ -1,82 +1,27 @@
1
1
  module Graphiti
2
2
  module Adapters
3
- # Adapters DRY up common resource logic.
4
- #
5
- # For instance, there's no reason to write ActiveRecord logic like this in
6
- # every Resource:
7
- #
8
- # allow_filter :title do |scope, value|
9
- # scope.where(title: value)
10
- # end
11
- #
12
- # sort do |scope, att, dir|
13
- # scope.order(att => dir)
14
- # end
15
- #
16
- # paginate do |scope, current_page, per_page|
17
- # scope.page(current_page).per(per_page)
18
- # end
19
- #
20
- # This logic can be re-used through an *Adapter*:
21
- #
22
- # use_adapter Graphiti::Adapters::ActiveRecord
23
- # allow_filter :title
24
- #
25
- # Adapters are pretty simple to write. The corresponding code for the above
26
- # ActiveRecord adapter, which should look pretty familiar:
27
- #
28
- # class Graphiti::Adapters::ActiveRecord
29
- # def filter(scope, attribute, value)
30
- # scope.where(attribute => value)
31
- # end
32
- #
33
- # def order(scope, attribute, direction)
34
- # scope.order(attribute => direction)
35
- # end
36
- #
37
- # def paginate(scope, current_page, per_page)
38
- # scope.page(current_page).per(per_page)
39
- # end
40
- # end
41
- #
42
- # An adapter can have a corresponding +sideloading_module+. This module
43
- # gets mixed in to a Sideload. In other words, *Resource* is to
44
- # *Adapter* as *Sideload* is to *Adapter#sideloading_module*. Use this
45
- # module to define DSL methods that wrap #allow_sideload:
46
- #
47
- # class MyAdapter < Graphiti::Adapters::Abstract
48
- # # ... code ...
49
- # def sideloading_module
50
- # MySideloadingAdapter
51
- # end
52
- # end
53
- #
54
- # module MySideloadingAdapter
55
- # def belongs_to(association_name)
56
- # allow_sideload association_name do
57
- # # ... code ...
58
- # end
59
- # end
60
- # end
61
- #
62
- # # And now in your Resource:
63
- # class MyResource < ApplicationResource
64
- # # ... code ...
65
- # use_adapter MyAdapter
66
- #
67
- # belongs_to :my_association
68
- # end
69
- #
70
- # If you need the adapter to do *nothing*, because perhaps the API you
71
- # are hitting does not support sorting,
72
- # use +Graphiti::Adapters::Null+.
73
- #
74
- # @see Resource.use_adapter
75
- # @see Adapters::ActiveRecord
76
- # @see Adapters::ActiveRecordSideloading
77
- # @see Adapters::Null
78
3
  class Abstract
79
- def default_operators
4
+ attr_reader :resource
5
+
6
+ def initialize(resource)
7
+ @resource = resource
8
+ end
9
+
10
+ # You want to override this!
11
+ # Map of association_type => sideload_class
12
+ # e.g.
13
+ # { has_many: Adapters::ActiveRecord::HasManySideload }
14
+ def self.sideloading_classes
15
+ {
16
+ has_many: ::Graphiti::Sideload::HasMany,
17
+ belongs_to: ::Graphiti::Sideload::BelongsTo,
18
+ has_one: ::Graphiti::Sideload::HasOne,
19
+ many_to_many: ::Graphiti::Sideload::ManyToMany,
20
+ polymorphic_belongs_to: ::Graphiti::Sideload::PolymorphicBelongsTo
21
+ }
22
+ end
23
+
24
+ def self.default_operators
80
25
  {
81
26
  string: [
82
27
  :eq,
@@ -423,7 +368,11 @@ module Graphiti
423
368
  parent, child, association_name, association_type
424
369
  else
425
370
  if [:has_many, :many_to_many].include?(association_type)
426
- parent.send(:"#{association_name}") << child
371
+ if parent.send(:"#{association_name}").nil?
372
+ parent.send(:"#{association_name}=", [child])
373
+ else
374
+ parent.send(:"#{association_name}") << child
375
+ end
427
376
  else
428
377
  parent.send(:"#{association_name}=", child)
429
378
  end
@@ -470,20 +419,6 @@ module Graphiti
470
419
  raise 'you must override #disassociate in an adapter subclass'
471
420
  end
472
421
 
473
- # You want to override this!
474
- # Map of association_type => sideload_class
475
- # e.g.
476
- # { has_many: Adapters::ActiveRecord::HasManySideload }
477
- def sideloading_classes
478
- {
479
- has_many: ::Graphiti::Sideload::HasMany,
480
- belongs_to: ::Graphiti::Sideload::BelongsTo,
481
- has_one: ::Graphiti::Sideload::HasOne,
482
- many_to_many: ::Graphiti::Sideload::ManyToMany,
483
- polymorphic_belongs_to: ::Graphiti::Sideload::PolymorphicBelongsTo
484
- }
485
- end
486
-
487
422
  # @param [Class] model_class The configured model class (see Resource.model)
488
423
  # @param [Hash] create_params Attributes + id
489
424
  # @return the model instance just created
@@ -528,13 +463,13 @@ module Graphiti
528
463
 
529
464
  private
530
465
 
531
- def numerical_operators
466
+ def self.numerical_operators
532
467
  [:eq, :not_eq, :gt, :gte, :lt, :lte]
533
468
  end
534
469
 
535
470
  def activerecord_adapter
536
471
  @activerecord_adapter ||=
537
- ::Graphiti::Adapters::ActiveRecord::Base.new
472
+ ::Graphiti::Adapters::ActiveRecord::Base.new(resource)
538
473
  end
539
474
 
540
475
  def activerecord_associate?(parent, child, association_name)
@@ -2,6 +2,24 @@ module Graphiti
2
2
  module Adapters
3
3
  module ActiveRecord
4
4
  class Base < ::Graphiti::Adapters::Abstract
5
+ def filter_eq(scope, attribute, value)
6
+ scope.where(attribute => value)
7
+ end
8
+ alias :filter_integer_eq :filter_eq
9
+ alias :filter_float_eq :filter_eq
10
+ alias :filter_big_decimal_eq :filter_eq
11
+ alias :filter_date_eq :filter_eq
12
+ alias :filter_boolean_eq :filter_eq
13
+
14
+ def filter_not_eq(scope, attribute, value)
15
+ scope.where.not(attribute => value)
16
+ end
17
+ alias :filter_integer_not_eq :filter_not_eq
18
+ alias :filter_float_not_eq :filter_not_eq
19
+ alias :filter_big_decimal_not_eq :filter_not_eq
20
+ alias :filter_date_not_eq :filter_not_eq
21
+ alias :filter_boolean_not_eq :filter_not_eq
22
+
5
23
  def filter_string_eq(scope, attribute, value, is_not: false)
6
24
  column = scope.klass.arel_table[attribute]
7
25
  clause = column.lower.eq_any(value.map(&:downcase))
@@ -54,56 +72,44 @@ module Graphiti
54
72
  filter_string_match(scope, attribute, value, is_not: true)
55
73
  end
56
74
 
57
- def filter_integer_eq(scope, attribute, value, is_not: false)
58
- clause = { attribute => value }
59
- is_not ? scope.where.not(clause) : scope.where(clause)
60
- end
61
- alias :filter_float_eq :filter_integer_eq
62
- alias :filter_big_decimal_eq :filter_integer_eq
63
- alias :filter_date_eq :filter_integer_eq
64
- alias :filter_boolean_eq :filter_integer_eq
65
-
66
- def filter_integer_not_eq(scope, attribute, value)
67
- filter_integer_eq(scope, attribute, value, is_not: true)
68
- end
69
- alias :filter_float_not_eq :filter_integer_not_eq
70
- alias :filter_big_decimal_not_eq :filter_integer_not_eq
71
- alias :filter_date_not_eq :filter_integer_not_eq
72
-
73
- def filter_integer_gt(scope, attribute, value)
75
+ def filter_gt(scope, attribute, value)
74
76
  column = scope.klass.arel_table[attribute]
75
77
  scope.where(column.gt_any(value))
76
78
  end
77
- alias :filter_float_gt :filter_integer_gt
78
- alias :filter_big_decimal_gt :filter_integer_gt
79
- alias :filter_datetime_gt :filter_integer_gt
80
- alias :filter_date_gt :filter_integer_gt
79
+ alias :filter_integer_gt :filter_gt
80
+ alias :filter_float_gt :filter_gt
81
+ alias :filter_big_decimal_gt :filter_gt
82
+ alias :filter_datetime_gt :filter_gt
83
+ alias :filter_date_gt :filter_gt
81
84
 
82
- def filter_integer_gte(scope, attribute, value)
85
+ def filter_gte(scope, attribute, value)
83
86
  column = scope.klass.arel_table[attribute]
84
87
  scope.where(column.gteq_any(value))
85
88
  end
86
- alias :filter_float_gte :filter_integer_gte
87
- alias :filter_big_decimal_gte :filter_integer_gte
88
- alias :filter_datetime_gte :filter_integer_gte
89
- alias :filter_date_gte :filter_integer_gte
89
+ alias :filter_integer_gte :filter_gte
90
+ alias :filter_float_gte :filter_gte
91
+ alias :filter_big_decimal_gte :filter_gte
92
+ alias :filter_datetime_gte :filter_gte
93
+ alias :filter_date_gte :filter_gte
90
94
 
91
- def filter_integer_lt(scope, attribute, value)
95
+ def filter_lt(scope, attribute, value)
92
96
  column = scope.klass.arel_table[attribute]
93
97
  scope.where(column.lt_any(value))
94
98
  end
95
- alias :filter_float_lt :filter_integer_lt
96
- alias :filter_big_decimal_lt :filter_integer_lt
97
- alias :filter_datetime_lt :filter_integer_lt
98
- alias :filter_date_lt :filter_integer_lt
99
+ alias :filter_integer_lt :filter_lt
100
+ alias :filter_float_lt :filter_lt
101
+ alias :filter_big_decimal_lt :filter_lt
102
+ alias :filter_datetime_lt :filter_lt
103
+ alias :filter_date_lt :filter_lt
99
104
 
100
- def filter_integer_lte(scope, attribute, value)
105
+ def filter_lte(scope, attribute, value)
101
106
  column = scope.klass.arel_table[attribute]
102
107
  scope.where(column.lteq_any(value))
103
108
  end
104
- alias :filter_float_lte :filter_integer_lte
105
- alias :filter_big_decimal_lte :filter_integer_lte
106
- alias :filter_date_lte :filter_integer_lte
109
+ alias :filter_integer_lte :filter_lte
110
+ alias :filter_float_lte :filter_lte
111
+ alias :filter_big_decimal_lte :filter_lte
112
+ alias :filter_date_lte :filter_lte
107
113
 
108
114
  # Ensure fractional seconds don't matter
109
115
  def filter_datetime_eq(scope, attribute, value, is_not: false)
@@ -180,7 +186,7 @@ module Graphiti
180
186
  end
181
187
  end
182
188
 
183
- def sideloading_classes
189
+ def self.sideloading_classes
184
190
  {
185
191
  has_many: HasManySideload,
186
192
  has_one: HasOneSideload,
@@ -198,26 +204,34 @@ module Graphiti
198
204
  end
199
205
 
200
206
  def associate_all(parent, children, association_name, association_type)
201
- association = parent.association(association_name)
202
- association.loaded!
203
-
204
- children.each do |child|
205
- if association_type == :many_to_many &&
206
- !parent.send(association_name).exists?(child.id) &&
207
- [:create, :update].include?(Graphiti.context[:namespace])
208
- parent.send(association_name) << child
209
- else
210
- target = association.instance_variable_get(:@target)
211
- target |= [child]
212
- association.instance_variable_set(:@target, target)
207
+ if activerecord_associate?(parent, children[0], association_name)
208
+ association = parent.association(association_name)
209
+ association.loaded!
210
+
211
+ children.each do |child|
212
+ if association_type == :many_to_many &&
213
+ !parent.send(association_name).exists?(child.id) &&
214
+ [:create, :update].include?(Graphiti.context[:namespace])
215
+ parent.send(association_name) << child
216
+ else
217
+ target = association.instance_variable_get(:@target)
218
+ target |= [child]
219
+ association.instance_variable_set(:@target, target)
220
+ end
213
221
  end
222
+ else
223
+ super
214
224
  end
215
225
  end
216
226
 
217
227
  def associate(parent, child, association_name, association_type)
218
- association = parent.association(association_name)
219
- association.loaded!
220
- association.instance_variable_set(:@target, child)
228
+ if activerecord_associate?(parent, child, association_name)
229
+ association = parent.association(association_name)
230
+ association.loaded!
231
+ association.instance_variable_set(:@target, child)
232
+ else
233
+ super
234
+ end
221
235
  end
222
236
 
223
237
  # When a has_and_belongs_to_many relationship, we don't have a foreign
@@ -16,23 +16,59 @@ The adapter #{@adapter.class} does not implement method '#{@method}', which was
16
16
  end
17
17
  end
18
18
 
19
+ class InvalidFilterValue < Base
20
+ def initialize(resource, filter, value)
21
+ @resource = resource
22
+ @filter = filter
23
+ @value = value
24
+ end
25
+
26
+ def message
27
+ allow = @filter.values[0][:allow]
28
+ reject = @filter.values[0][:reject]
29
+ msg = <<-MSG
30
+ #{@resource.class.name}: tried to filter on #{@filter.keys[0].inspect}, but passed invalid value #{@value.inspect}.
31
+ MSG
32
+ msg << "\nWhitelist: #{allow.inspect}" if allow
33
+ msg << "\nBlacklist: #{reject.inspect}" if reject
34
+ msg
35
+ end
36
+ end
37
+
19
38
  class InvalidLink < Base
20
- def initialize(resource_class, sideload)
39
+ def initialize(resource_class, sideload, action)
21
40
  @resource_class = resource_class
22
41
  @sideload = sideload
42
+ @action = action
23
43
  end
24
44
 
25
45
  def message
26
46
  <<-MSG
27
47
  #{@resource_class.name}: Cannot link to sideload #{@sideload.name.inspect}!
28
48
 
29
- Make sure the endpoint "#{@sideload.resource.endpoint[:full_path]}" exists, or customize the endpoint for #{@sideload.resource.class.name}.
49
+ Make sure the endpoint "#{@sideload.resource.endpoint[:full_path]}" exists with action #{@action.inspect}, or customize the endpoint for #{@sideload.resource.class.name}.
30
50
 
31
51
  If you do not wish to generate a link, pass link: false or set self.relationship_links_by_default = false.
32
52
  MSG
33
53
  end
34
54
  end
35
55
 
56
+ class SingularFilter < Base
57
+ def initialize(resource, filter, value)
58
+ @resource = resource
59
+ @filter = filter
60
+ @value = value
61
+ end
62
+
63
+ def message
64
+ <<-MSG
65
+ #{@resource.class.name}: passed multiple values to filter #{@filter.keys[0].inspect}, which was marked single: true.
66
+
67
+ Value was: #{@value.inspect}
68
+ MSG
69
+ end
70
+ end
71
+
36
72
  class Unlinkable < Base
37
73
  def initialize(resource_class, sideload)
38
74
  @resource_class = resource_class
@@ -49,6 +49,7 @@ module Graphiti
49
49
  def typecast(name, value, flag)
50
50
  att = get_attr!(name, flag)
51
51
  type = Graphiti::Types[att[:type]]
52
+ return if value.nil? && type[:kind] != 'array'
52
53
  begin
53
54
  flag = :read if flag == :readable
54
55
  flag = :write if flag == :writable
@@ -44,8 +44,8 @@ module Graphiti
44
44
  attr_writer :config
45
45
  end
46
46
 
47
- class_attribute :adapter,
48
- :model,
47
+ class_attribute :adapter, instance_reader: false
48
+ class_attribute :model,
49
49
  :type,
50
50
  :polymorphic,
51
51
  :polymorphic_child,
@@ -66,7 +66,7 @@ module Graphiti
66
66
  def self.inherited(klass)
67
67
  super
68
68
  klass.config = Util::Hash.deep_dup(config)
69
- klass.adapter ||= Adapters::Abstract.new
69
+ klass.adapter ||= Adapters::Abstract
70
70
  klass.default_sort ||= []
71
71
  klass.default_page_size ||= 20
72
72
  # re-assigning causes a new Class.new
@@ -200,6 +200,10 @@ module Graphiti
200
200
  Util::AttributeCheck.run(self, name, flag, request, raise_error)
201
201
  end
202
202
 
203
+ def adapter
204
+ @adapter ||= self.class.adapter.new(self)
205
+ end
206
+
203
207
  def filters
204
208
  self.class.filters
205
209
  end
@@ -10,9 +10,17 @@ module Graphiti
10
10
  if att = get_attr(name, :filterable, raise_error: :only_unsupported)
11
11
  aliases = [name, opts[:aliases]].flatten.compact
12
12
  operators = FilterOperators.build(self, att[:type], opts, &blk)
13
+
14
+ if Graphiti::Types[att[:type]][:canonical_name] == :boolean
15
+ opts[:single] = true
16
+ end
17
+
13
18
  config[:filters][name.to_sym] = {
14
19
  aliases: aliases,
15
20
  type: att[:type],
21
+ allow: opts[:allow],
22
+ reject: opts[:reject],
23
+ single: !!opts[:single],
16
24
  operators: operators.to_hash
17
25
  }
18
26
  else
@@ -53,7 +61,7 @@ module Graphiti
53
61
  end
54
62
 
55
63
  def stat(symbol_or_hash, &blk)
56
- dsl = Stats::DSL.new(adapter, symbol_or_hash)
64
+ dsl = Stats::DSL.new(new.adapter, symbol_or_hash)
57
65
  dsl.instance_eval(&blk) if blk
58
66
  config[:stats][dsl.name] = dsl
59
67
  end
@@ -25,7 +25,7 @@ module Graphiti
25
25
  def associate(parent, child, association_name, type)
26
26
  child_resource = self.class.resource_for_model(parent)
27
27
  if child_resource.sideloads[association_name]
28
- child_resource.adapter
28
+ child_resource.new.adapter
29
29
  .associate(parent, child, association_name, type)
30
30
  end
31
31
  end
@@ -61,10 +61,10 @@ module Graphiti
61
61
  resource = resource ||= Util::Class.infer_resource_class(self, name)
62
62
  sideload = resource.sideload(as)
63
63
 
64
- _adapter = adapter
64
+ _resource = resource
65
65
  filter sideload.true_foreign_key, resource.attributes[:id][:type] do
66
66
  eq do |scope, value|
67
- _adapter.belongs_to_many_filter(sideload, scope, value)
67
+ _resource.new.adapter.belongs_to_many_filter(sideload, scope, value)
68
68
  end
69
69
  end
70
70
  end
@@ -132,11 +132,15 @@ module Graphiti
132
132
 
133
133
  def filters(resource)
134
134
  {}.tap do |f|
135
- resource.filters.each_pair do |name, config|
135
+ resource.filters.each_pair do |name, filter|
136
136
  config = {
137
- type: config[:type].to_s,
138
- operators: config[:operators].keys.map(&:to_s)
137
+ type: filter[:type].to_s,
138
+ operators: filter[:operators].keys.map(&:to_s)
139
139
  }
140
+ config[:single] = true if filter[:single]
141
+ config[:allow] = filter[:allow] if filter[:allow]
142
+ config[:reject] = filter[:reject] if filter[:reject]
143
+
140
144
  attr = resource.attributes[name]
141
145
  if attr[:filterable].is_a?(Symbol)
142
146
  if attr[:filterable] == :required
@@ -94,6 +94,28 @@ module Graphiti
94
94
  next
95
95
  end
96
96
 
97
+ if new_filter[:single] && !old_filter[:single]
98
+ @errors << "#{old_resource[:name]}: filter #{name.inspect} became singular."
99
+ end
100
+
101
+ if new_filter[:allow] != old_filter[:allow]
102
+ new = new_filter[:allow] || []
103
+ old = old_filter[:allow] || []
104
+ diff = new - old
105
+ if diff.length > 0
106
+ @errors << "#{old_resource[:name]}: filter #{name.inspect} whitelist went from #{old.inspect} to #{new.inspect}."
107
+ end
108
+ end
109
+
110
+ if new_filter[:reject] != old_filter[:reject]
111
+ new = new_filter[:reject] || []
112
+ old = old_filter[:reject] || []
113
+ diff = new - old
114
+ if diff.length > 0
115
+ @errors << "#{old_resource[:name]}: filter #{name.inspect} blacklist went from #{old.inspect} to #{new.inspect}."
116
+ end
117
+ end
118
+
97
119
  if (diff = old_filter[:operators] - new_filter[:operators]).length > 0
98
120
  diff.each do |op|
99
121
  @errors << "#{old_resource[:name]}: filter #{name.inspect} removed operator #{op.inspect}."
@@ -72,7 +72,28 @@ module Graphiti
72
72
  operator = param_value.keys.first
73
73
  value = param_value.values.first unless filter.values[0][:type] == :hash
74
74
  value = value.split(',') if value.is_a?(String) && value.include?(',')
75
+
76
+ if filter.values[0][:single] && value.is_a?(Array)
77
+ raise Errors::SingularFilter.new(resource, filter, value)
78
+ end
79
+
75
80
  value = coerce_types(param_name.to_sym, value)
81
+
82
+ value.each do |v|
83
+ if allow = filter.values[0][:allow]
84
+ unless allow.include?(v)
85
+ raise Errors::InvalidFilterValue.new(resource, filter, value)
86
+ end
87
+ end
88
+
89
+ if reject = filter.values[0][:reject]
90
+ if reject.include?(v)
91
+ raise Errors::InvalidFilterValue.new(resource, filter, value)
92
+ end
93
+ end
94
+ end
95
+
96
+ value = value[0] if filter.values[0][:single]
76
97
  yield filter, operator, value
77
98
  end
78
99
  end
@@ -68,6 +68,7 @@ module Graphiti
68
68
  end
69
69
 
70
70
  def check!
71
+ return if scope_proc
71
72
  case type
72
73
  when :has_many, :has_one
73
74
  unless resource.filters[foreign_key]
@@ -14,14 +14,33 @@ module Graphiti
14
14
  end
15
15
 
16
16
  def generate
17
- if params.empty?
18
- path
17
+ on_demand_links(raw_url)
18
+ end
19
+
20
+ private
21
+
22
+ def raw_url
23
+ if @sideload.link_proc
24
+ @sideload.link_proc.call(@model)
19
25
  else
20
- "#{path}?#{URI.unescape(params.to_query)}"
26
+ if params.empty?
27
+ path
28
+ else
29
+ "#{path}?#{URI.unescape(params.to_query)}"
30
+ end
21
31
  end
22
32
  end
23
33
 
24
- private
34
+ def on_demand_links(url)
35
+ return url unless Graphiti.config.links_on_demand
36
+
37
+ if url.include?('?')
38
+ url << '&links=true'
39
+ else
40
+ url << '?links=true'
41
+ end
42
+ url
43
+ end
25
44
 
26
45
  def params
27
46
  @params ||= {}.tap do |params|
@@ -66,10 +66,11 @@ module Graphiti
66
66
  end
67
67
 
68
68
  def wrap_proc(inner)
69
- type_name = @attr[:type]
69
+ _resource = @resource.new
70
+ _name = @name
70
71
  ->(serializer_instance = nil) {
71
- type = Graphiti::Types[type_name]
72
- type[:read][serializer_instance.instance_eval(&inner)]
72
+ value = serializer_instance.instance_eval(&inner)
73
+ _resource.typecast(_name, value, :readable)
73
74
  }
74
75
  end
75
76
 
@@ -44,11 +44,7 @@ module Graphiti
44
44
  proc do
45
45
  if @proxy.query.links?
46
46
  link(:related) do
47
- if prc = sl.link_proc
48
- prc.call(@object)
49
- else
50
- ::Graphiti::Util::Link.new(sl, @object).generate
51
- end
47
+ ::Graphiti::Util::Link.new(sl, @object).generate
52
48
  end
53
49
  end
54
50
  end
@@ -75,7 +71,7 @@ module Graphiti
75
71
  action = sideload.type == :belongs_to ? :show : :index
76
72
  prc = Graphiti.config.context_for_endpoint
77
73
  unless prc.call(sideload.resource.endpoint[:full_path], action)
78
- raise Errors::InvalidLink.new(@resource_class, sideload)
74
+ raise Errors::InvalidLink.new(@resource_class, sideload, action)
79
75
  end
80
76
  end
81
77
 
@@ -1,3 +1,3 @@
1
1
  module Graphiti
2
- VERSION = "1.0.alpha.4"
2
+ VERSION = "1.0.alpha.5"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.alpha.4
4
+ version: 1.0.alpha.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-07 00:00:00.000000000 Z
11
+ date: 2018-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jsonapi-serializable