Rails organizing code - ruby-on-rails

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.

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)

Navigating through source code efficiently in Ruby

I was trying to find the method being called when Item.where(dst: "video") is called (Item being a Mongoid model). Looking up in the source code, I see that criteria.rb is the place to go to. However, def where calls super. Then Origin::Selectable (included inside Origin::Queryable) defines it:
def where(criterion = nil)
criterion.is_a?(String) ? js_query(criterion) : expr_query(criterion)
end
Now, I would have to see where js_query and expr_query are, see what they do and so on.
It gets tough going through all this source code and modules, finding all the methods and then trying to figure out how it works.
Is there a better way to do this process to find out how things work?
You probably need to improve your editor experience. There are three remarkable abilities (besides many others like Eclipse/Aptana, NetBeans, etc):
RubyMine — not free, but probably the best one;
Atom Editor with RSense plugin — free (plugins are also available for SublimeText and TextMate2;
vim/emacs with [c|e]tags.
Depending on your choice you yield an ability to quickly navigate through your code with either Ctrl+Click or with your preferred keyboard shortcut.
Here on SO this question was asked an amount of times as well: https://stackoverflow.com/search?q=best+ruby+editor
Hope it helps.
If you know the class of the receiver (say A) and the method name (say foo), then you can do:
A.instance_method(:foo).source_location
That will give the file name and the line number in most cases. If it returns nil, then it is likely a C-defined method, which does not rely on other Ruby methods.
Another way is to use the pry gem or the method_source gem.

Magento -> Is this an anonymous function?

We have a lot of code in our codebase that was done by a third-party developer. They modified many places in the core and we wanted to figure out exactly what is happening.
They have code like this:
$pidsess = Mage::getSingleton('core/session', array('name'=>'frontend'));
$pidsess->setNA_pid(array("PID" => $pid, "PartInfo" => $return_data['result']));
What this does is store a "globally available variable" that can be called at anytime the Magento Session is alive like this:
$pidsess = Mage::getSingleton('core/session', array('name' => 'frontend'));
$piddata = $pidsess->getNA_pid();
Where can I find the code that is setting this "variable"? I have searched for 'setNA_' and found several calls but none of them the actual function call.
Is this a part of Magento's stupid EAV architecture where you can make up your own functions to set EAV values? If so, where would the values be stored?
Calling something stupid just because you didn't have time to understand and/or didn't like it certainly isn't going to help. However, as with many magento base classes, it probably just sets a variable into the magento session that ends somewhere in the session storage.
I'd start with Mage::log(print_r($_SESSION,1) to see if there is really an ['NA_pid'].
And it has nothing to do with EAV.

Rails: How to simplify ".select(:my_field).map(&:my_field)"?

To get the invoice numbers of selected jobs I do:
Job.where(...).map(&:invoice_number)
Since the invoice numbers is all I need, I thought to add select(:invoice_number) (I assume the SQL query becomes more efficient this way):
Job.where(...).select(:invoice_number).map(&:invoice_number)
Is there a better way to do the same ? (the .select(:invoice_number).map(&:invoice_number) part seems inefficient to me)
You could always use select_values. Something akin to:
Job.connection.select_values(Job.where(...).select(:invoice_number).to_sql)
This avoids the instantiation of ActiveRecord objects.
I know that this question is rather old, but in case anybody else checks this out, this can be achieved with pluck (http://apidock.com/rails/ActiveRecord/Calculations/pluck). As per http://edgeguides.rubyonrails.org/active_record_querying.html#pluck "pluck makes it possible to replace code like: Client.select(:id).map(&:id) with Client.pluck(:id)"
Would love to be proven wrong, but.. I don't think it's possible
Since all the active record methods are chainable, a method that returned an array of strings would break this. If such a method existed then it wouldn't be in AR, I can't think of where else to look..
You can use GROUP_CONCAT.
invoice_numbers = Job.where(...).select('group_concat(invoice_number) as invoice_numbers').
first.invoice_numbers.split(',').map(&:to_i)
This approach is too long and is not very obvious, but it will run faster than
Job.where(...).select(:invoice_number).map(&:invoice_number)

attachment_fu: Don't reload thumbnails

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.

Resources