Preamble
I'm developing a card game server in Rails. The application records what a player is expected to do via a tree of instances of the PendingAction model.
class Player < ActiveRecord::Base
has_many :cards
has_many :pending_actions
end
At any time, the player needs to act on any leaf PendingActions - there may be more than one, for instance:
1. End turn
|
+- 2. Do thing A
| |
| + 3. Do thing pre-A
|
+- 4. Do thing B
|
+ 5. Do thing pre-B
The view code presents the player with one or more forms, requesting their choice for each leaf action. For instance, in the above case, forms would be presented to solicit input for actions 3 and 5.
When the player makes a choice for, say, action 5, that action is destroyed. However, processing their choice may cause them to need to make another choice before the parent (action 4) can happen. Like so:
1. End turn
|
+- 2. Do thing A
| |
| + 3. Do thing pre-A
|
+- 4. Do thing B
|
+ 6. Do other thing pre-B
Action 4 therefore needs to be on-hand, so action 6 can be created as its child. At present, I am simply passing action 4 around as a function argument. However, I've just come across a situation where it would really help to be able to have access to it in a before_save hook on a Card object.
Question
Where is the best place to store an object, associated with a Player, that I can access from all models related to that Player, which is valid throughout - but not beyond - a single HTTP request?
I am running under Heroku, with 1 dyno - so I believe I'm single-threaded.
It depends on which environment you work, but lets assume that this is single-threaded environment. Next, first thought was to create instance attribute to manually assign and retrieve anything. It is will work fine when Player instance is the same for all places.
If not, I suggest to introduce class variable, some sort of cache, that will hold Player#id => <some_obj> reference, and with helper accessors, like Player#some_obj which will query Player for object by self.id.
That way you will be ensured that for all Player with the same Id, some_obj will be the same. Also, you will need to deside, when to empty that class-cache. For Rails, i suppose after_filter is good enough.
Related
I'm using the AR includes method to execute a LEFT OUTER JOIN between objects User and Building, where a User may or may not have a Building association:
users = User.includes(:building).references(:buildings)
Since I'm using references, any associated Building objects will be eager loaded.
My expectation was that I would then be able to iterate through the list of users, and check whether a user had a building associated with them without triggering additional queries, but I see that in fact whenever I try to access the building property of a user that doesn't have one, AR makes another SQL call to try and retrieve that building (though on subsequent tries it will just return nil).
These queries are obviously redundant as the association would have been loaded during the initial join, and seems to defeat the whole purpose of eager loading with includes/references, as now I'm looking at N times the number of queries equal to the number of empty associations.
users.each do | user |
# This will trigger a new query when building is not present:
# SELECT "buildings".* FROM "buildings" WHERE "buildings"."address" = $1 LIMIT 1 [["address", "123 my street"]]
if user.building
puts 'User has building'
else
puts 'User has no building'
end
end
User class:
class User < ActiveRecord::Base
belongs_to :building, foreign_key: 'residence_id'
end
Is there a way to check the presence of the users' building association without triggering extra queries?
ON RAILS 4.2.0 / POSTGRES
UPDATE:
Thank you #BoraMa for putting together this test. Looks like we're getting different behavior across recent Rails versions:
OUTPUT (RAILS 4.2.0):
User 1 has building
User 2 has building
User 3 has no building
D, [2016-05-26T11:48:38.147316 #11910] DEBUG -- : Building Load (0.2ms) SELECT "buildings".* FROM "buildings" WHERE "buildings"."id" = $1 LIMIT 1 [["id", 123]]
User 4 has no building
OUTPUT (RAILS 4.2.6)
User 1 has building
User 2 has building
User 3 has no building
User 4 has no building
OUTPUT (RAILS 5.0.0)
User 1 has building
User 2 has building
User 3 has no building
User 4 has no building
Take aways:
This issue was limited to "dangling foreign keys (ie the residence_id
column is not nil but there is no corresponding building object)"
(THANKS #FrederickCheung)
The issue has been resolved as of Rails 4.2.6
Sounds like you got bit by a bug in Active Record, that was fixed in rails 4.2.3.
In the case where the column was nil Active Record already knows that it doesn't even need to try loading the associated object. The remaining cases were the ones impacted by this bug
Seems like a typo, please notice building instead of buildings: User.includes(:building).references(:buildings)
That should trigger the big query that uses the format of AS tX_rY for each association and table.
It seems that since rails 4.1 there are potential clashes with how just how implicit #includes should be, see the following open issue.
This code is all untested for syntax, but there would be two approaches I would try:
1/ Make the eager loading implicit
users = User.eager_load(:building).preload(:buildings)
2/ Separate out the two types of users, ones where the building is attached, meaning you don't even try and preload the building, removing the innefficiency.
users = User.includes(:building).where.not(residence_id: nil).references(:buildings)
users.each do | user|
puts "User has building: #{user} #{user.building}"
end
# No additional references needed to be eager-loaded.
users = User.where(residence_id: nil)
users.each do | user |
puts "#{User} has no building."
end
I have a user model and a content model. When a user views a piece of content, I need to make sure the user does not see that content again for say, 48 hours.
What's the Rails way to model this out? I'd like to have a table with a user_id, content_id, and a timestamp that the view was recorded, then have a worker clear out entries with timestamps > 2 days. This way when a user requests more content, I can filter out content that has an entry where user_id and content_id match.
Don't think it should matter, but I'm using MySQL with Rails 3.2.
I think you can do the following in your model.
class User < ActiveRecord::Base
...
has_many :contents, -> { where(["EXTRACT(HOUR FROM last_viewed_at) > ? OR last_viewed_at IS ?", 48, nil)}
end
I used or condition to make it nil because when it is initialized or new record created so that user can be able to see it.
I am not sure how you are using your worker.
Please suggest me if I am missing anything. I am not intended to answer accurately, rather trying a way I can realize what is possible.
Hi guys I have a situation where on a form I'm taking in orders for a car servicing application. I have the following models:
Car
belongs_to :car_company
Car_company
has_many :cars
Services
attributes_accessible :car_company_id, :car_id
#virtual attributes
attributes_accessible :car_company_name, :car_reg
The thing is that on a single form the user can enter in the name of the car company as well as the registration number of a car. If the company name doesnt exist it creates a new company and associates it with the service and the same goes for the car. I got this part working however the thing is that I want that on submitting this form the car created should be automatically associated with the car_company whether the carcompany exists or doesn't exist.
I'm pretty stuck here on how to get this thing done the right way? Its basically just to avoid having to enter the car details and the company details seperately just to use them on a form. Any ideas guys?
I see you are using an unconventional model name. By convention in rails, your model should be CarCompany. However, I think what you have will work.
Putting something like this in the appropriate controller may be something like what you want. If not, please clarify what you want.
car_company = Car_company.find_or_initialize_by_name(params[:car_company_name])
car = Car.find_or_initialize_by_registration(params[:car_registration])
car_company.cars << car
car_company.save
You actually may be able to combine the middle two lines with car_company.cars.find_or..., but I'm not sure if that works or not.
I hope that helps.
I'm attempting to write a site in Rails where a user in a manufacturing plant can see what devices are failing. The program storing the alarm data stores one entry when a device faults, and then stores another entry when the device gets fixed. The entries are linked only by having the same value in the EventAssociationID column. How might I write a named scope in Rails to check which faults have been fixed and which ones haven't?
I wasn't able to do it in a named scope, however, I was able to define a method for the model that solved the problem:
def inAlarm
return ConditionEvent.count(:all, :conditions => ['EventAssociationID = ?', self.EventAssociationID]) == 1
end
I am very new to RoR so this may be very fundamental. My structure keeps getting a level deeper and I can't figure out how to find the id anymore.
First you have a Company which can have many Users. Users sign in and are authenticated and the current_user is saved in a cookie with the Session.
Since the User has one Company I can always find the Company.id through the current_user.
Next a Company has many Farms. In farms create I can get the company id from the user cookie and the farm id is new so that works, and in farm show Rails knows which farm it is supposed to show. So that level works.
Now I want to add that a Farm has many Blocks. I am adding Blocks through the associated Farm show page, but the Blocks_controller doesn't know what farm page it is on (as far as I can tell, if it can any info is appreciated).
Here is the FarmsController create that works:
def create
company_id = current_user.company_id
#company = Company.find(company_id)
#farm = #company.farms.build(params[:farm])
if #farm.save
flash[:success] = "farm created"
redirect_to root_path
else
render 'pages/home'
end
end
And this code just complains that it doesn't know what id I am talking about:
BlocksController
def create
#farm = Farm.find(params[:id])
#block = #farm.blocks.build(params[:block])
end
This is displaying on the associated Farm show page, so if there is a way to capture the id I would love to know what it is.
Thank you for your time.
The three easiest ways to get that id is to:
Pass in that farm_id using a hidden form field. When creating the link to your blocks/new form just pass in the farm_id ie use a path like new_blocks_path(:id => #farm.id) inside your blocks controller you will want to make sure that the farm_id is set on the Block model.
def new
#block = new Block
#block.farm_id = params[:farm_id]
end
Then if you are using form for the farm_id field (which should probably be of type hidden), it should contain the right id. Now change the first line in the "create" block method to
#farm = Farm.find(params[:block][:farm_id])
You can combine the process of adding the blocks and the farms using nested forms. Take a look at http://railscasts.com/episodes/196-nested-model-form-part-1 for how to do this.
You can use nested RESTful resources to make sure that within the blocks controller you always have access to the farm id. For more information about how to do this try take a look at http://railscasts.com/episodes/139-nested-resources