Creating a "feed" from multiple rails models, efficiently? - mysql

This is a follow up to Creating "feeds" from multiple, different Rails models. In this question, tadman suggests this method of creating a user feed of recent items from three models (Ticket, Post, Report):
#items = [ Ticket, Post, Report ].inject([ ]) do |a, with_class|
a + with_class.find(:all, :limit => 10, :order => 'created_at DESC')
end.sort_by(&:created_at).reverse[0, 10]
He suggests this as a method that will work, but that won't necessarily be the most efficient. He suggests as well than an alternative method would be to "create an index table that's got a polymorphic association with the various records."
I'm really interested in learning more about this alternative solution, it seems both more efficient and more elegant. Can anyone tell me how one would do this? Let's use the same background info from the last question as a base.

What I did once was, have a separate model Feed (feeds_controller) and update it in after_save callbacks to all the interesting models. So for example if you have a model Article, have an after_save callback:
def after_save
feed = Feed.new
feed[:model_name] = 'Article'
feed[:item_id] = id
feed.save
end
then, you can access the feed linearly just like any other model. The computational expense is incurred when saving the the feed, not reading from the feed.
Oh, you can also have Feed has_many :article; has_many :user, has_many :status and so forth, and then :include all those resources in the feed, and render them in views. Hope this makes sense ;-)

Related

ActiveModelSerializers Polymorphic Json

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!

Thinking Sphinx Has Many Through Rails

Can you please help me in thinking sphinx?
Relationship:
Continent
has_many :countries
has_many :country_reports, through: :countries, :class_name => "Report"
has_many :publishers, through: :country_reports
Expected Output:
I want to find Continent.first.publishers
Please tell me how to write this in thinking sphinx rails
As I answered on the Thinking Sphinx Google group:
Because you’re searching on publishers, it’s the Publisher index you’ll need to modify to get this to work. I’m presuming a publisher belongs_to :country_report, country report belongs_to :country, and country belongs_to :continent.
If you’re using SQL-backed indices (using the :with => :active_record option), then you’ll want the following in your Publisher index:
has country_report.country.continent_id, :as => :continent_id
If you’re using real-time indices (:with => :real_time), then it’s the same, but you must specify the type as well:
has country_report.country.continent_id, :as => :continent_id, :type => :integer
However, if there’s a has_many or has_and_belongs_to_many instead of belongs_to in that chain of associations from publisher to continent, then it’s a little more complicated. Also, in this case, it might be possible to have more than one continent for a publisher, so we’re dealing with multiple values here.
For SQL-backed indices, a minor change, alter the associations chain:
has country_reports.country.continent_id, :as => :continent_ids
But with a real-time index, it’s better to have a method on the Publisher model that returns the necessary value or values, and then use that in the index:
# in app/models/publisher.rb
def continent_ids
country_reports.collect(&:country).collect(&:continent_id)
end
# in app/indices/publisher_index.rb
has continent_ids, :type => :integer, :multi => true
And then, once you’ve rebuilt your index, you can search as follows (with the attribute being plural if appropriate):
Publisher.search “foo”, :with => {:continent_id => continent.id}
This may work as well (the multiple levels of associations may confuse things though):
continent.publishers.search “foo”

Ruby advanced active record queries across multiple tables

I'm fairly new and am coming across a problem as I progress in the application I'm working on.
I have the following relationships set up
game
belongs_to :challenge
has_many :game_players, through: :playersessions, source: :user
has_many :playersessions
challenge
has_many :phrases
has_many :games
phrase
has_many :playedphrases
has_many :playersessions, through: :playedphrases
playedphrase
belongs_to :playersession
belongs_to :phrase
playersession
has_many :playedphrases
has_many :phrases, through: :playedphrases
All these relationships are working in my program, but I need to start doing some more advanced querying. For instance, I would like to find the remaining phrases. In english it would be "find this Game's Challenge's Phrases and remove the this Game's Playersession's Phrases". I believe the problem lies in the fact that #game.challenge.phrases returns Phrase objects and #game.playersessions must be iterated through to find all the phrase objects.
Any guidance on how to handle this type of query? Please let me know if any other info would help out.
I finally figured out a way to make this work - I don't know if it is the most eloquent ruby, but it works.
def determine_remaining_phrases
remain = #game.challenge.phrases
#playersessions.each do |session|
remain -= session.phrases
end
return remain
end
Basically I needed to iterate through the playersessions and remove the associated phrases each pass through. This leaves me with an array of phrase objects. If someone has a more eloquent answer, please let me know.

Two applications sharing the same database Rails

Not sure if im overthinking this but would like some guidance and advice on this scenario.
I have two applications, one which you can log into and perform your basic CRUD, i.e create blog posts and the second a view of the same application, but no ability to log into and no ability to create a blog post. The second application would read from the same database as the first.
My question is how do i get the two applications reading from the same model in development and do i still need to create my models with columns etc in the view only app?
Example
App 1 (With CRUD)
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: :slugged
belongs_to :category
belongs_to :user
has_many :images, as: :imageable, :dependent => :destroy
accepts_nested_attributes_for :images
attr_accessible :comments, :title, :category_id, :user_id, :image_id, :images_attributes, :imageable_id, :imageable_attributes, :slug
#Validations
validates :comments, :presence => {:message => 'Add your Comments'}
validates :title, :presence => {:message => 'Add your Title'}
#scopes
scope :latest_posts, :order => "posts.created_at DESC"
#scope :ruby_posts, :include => :category, :conditions => {"categories.name" => "Ruby"}, :order => "posts.created_at DESC"
def self.search(search)
where("title like ?", "%#{search}%")
end
end
App 2 (No Crud)
class Post < ActiveRecord::Base
#do i still provide all associations and attributes here?
end
I would really appreciate an explanation of what is going on in this kind of setup
thanks
You will need to have your models either shared or duplicated between the two applications. This means your Post example for App 2 would need to have the associations, scopes, and methods.
I have done this once before by moving all of the model classes into a gem that is included into both projects. This was actually pretty easy to do.
You do not need to share migrations though. If they are pointing to the same database, migrations should only live in one app, probably the one doing the writing. I wouldn't even let db/schema be checked in on App 2 and would maybe go further and disable rake db:* tasks.
Even if you move your models into a shared gem, you might want your "read only" app to enforce its read-only behavior by clearing permissions to assign attributes (attr_accessible and accepts_nested_attributes_for) or somehow preventing ActiveRecord models from saving in its environment. One quick and dirty way would be to monkey patch ActiveRecord::Base#save in an initializer for App 2 and have it do nothing or raise an error.

return array in rails and storage it

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/