Dynamic nested form elements based on inputting a starting number - ruby-on-rails

Like many others, I'm new to Rails and have a question. I'm working on a sample tracker for a small analytical lab. I would like users to submit batches consisting of many samples. I want the front page to be a simple gateway into batch submission. My general plan is:
Front page asks for number of samples in batch. User enters number and hits submit.
A form is generated where the user can enter batch information (Sampling date, experiment name, batch model stuff). Under the batch fields there should be as many fields for individual sample IDs as the user indicated in the first step.
User fills all this out and the batch and its samples are created upon submission.
My feeling is that the homepage should pass some sort of parameter to the batches controller, which then iteratively builds the samples while the model has a method to iteratively build form elements for the view. Is this thinking correct? How could I pass a parameter that isn't directly related to any models or controllers? I could find any similar questions, but if anyone can link me to a solution for a similar problem or a Railscast or something I'd be very grateful!

There's no need to back a form with a model. For your view, you'll just want something like this example (in Haml):
- form_tag new_batch_path, :method => "get" do
= label_tag(:sample_count, "Number of samples:")
= text_field_tag(:sample_count, 3)
= submit_tag("Get Started!")
And then in your controller, and the new_batch view, you can just reference params[:sample_count]
- (params[:sample_count] || 5).to_i.times do |n| ...
Because this isn't tied to a model (and nothing's being saved anyway) you can't use model validations to check the value. If you do want to verify, you'll do the verification in the batches controller - either as a before_filter, or just inline:
#sample_count = params[:sample_count].to_i
unless (1..10).include? #sample_count
flash[:error] = "A batch must contain between 1 and 10 samples."
redirect_to root_url
end
Note that nil.to_i, "".to_i and rubbish like "ajsdgsd".to_i all equal 0, so unless you want people to be able to specify 0 samples, this code is fairly robust

Have a look at these Railscasts series:
Nested Model Form: Part 1, Part 2
Complex Forms: Part 1, Part 2, Part 3
The "Nested Model Form" screencasts are newer, so I'd go with these ones first.

Related

How do I generate all new records for a given model randomly using pre-defined options?

I'd like one of my models, Stones, to be generated at random using pre-defined options I've stored in a set of arrays and hashes. Instead of Create using params from the URL, I'd like new Stones to always be defined using this random generation process. I don't need any user input at all, except that each stone belongs to a given player.
I'm still new to rails; where should I put all this code? I know enough to be able to define the arrays and hashes and randomly select from them when I need to, but I'm not sure where and how to replace the part of the code that draws params from URLs and fills in a new record before it is saved. I know controllers are supposed to be skinny, so do I do this in the model?
Apologies if this is a duplicate. I searched extensively and couldn't find an applicable solution.
Thanks for any help!
I would create a service for this. Something like:
# app/services/stone_creator.rb
class RandomStoneCreator
RANDOM_FOOS = ['bar', 'baz', 'bat']
def self.call(user)
Stone.create!({
foo: RANDOM_FOOS.sample,
user: user
})
end
end
And then anywhere that you need a new random stone you can call it like:
random_stone = RandomStoneCreator.call(current_user)

Need to output 1095 fields in Rails app

I'm writing an application that requires 3 inputs a day for every day of the year. (3*365=1095). I'm struggling with a way to output each field in an efficient manner.
The inputs are not all-or-nothing (you could fill in 10 days worth of input, hit save, and come back later to fill in more)
I attempted to do this by building all 1095 objects in the controller and then outputting the inputs in the view, but obviously this is really slow and probably memory intensive.
Any suggestions? I'm leaning toward writing the entire form client-side and then filling in the existing elements using AJAX.
EDIT
The model is called Timing and has these attributes:
month, day, time1, time2, time3
so there are 365 models to be saved.
Sounds like you've got a nested resource. You have a resource called timing which contains a resource called, what, day?
#routes
resources :timing do
resources :day
end
So assuming that when timing is created, you have all 365 days created as well (sounds like a pretty expensive operation). Displaying the fields isn't that tricky. You could just do
#controller
def show
#timings = Timing.all
end
#view
(Date.beginning_of_year..Date.end_of_year).each do |day|
t = #timings.find { |timing| timing.date == day } #or some other method of deciding that the current day has a timing
unless t.nil?
form_for t #etc
else
form_for Timing.new #etc
end
end
Then perhaps you could make each for submit via UJS and call it a day. Though this sounds like a pretty complicated design, but I'm not sure what your problem area is.
If I understand your question correctly, you want a way to dynamically show time inputs, 3 of them per day, on a form.
If the above is correct, what I would suggest is that you do the nested resource as #DVG has detailed, and load the current day only. If you need to load multiple days, you can easily request that through UJS (Ajax) and load it on the same page.
What you probably want to do, in order not to melt down the server, is auto-save the time inputs or auto-save each day's time inputs when the grouping loses focus.
#DVG's answer probably works fine, but it keeps all of the work on the server.
What I ended up going with was my initial thought: get all of the existing timings like this:
def edit
#timings = Timing.find_by_store_id(params[:store_id])
end
then in the view, I wrote two javascript functions: one that writes all 365 rows with all 3 columns. Once the field were all output in Javascript, I used another function that took the existing records and inserted them into the form:
<script type="text/javascript">
function updateForm(){
timings = <%= #timings.to_json %>;
... fill out the fields ...
}
</script>
It works nice and fast, and best of all, no AJAX calls. Of course one caveat is that this fails if the user has Javascript disabled, but that's not an issue for me.

Is this a race condition issue in Rails 3?

Basically I have this User model which has certain attributes say 'health' and another Battle model which records all the fight between Users. Users can fight with one another and some probability will determine who wins. Both will lose health after a fight.
So in the Battle controller, 'CREATE' action I did,
#battle = Battle.attempt current_user.id, opponent.id
In the Battle model,
def self.attempt current_user.id, opponent_id
battle = Battle.new({:user_id => current_user.id, :opponent_id => opponent_id})
# all the math calculation here
...
# Update Health
...
battle.User.health = new_health
battle.User.save
battle.save
return battle
end
Back to the Battle controller, I did ...
new_user_health = current_user.health
to get the new health value after the Battle. However the value I got is the old health value (the health value before the Battle).
Has anyone face this kind of problem before ???
UPDATE
I just add
current_user.reload
before the line
new_user_health = current_user.health
and that works. Problem solved. Thanks!
It appears that you are getting current_user, then updating battle.user and then expecting current_user to automatically have the updated values. This type of thing is possible using Rails' Identity Map but there are some caveats that you'll want to read up on first.
The problem is that even though the two objects are backed by the same data in the database, you have two objects in memory. To refresh the information, you can call current_user.reload.
As a side note, this wouldn't be classified a race condition because you aren't using more than one process to modify/read the data. In this example, you are reading the data, then updating the data on a different object in memory. A race condition could happen if you were using two threads to access the same information at the same time.
Also, you should use battle.user, not battle.User like Wayne mentioned in the comments.

break down a complex search query in Rails 3

I have a controller which has a lot of options being sent to it via a form and I'm wondering how best to separate them out as they are not all being used simultaneously. Ie sometimes no, tags, sometimes no price specified. For prices I have a default price set so I can work around with it always being there, but the tags either need to be there, or not. etc.
#locations = Location.find(params[:id])
#location = #locations.places.active.where("cache_price BETWEEN ? AND ?",price_low,price_high).tagged_with([params[:tags]).order(params[:sort]).paginate :page => params[:page]
I haven't seen any good examples of this, but I'm sure it must happen often... any suggestions? Also, even will_paginate which gets tacked on last should be optional as the results either go to a list or to a google map, and the map needs no pagination.
the first thing to do when refactoring a complex search action is to use an anonymous scope.
Ie :
fruits = Fruit.scoped
fruits = fruits.where(:colour => 'red') if options[:red_only]
fruits = fruits.where(:size => 'big') if options[:big_only]
fruits = fruits.limit(10) if options[:only_first]
...
If the action controller still remains too big, you may use a class to handle the search. Moreover, by using a class with Rails 3 and ActiveModel you'll also be able to use validations if you want...
Take a look at one of my plugins : http://github.com/novagile/basic_active_model that allows you to easily create classes that may be used in forms.
Also take a look at http://github.com/novagile/scoped-search another plugin more specialized in creating search objects by using the scopes of a model.

RESTful nested conventional routing

I have the model:
User -1---n- Transaction(amount,description, date)
User -1---n- TransactionImport -1---n- TransactonImportField(name,value)
(personal expense tracking app).
What I want to achieve is this:
User opens URL and pastes the CSV with the list of transactions.
User submits it.
System extracts data from CSV into TransactionImport (row) + TransactionImportField (cell).
User can choose which column means what (amount, description, date) from the imported data in TransactionImport(Field).
User click save and the system transfers TransactionImport into the Transaction.
What I can't seem to get right is the fact that step 3 creates multiple records of TransactionImport (and related TransactionImportField).
So doing POST /transaction_imports?csv=abcd is expected to produce one record if we would be RESTful. But the code is supposed to be something like this:
# TransactionImportsController
def create
result = TransactionImports.parse(params[:csv])
flash[:notice] = result.message
redirect_to transaction_imports_path
end
I am probably approaching the task from a wrong angle as I feel that implementation doesn't fit in tp the inherited_resources.
Could you please advise what would be the most conventional way of implementing this?
Thanks,
Dmytrii.
REST/HTTP has no expectation that doing POST will only create one record. That maybe the default rails behaviour, but you should not constrain your design because of that.

Resources