Reduce need to work arounds
While perhaps better as three separate issue, they are all interconnected so I will group them into one group of bugs.
Custom endpoints don't respond to route_format changes. We have fixed this with
JsonApiClient::Query::Requestor.class_eval do
def custom(method_name, options, params)
path = resource_path(params)
params.delete(klass.primary_key)
path = File.join(path, method_name.to_s)
request_method = options.fetch(:request_method, :get).to_sym
query_params, body_params = [:get, :delete].include?(request_method) ? [params, nil] : [nil, params]
path = path.dasherize if klass.route_format == :dasherized_route
request(request_method, path, params: query_params, body: body_params)
end
end
Has one associations has a bug from the latest code change, fixed by applying .underscore
JsonApiClient::Associations::HasOne::Association.class_eval do
def load_records(data)
record_class = JsonApiClient::Utils.compute_type(klass, data["type"].underscore.classify)
record_class.load id: data["id"]
end
end
And finally relationship_data_for was falling apart when there was no key? method on the relationship_definition which is triggered by assigning and array to a has_many relation which is fixed by making more if's as I am not too sure of all the inner workings
JsonApiClient::Resource.class_eval do
def relationship_data_for(name, relationship_definition)
# look in included data
if relationship_definition.try(:key?, "data")
if relationships.attribute_changed?(name)
return relation_objects_for(name, relationship_definition)
else
return included_data_for(name, relationship_definition)
end
else
if relationships.attribute_changed?(name)
return relationship_definition.to_a
else
return included_data_for(name, relationship_definition)
end
end
url = relationship_definition["links"]["related"]
if relationship_definition["links"] && url
return association_for(name).data(url)
end
nil
end
end
Another oddity discovered required a wee bit more work.
class Foo < JsonApiClient::Resource
property :fuzz
end
class Bar < JsonApiClient::Resource
has_one :foo
has_many :foos
end
bar = Bar.new
bar.foo = Foo.new(fuzz: "Buzz") #<Foo:@attributes={"type"=>"foos", "fuzz"=>"Buzz"}>
bar.foo #<Foo:@attributes={"type"=>"foos", "id"=>nil}>
bar.foo.fuzz #nil
bar.foos = [Foo.new(fuzz: "Buzz"), Foo.new(fuzz: "Buzz")] #[#<Foo:@attributes={"type"=>"foos", "fuzz"=>"Buzz"}>, #<Foo:@attributes={"type"=>"foos", "fuzz"=>"Buzz"}>]
bar.foos.first.fuzz #nil
@code-bunny this is not an oddity. According to spec client can send only relationship id and type currently when you assign relationship to resource it only stores it's id and type when you accessing relationship via getter method it recreates object according to id and type and cache it (for performance).
Can you give me usecase when you need to access relationship attributes of object which you currently create/update ?
@senid231 so an example where we would want to access the relationship before committing it is for instance when using a wizard like an order checkout.
class Item < JsonApiClient::Resource
property :title
end
class Order < JsonApiClient::Resource
include SomeStepWizard
has_many :items
end
order = Order.new
order.step # 1
order.items = [Item.find(1).first]
order.next_step
# in some view
order.items.each { |item| puts "Thank you for wanting to buy #{item.title}" }
order.save
Basically when we assign a resource it should behave in the same was is if we had run an include on the endpoint. This would keep the behaviour consistent as demonstrated in my example.
bar = Bar.includes(:foo).find(1).first
bar.foo.hello # "world"
bar = Bar.new
bar.foo = Foo.find(1).first
bar.foo.hello # "world"