Been wrestling with trying to get polymorphic serializers working and testing data via rspec. Just upgraded to 0.10+
I found this post, which makes a lot of sense, and does give me a entry into generating the serializations, however, when doing it for polymorphs, I never get the type and id properly named (expecting to see asset_id and asset_type nested)
{:id=>1,
:label=>"Today I feel amazing!",
:position=>0,
:status=>"active",
:media_container_id=>1,
:asset=>
{:id=>4
Test ActiveModel::Serializer classes with Rspec
class MediaSerializer < ApplicationSerializer
attributes :id,
:label,
has_one :asset, polymorphic: true
end
I noticed that the tests dont even seem to properly add the polymorphic identifiers either (ie asset_id, asset_type -- or in the test case imageable_id, imageable_type)
https://github.com/rails-api/active_model_serializers/commit/045fa9bc072a04f5a94d23f3d955e49bdaba74a1#diff-c3565d7d6d40da1b2bf75e13eb8e6afbR36
If I go straight up MediaSerialzer.new(media) I can poke at the .associations, but I cant seem to get them to render as if I was generating a full payload
From the docs
https://github.com/rails-api/active_model_serializers
serializer_options = {}
serializer = SomeSerializer.new(resource, serializer_options)
serializer.attributes
serializer.associations
Im pretty sure Im missing something/doing something wrong - any guidance would be great.
Thanks
It isn't easy to get the effect you are looking for, but it is possible.
You can access the hash generated by the serializer by overriding the associations method.
class MediaSerializer < ApplicationSerializer
attributes :id,
:label,
has_one :asset, polymorphic: true
def associations details
data = super
data[:asset] = relabel_asset(data[:asset])
data
end
def relabel_asset asset
labelled_asset = {}
asset.keys.each do |k|
labelled_asset["asset_#{k}"] = asset[k];
end
labelled_asset
end
end
I learnt alot about ActiveModelSerializer to get the hang of this! I referred to Ryan Bates' podcast on the topic:
http://railscasts.com/episodes/409-active-model-serializers
In there he describes how you can override the attributes method and call super to get access to the hash generated by the serializer. I guessed I could do the same trick for the associations method mentioned in your post. From there it takes a little bit of Ruby to replace all the keys, but, if I have understood correctly what you require, it is technically possible.
Hope that helps!
Related
I get a trouble about customize node label in Neo4jrb.
class Core::Product
include Neo4j::ActiveNode
id_property :id
property :name
property :code
property :stock_quantity, type: Integer
property :sale_price, type: Float
property :last_update, type: DateTime
end
When I create new node, it will has label as Core::Product. I want it to be Product instead.
According this post It seem that _classname property could resolve my problem but I have no idea how to implement it.
Any ideas?
Co-maintainer of Neo4j.rb here and author/responsible party for _classname. _classname is a very legacy option at this point, a holdover from when some DB responses didn't include node labels or relationship types. You can override automatic label assignment by calling self.mapped_label_name = in your model.
class Core::Product
include Neo4j::ActiveNode
self.mapped_label_name = 'Product'
# etc,...
end
You'll also want to be aware that the auto-location of association models won't work correctly, so instead of this:
has_many :out, :products, type: 'HAS_PRODUCT'
You'll need to do this:
has_many :out, :products, model_class: 'Core::Product', type: 'HAS_PRODUCT'
We have an open issue, https://github.com/neo4jrb/neo4j/issues/753, that discusses it. I namespace my models to organize code but want my labels to omit them, so I'd love a configuration option that handles this for me.
FOLLOW-UP
I just merged https://github.com/neo4jrb/neo4j/pull/790 into master. It lets you tell the gem to ignore module names when creating labels. I'm going to put it to work in some code this week but if you'd like to test it out, we always love feedback.
What is the best way/practice to declare a Rails(4) array field (with mysql database)? I need to store some ids into that array. I tried to do this using the ActiveRecord Serializer and I customized the attribute accessors so my field can behave like an array.
class OfficeIds < ActiveRecord::Base
serialize :office_ids
def office_ids=(ids)
ids = ids.join(",") if ids.is_a?(Array)
write_attribute(:office_ids, ids)
end
def office_ids
(read_attribute(:office_ids) || "").split(",")
end
end
I feel that this is not the best approach for this kind of situation. Any help would be appreciated. Thanks!
If you're using the serializer, there's no need to write a wrapper method for this. You should be able to assign arbitrary objects to that field:
ids = OfficeIds.new
ids.office_ids = [ 1, 2, 3 ]
ids.save
It is rather odd to have a model called OfficeIds though, as a plural name for this willc cause all kinds of trouble. Are you sure you don't want a traditional has_many relationship for these?
I'm quite stumped trying to model self-referential, many-to-many, symmetrical entity using Datamapper:
I want to keep track of compatibility of various software releases I'm tracking:
class SoftwareRelease
include DataMapper::Resource
property :id, Serial
property :software_release_type_id, Integer
property :version, String
belongs_to :software_release_type
has n, :compatibilities, child_key: [ :source_id ]
has n, :compatible_releases, self, through: :compatibilities, via: :target
end
class SoftwareReleaseType
include DataMapper::Resource
property :id, Serial
property :name, String
property :short_name, String
has n, :software_releases
end
class Compatibility
include DataMapper::Resource
belongs_to :source, 'SoftwareRelease', key: true
belongs_to :target, 'SoftwareRelease', key: true
end
A given software release 'a.b.c' can have a type of 'hardware platfrom foo', while a given software release 'd.e.f' can have a type of 'hardware platform bar'.
To establish compatibility between the software release in the above code, I must perform 2 adds
release_a_b_c.compatible_releases << release_d_e_f
release_d_e_f.compatible_releases << release_a_b_c
That's kinda ugly.
I'd love to be able to do just one add and have a symmetric relationship established. I can obviously wrap 2 pushes with an instance method in SoftwareRelease, but that feels like I'm sweeping the ugliness under a rug. Any ideas for an elegant solution?
Much obliged,
Pawel
PS
Long time ago, active-record had acts_as_network plugin which did something similar. Haven't been able to find anything in the DM world.
I don't think there is anything in DM or any of its plugins to do what you want, but I would like to posit that writing an instance method to create both one-way relations is in fact fairly elegant and I wouldn't consider it "sweeping the ugliness under the rug" at all.
Part of the power of implementing object relations in classes is that the programmer gets to extend those classes in meaningful ways by creating instance methods inside the class. You have a need for a symmetrical relationship between objects, so writing that simple instance method is meaningful and self-documenting. And there is no risk of side effects or inappropriate calls because it is a public method of that specific class.
I'm not really saying anything new, here, but I think your solution is perfectly acceptable. Frankly, I would rather see relatively rare use cases pushed into the programmer's domain, rather than clutter up the DataMapper code. I don't need one of those big fat Victorinox pocket knives with every conceivable tool. :)
I know that it could sound an "easy-question" but I am new in rails and I don't know if I am structuring correctly my app.
My app is "easy" it has to connect a webpage and collect all JPG links and store the links in a database.
In my "model folder" I have defined my functions:
class JPG < ActiveRecord::Base
acts_as_paranoid
validates :title, :link, presence: true
validates :link, uniqueness: true
attr_accessible :tag_list, :id, :title
#acts_as_taggable
def main_web
require 'rubygems'
require 'scrapi'
require 'uri'
Scraper::Base.parser :html_parser
scraper = Scraper.define do
array :items
process "div.mozaique>div", :items => Scraper.define {
process "div.thumb>a", :link => "#href"
}
result :items
end
uri = URI.parse(URI.encode(web))
return scraper.scrape(uri)
end
end
As you can see the function main_web returns an array.
Well my question is "easy". If I want to call the function and to storage the information in the database, how do I have to do it?
I don't want to show the information in the screen I only want to storage the data in the database but from where do I have to do the call? view? or controller?
I suggest you read more about the controller-view model. You need to call and store in the database from the controller.
If main_web returns an array then wouldn't #jpg = #jpg.main_web mean that #jpg is now an array instead of the Active Record model? That would seem to be why it wouldn't save. You might want to add a field to your database that holds the array you generate from main_web. Then instead of returning it you could just call save on your #jpg model.
Also a nice tip would be if you are having issues with models or other things really try typing 'rails console' on command line. You could run your 3 lines of code there to see if they worked. This wouldn't solve your issue but might allow you to play around with some things.
#jpg = JPG.new
#jpg = #jpg.main_web
#jpg.save
Replacing my initial answer after giving it more consideration...
You are doing this:
return scraper.scrape(uri)
which you assign to the #jpg instance. Thus, #jpg is no longer an instance of JPG and cannot be saved as such.
Also, it is not necessary to call and store from the controller. The controller is more a decision maker than anything. It determines state and routes control and information to the desired view or model. The model is where data should be managed. Though, that may be a matter of debate and is my opinion.
Recommended reading:
http://sirupsen.com/what-I-wish-a-ruby-programmer-had-told-me-one-year-ago/
On the Rails API side I have the following 2 models:
class Grower < ActiveRecord::Base
has_many :addresses, as: :addressable
accepts_nested_attributes_for :addresses
end
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
end
as well as a Growers controller which returns and can create/update Growers with embedded Addresses attributes. I also have an Addresses controller with proper routing so that Addresses can be viewed/created/updated for a specific Grower. The latter is more of an "in-case", and I'm not sure I'll be needing to return/update/create addresses as a separate payload.
I am starting to try and piece together an Ember app that would allow me to view/edit/create a Grower at the same time as its Address(es). Can anyone point me to an existing real or sample app that does this? I will be posting my code as I go along, but I already have an idea of some areas where I will be running into trouble:
Rails returns/expects nested params named addresses_attributes. Ember, I am sure, doesn't use that convention. What's the best approach of resolving this?
Because of the polymorphic association (objects other than Grower can be addressable), on the API/Address side, to get the correct belongs_to object, Rails uses addressable_id in conjunction with addressable_type. In this example the addressable_type would be "Grower", and the addressable_id would be the grower_id value. How would one go about translating that on the Ember side?
UPDATE:
I got it to work at least a couple different ways. My preferred solution, at least for this particular case, is in the answer section.
Here is a sample of code based #yuяi's strategy that worked well for me:
App.Post = DS.Model.extend
comments: DS.hasMany('comment')
App.PostSerializer = DS.ActiveModelSerializer.extend( DS.EmbeddedRecordsMixin,
attrs:
comments: {embedded: 'always'}
keyForAttribute: (attr) ->
if attr == "comments"
"comments_attributes"
else
#_super(attr)
)
This solution worked well with Ember 1.6.1 and Ember Data 1.0.0-beta.8.2a68c63a.
I found a couple ways to get it done, but the final approach doesn't require any changes on the Rails/API side.
On the client (Ember) side:
I added the addresses hasMany property to the App.Grower model. I also mapped it on the RESTAdapter to what's expected from the API, by setting the key for addresses to addresses_attributes.
I added the grower (for now - will change to addressable once I have other addressable models) belongsTo property on App.Address. It's not really required for what I'm doing, but it might be useful in the future.
I set the addresses on the RESTAdapter to be embedded: 'always'.
On the App.GrowersEditController, I just do a model.save (transaction.commit), and the child addresses are automatically saved via the API call.
On the App.GrowersAddController, I use the App.Address.createRecord and App.Grower.createRecord methods using the user-entered Grower and Address data. Then I use the pushObject method to append the Address to the Grower, and then call a save on the Grower (commit on transaction). Again, the address data gets submitted and saved automatically.