attachment_fu: Don't reload thumbnails - ruby-on-rails

I've got a nice little "photos" class that has attached images. When I go to the page to sort the photos' order, it iterates though each photo, sets the new "sort" value, and saves it. All good so far.
Problem is, I've noticed this behavior is fairly slow. Turns out, attachment_fu reloads the thumbnail on every save - regardless of whether or not there's new image data to work with.
Obviously this system has been well-thought-out, so I'm only left to assume that a provision exists for this situation. How do I tell attachment_fu not to regenerate the thumbnails when it's not appropriate?
Thanks, --Matchu
Edit: I just remembered that, for this particular situation, I can use update_attribute to dodge all the validations and other callbacks. However, this isn't really a viable answer to the whole big scenario. What am I missing?

Went in and hacked attachment_fu a bit, and rewrote the save_attachment? behavior. Pretty much, I added some new conditions: in addition to a temp file existing, one of the following must be true:
No file for the image already exists (using the full_filename attribute).
The image data was explicitly updated using the uploaded_data= method.
The image is a thumbnail.
It passes all three test cases - new photo uploads, edit photo images, and editing non-image photo data - but I haven't really tested this out in the wild just yet. I'll probably have to make a few fixes; we'll see what happens.

The only moderately useful thread I've seen on this topic is here:
http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/709d97e06b373786
I think Matchu's solution is probably the correct one with a quick review of the attachment_fu code. I'd love it if Matchu could share a patch or a snippet of his modified save_attachment? method. I'm about to dig into this on my own, since this has become a problem for me and it'll probably be less work than replacing attachment_fu entirely...
Update
With Matchu's outline, I came up with a short (if inelegant) solution that seems to work after light testing.
I modifed save_attachment? in attachment_fu/attachment_fu.rb:
def save_attachment?
return false unless (thumbnail || !full_filename || #active_upload) #added
File.file?(temp_path.to_s)
end
... to check the conditions Matchu laid out. I couldn't come up with an elegant way to tell whether data had been passed along to the uploaded_data= setter method (if anyone has a better way to do this, I'm all ears; I'm still a ruby/rails noob) so I also added a line to uploaded_data= to set the global variable #active_upload:
def uploaded_data=(file_data)
return nil if file_data.nil? || file_data.size == 0
self.content_type = file_data.content_type
self.filename = file_data.original_filename if respond_to?(:filename)
#active_upload=true # added
if file_data.is_a?(StringIO)
file_data.rewind
self.temp_data = file_data.read
else
self.temp_path = file_data
end
end
Hope that helps, and if anyone has a more elegant way to handle what I did there with the global variable, I'd love to hear it.

Related

Independent ActiveRecord query inside ActiveRecord::Relation context

There is some ruby on rails code
class User < ActiveRecord::Base
def self.all_users_count
User.all
end
end
User.all_users_count
returns, for example, 100
User.limit(5).all_users_count
Now it return 5 because of ActiveRecord::Relation context, in despite of i wroute name of class User.all instead simple all
(.to_sql show that query always contains limit or where id or other things in other cases)
So, how can i make context-independent AR queries inside model methods? like User.all and others?
Thank you!
Ps. Or maybe my code has an error or something like this, and in fact User.all inside any methods and context always must returns correct rows count of this model table
This is very weird and unexpected (unfortunately I can't confirm that, because my computer crashed, and have no rails projects at hand).
I would expect
User.all
to create a new scope (or as you call it - context)
Try working around this with
User.unscoped.all
Edit:
I tried it out on my project and on clean rails repo, and the results are consistent.
And after thinking a bit - this is maybe not even an issue - I think your approach could be faulty.
In what scenario would you chain User.limit(2).all_users_count ?? I can't think of any. Because either you need all users count, and you call User.all_usert_count (or just User.count)
... or you need something else and you call User.limit(2).where(...) - there's no point in calling all_users_count in that chain, is it?
And, when you think of it, it makes sense. Imagine you had some different method like count_retired, what would you expect from such call:
User.limit(2).count_retired ?
The number of retired users not bigger than 2, or the number of all retired users in the system? I would expect the former.
So I think one of two possibilities here:
either you implemented it wrong and should do it in a different way (as described above in the edit section)
or you have some more complex issue, but you boiled your examples down to a point where they don't make much sense anymore (please follow up with another question if you please, and please, ping me in the comment with a link if you do, because it sounds interesting)

Rails - ActiveRecord Dirty - Getting associated objects from the changes hash

I'm working on an audit trail of sorts for an app so that the user can see what is being changed throughout the system.
I have a hash of changes from ActiveRecord Dirty, like follows:
{"ingredient_type_id"=>[nil, 199575006], "name"=>[nil, "asdfg"], "amount"=>[nil, 3.0], "unit"=>[nil, "x"], "notes"=>[nil, "asdf"]}
This works great and I can parse what I need to output and create database records with the info.
I just have one question - How can I get associated objects from this? In this case, the ingredient_type? I actually want to output something like:
"Ingredient type was changed to #{IngredientType.find(199575006).name}."
But I'm not sure how I would parse that hash on a dynamic basis to do that.
Pretty much the way you've suggested I'd have thought, But you don't need to parse the hash for the changes, Dirty gives you much more than that
if ingredient_type_id_changed?
unless ingredient_type_id.blank?
ingredient_name = IngredientType.find(ingredient_type_id).name
else
ingredient_name = 'blank'
end
end
You might even be able to do ingredient_type.name, Not sure at that point if active record dirty will let you go through the association. If you test it (or if anyone else knows) let me know

first_or_create: determining which is called

I call first_or_create like so:
collection = Collection.first_or_create(:title => title)
Is there a way to determine if the result is an existing entry or a freshly created one? So far the best solution I've come up with is to use first_or_initialize:
collection = Collection.first_or_initialize(:title => title)
if collection.id.nil?
<process>
collection.save
end
But this feels a bit hacky. Is there a way to get this information directly from first_or_create?
first_or_create takes a block that'll only be executed if a new record is created, you can set some flag inside that block to indicate it's a new record, for example
MyObject.where(attr: "value").first_or_create do |obj|
#my_object_created = true
end
As far as I know you can't know. Two options are to check the created_at time (unreliable), or instead use first_or_initialize, then check to see if new_record? is true, and if so, do your other operations and then call save!. This may be the best approach for you anyway, since you may very well not want to finalize the save until the other relations are saved, and you probably want to do all of that in the same database transaction.
Using first_or_create you can't know for sure is it a newly created object or one from the database. Possible tricky solution is to compare created_at value with current time. This works if you don't create objects often.
Btw, why you need to know is it the newly created object or not?

Rails 3: Caching to Global Variable

I'm sure "global variable" will get the hair on the back of everyone's neck standing up. What I'm trying to do is store a hierarchical menu in an acts_as_tree data table (done). In application_helper.rb, I create an html menu by querying the database and walking the tree (done). I don't want to do this for every page load.
Here's what I tried:
application.rb
config.menu = nil
application_helper.rb
def my_menu_builder
return MyApp::Application.config.menu if MyApp::Application.config.menu
# All the menu building code that should only run once
MyApp::Application.config.menu = menu_html
end
menu_controller.rb
def create
# whatever create code
expire_menu_cache
end
protected
def expire_menu_cache
MyApp::Application.config.menu = nil
end
Where I stand right now is that on first page load, the database is, indeed, queried and the menu built. The results are stored in the config variable and the database is never again hit for this.
It's the cache expiration part that's not working. When I reset the config.menu variable to nil, presumably the next time through my_menu_builder, it will detect that change and rebuild the menu, caching the new results. Doesn't seem to happen.
Questions:
Is Application.config a good place to store stuff like this?
Does anyone see an obvious flaw in this caching strategy?
Don't say premature optimization -- that's the phase I'm in. The premature-optimization iteration :)
Thanks!
I would avoid global variables, and use Rails' caching facilities.
http://guides.rubyonrails.org/caching_with_rails.html
One way to achieve this is to set an empty hash in your application.rb file:
MY_VARS = {}
Then you can add whatever you want in this hash which is accessible everywhere.
MY_VARS[:foo] = "bar"
and elsewhere:
MY_VARS[:foo]
As you felt, this is not the Rails way to behave, even if it works. There are different ways to use caching in Rails:
simple cache in memory explained here:
Rails.cache.read("city") # => nil
Rails.cache.write("city", "Duckburgh")
Rails.cache.read("city") # => "Duckburgh"
use of a real engine like memcached
I encourage you to have a look at http://railslab.newrelic.com/scaling-rails
This is THE place to learn caching in all it's shapes.

Rails organizing code

i happen to be kinda picky when programming something big. I try to find the best way to do it it terms of speed and complexity. Since i've been learning Rails the previous 3 months, i try to find the best techniques for everything. I would like to ask you how you would go about writing some code like this :
#defender = User.find_by_id(user_id)
#attacker = current_user.clone
#attacker_starting_attribs = current_user
#defender_starting_attribs = #defender.clone
#defenderWeapon = #defender.getEquippedWeapon
#attackerWeapon = #attacker.getEquippedWeapon
#combat = Combatant.fight(#attacker, #defender)
This code is about the battle outcome between two persons in a browser game. The code works well, but i've some problems in terms of good coding. In fact, i know that my code is bad here, that's why i ask you what a better version would be. Let me explain what happens in this code.
#defender is given by user_id, so i guess that this part is needed. Now, in #attacker i'm cloning the current_user. The reason is that Rails works on objects and current_user will be changed inside Combatant.fight. I need both the new hp and the old hp and that is why i'm cloning the object. The defender and attacker starting attribs illustrate that concept. Now, i get the weapons in instance variables, so that i can get their information inside the final view.
However, the weapons are needed inside the fight function and i execute the same getEquippedWeapon twice again inside fight(). I was not so comfortable with something like fight(#attacker, #defender, #attacker_weapon, #defender_weapon), but i don't also like the idea of repetition. So, i would like an opinion on that.
#combat is a hash containing the result of the combat. Fight happens and i get that hash back in the view.
I'm not pleased with my coding on that stage and i want your opinion. How would you do it ? Is there maybe a design pattern for that ? Please tell me your opinion.
Thanx :)
I'm finding it difficult to completely understand what you're trying to do. I get the gist of it though (2 people fighting). I won't be able to provide an answer yet, but hopefully this gets the ball rolling:
From the code you provided, #attacker_starting_attribs and #defender_starting_attribs aren't being used.
As far as "good techniques", I try to stay as OO as possible. Instead of
Combatant.fight(#attacker, #defender), I would do #attacker.fight(#defender)
As a ruby convention, method names are underscored. In your case, .get_equipped_weapon instead of .getEquippedWeapon, or even better .equipped_weapon.
Anyways, I bet if you provided more code, you'd get more answers.

Resources