Which MVC layer should handle a child claiming a toy? - ruby-on-rails

I have two models in my app: a child that has_many :toys and a toy that belongs_to :child. I made the db migration I needed for this to work (added child_id to toys table).
At first, children exist on their own, and toys exist on their own (with no association). At the start of every day in kindergarten, no child owns any toys. To play with a toy, a child must claim it first, and become its owner. So, now I need to somehow implement a child.claim(toy) method, and here I get stuck. Specifically:
Should this go into the child controller or model? Or maybe it should be somehow split between both?
If it should go into the controller, should it correspond to one of the CRUD actions or be its own thing as def claim(toy)?
Edit 1: The child is the user and is logged on via the browser. (Today's kids can do amazing things)

Actually you don't need a claim method if child is the user. You can have a claim_toy method in your controller. In you toys index view for each toy you can give a link as follows.
<%= link_to "claim", claim_toy_path(:toy_id => toy.id) %>
Your controller method will look something like this.
def claim_toy
toy = Toy.find(params[:toy_id])
current_child.toys << toy
end
simple enough. This is not a restful solution by the way.

A Child is a Foreign Key on Toy
There are certainly other ways to do this, but based on your original question, the simplest solution is to make your behavior consistent with the fact that a child is associated with a toy in the Toy table.
Simplest Solution
Setting aside arguments about what one should do in a perfect OOP/MVC design, the sensible place to make the change is in a controller since when a child claims a toy, the claim is processed by a an #update (or perhaps even a #claim) action on the Toy controller.
The child is the user and is logged on via the browser.
In your case, the session already knows who the child is, so adding the foreign key to the Toy model is trivial. The controller is the entity that receives the params for the associated model, so it's the correct place to tell the Toy model what attributes to update for a given toy.
There are certainly more complicated solutions, but they are of dubious merit based on the information provided in your original post. As always, your mileage may vary.

I'd build a separate class handling all the logic regarding a child and a toy. Call it context, call it concern, but do it.
class ToyChild # or ToyInteraction or ChildContext::Toys...
attr_reader :toy, :child
def initialize(toy, chid)
#toy = toy
#child = child
end
def associate
toy.child = child
# could be more difficult: you should check if the child has not enough toys, if the toy is not already assigned to another child etc...
#I'd avoid saving here since you may want to perform other operations
end
def foo
#code here
end
end
And in controller:
assoc = ToyChild.new(toy, child).associate
assoc.save
This style of coding:
is easier to test
splits responsibilities clearly
keeps things dry (no specific code in controllers)

Related

Can two or more inter-related models have one common controller in rails

am a newbie in rails and I want your help. I want to build a simple project in rails that includes more than one model that are inter-related. So in this case, is it possible to create only one controller that corresponds to all the models or should I create a controller for each model? And please I want reasons for your answers. Thank you.
Of course..
You could easily have something like:
#animals_controller.rb
def animals
#dogs = Dog.all
#cats = Cat.all
#cheetahs = Cheetah.all
end
BUT...
Imagine having an update, edit, new, show, create and destroy for each of these models in the same controller!!
That would be a mess - and you would end up with a complex file that would be (mildly said) annoying to maintain.
Therefore, for your own sake, and because it is good practice, you normally create a corresponding controller for each of your models(animals): model: dog.rb, controller: dogs_controller.rb, model: cat.rb controller: cats_controller.rb etc...
so you, after a few months abroad, can come back and continue your work without thinking What the ....
I would recommend looking into the MVC (for example on youtube) and really understanding how it connects the database with your view and so on.
Understand the data-flow and you will find yourself with a powerfull toll later on when you're debugging, construction forms, creating ajax-calls, custom attributes and so on.
ous,
You need to create a controller for each model in order to have CRUD options. It depends on what the models will do if you don't want a controller, for example if model just does some calculations and you don't really need to pass data to your view then you might not need it.

How to implement edit action in controller if I don't yet have an ActiveRecord object

I am developing my first Rails application, which tracks job statuses for a job shop. I have the following models:
class Job < ActiveRecord::Base
has_many :parts
...
end
class Part < ActiveRecord::Base
belongs_to :job
end
I would like to have a page with a form that accepts a scanned barcode as input. It would find the Part object in the DB using the barcode ID and would set the Part.completed_at attribute to the current time.
I am trying to keep everything as RESTful as possible, and so at first it would seem like this page should be a view for the edit action in the Parts controller. However, in this view I don't yet have the Part object. What is the best way to implement this functionality? Should it be a new action in the Parts controller called something like find_and_update? Thanks in advance for steering me in the right direction.
You can implement a new non-RESTful action called find_and_update, that will work just fine.
If you want to keep it RESTful:
1) you could have a PartCompletions controller, that has a create action that accepts an ID. That is to say, when you submit a barcode, you are "creating" a "part completion". One of the light-bulb moments people have learning REST with Rails is when they learn that your RESTful controllers don't have to map 1-to-1 with your models / database tables. You can create new abstractions to help stick to the RESTful verbs (create, read, update, delete).
2) If you didn't want to create an extra controller, you could use the update method of your existing PartsController. That is to say, you're "updating" a "part" and giving it some new values (in this case a new completed_at date). You'd pass it the id from the barcode reader, and also pass it a hash with the new values you want to change (ie, that'd mean passing in the completed at field with the current time in your post request, rather than setting it in your controller action)
Either of these two approaches can be fine - as is the non-RESTful option, too - it depends on how big your app is, how many other non-RESTful actions you're likely to end up having if you don't strictly follow REST, etc. The smaller and more self-contained your app is, the more it's OK to break REST and other conventions/patterns in order to save time and keep it simple up front. My advice: don't spend too long agonising over which approach is the best. Pick one, and write code. You can re-factor it later.

Rails STI: transfer records from one model to another

I have a model called Coupons
Then I have two child models CouponApplicationsand ApprovedCoupons.
The last two inherit from Couponsvia an STI architecture.
Now I want to realise the following:
A user sees CouponApplications
He clicks on an Approve button which makes the CouponApplications ApprovedCoupons
I realise that I could simply update the typecolumn of the Couponsrecord to change types. However, there are several Concerns, hooks etc in the ApprovedCoupons model which are happening after creation so this is not so easy. In fact, I want to create a complete new record to trigger those Concerns, hooks etc.
So I wrote this which I consider really bad:
#coupon_application = CouponApplication.find(params[:id])
#approved_coupon = ApprovedCoupon.new
# copy/paste attributes except the ID as this would be considered a duplication
#approved_coupon.attributes = #coupon_application.attributes.except("id")
# set the new type
#approved_coupon.update_attributes(type: "Advertisement")
#approved_coupon.save
I hope you understand what I want to achieve. It works this way but I doubt this is clean code.
To summarize:
I want to change the Coupontype from CouponApplication to
ApprovedCoupon
I still want to trigger the Concerns, hooks etc. in my ApprovedCoupon model so I decided to create a new ApprovedCoupon
record instead of just changing the type.
Is there any better approach?
You could add an approve method to your CouponApplication model like this:
class CouponApplication < Coupon
...
def approve
data = attributes.except('id', 'created_at', 'updated_at')
ApprovedCoupon.create(data.merge(type: 'Advertisement'))
end
end
Now your code can be simplified to this:
#coupon_application = CouponApplication.find(params[:id])
#approved_coupon = #coupon_application.approve

Rails: getting data from a table for each iteraction of a loop

I have a loop (for item in #dataset) and I want, in each iteration, to get different data from another table and make some operations that will be printed in the view. I cant't get this data from the dataset used in the loop.
How can I do this according to MVC? I can put the code into the loop, in the view, but I think it's horrible.
Must I use a helper for do this, and call the function from the view?
If you have one table, and want to get data from another table, usually this is in the situation of a has_many relation. For example, we have #people (Person model), and each person has_many addresses (Address model). In those cases the best thing to do is this
# Controller
#people = Person.find(:all, :include => :addresses)
...
# View
#people.each do |p|
p.addresses.each do |address|
...
If your data is not just normal database tables (maybe you get it from a web service or so on), then a good thing to do is to build all the data in the controller ahead-of-time, then pass that to the view. Something like this
# Controller
#people = Person.find(:all)
#people.each do |p|
# attach loaded data to the person object in controller
p.addresses = Address.load_from_somewhere_by_name(p.name)
...
This way the view code stays clean, like this:
# View
#people.each do |p|
p.addresses.each do |address|
...
It is quite hard to tell about best solution for your problem without details about data and relations between your models(tables). The common idea is next: keep your views stupid. Get all data needed to render view inside your controllers action. Make all changes and calculations inside same action. Then in view use this data.
btw, if you are talking about N+1 problem then read more about 'include' and 'joins' ActiveRecord parameters.
If I was you I would create a separate class that encapsulates the dataset and contains all the logic involved in processing the dataset entries. This class could be iterable (respond to each). Then I would pass an instance of this class to the view and use only its methods there.
Person(id, name, other fields...)
Event(id, title, ...)
Date(id, date, time, event_id, ...)
Disponibility(id, percent, date_id, person_id, ...)
Of course, all the relationships are defined in the Model.
I have a view that shows all the dates for a event, it's easy. But I want, for each date, to print the data for all the people whose are available for that date. In my view, forgetting MVC design pattern, I could code something like this:
<% for date in #Dates
available = Disponibility.find_by_date_id(date.id)
for item in available
guy = Person.find_by_id(item.person_id)
%>
Render date and people available...
I want to avoid this in the view. I think the Orion Edwards' answer is the nearest for what I need, but, in that example, address is an empty field for Person table? Or how can I append a new attribute to Person class?
Although, am I missing any SQL trick for do this?
Keep all the logic on your #dataset item inside the controller action
Utilize methods in your models for the interaction with other model objects you need
You should be left with only #dataset for your view that you can render in a partial.
You would need to further explain your situation for any more of answer on that. What kind of operations and other models do you need to interact with? If you post your model associations I'm sure we could really square you away.
Good luck!

Should rails models be concerned with other models for the sake of skinny controllers?

I read everywhere that business logic belongs in the models and not in controller but where is the limit?
I am toying with a personnal accounting application.
Account
Entry
Operation
When creating an operation it is only valid if the corresponding entries are created and linked to accounts so that the operation is balanced for exemple buy a 6-pack :
o=Operation.new({:description=>"b33r", :user=>current_user, :date=>"2008/09/15"})
o.entries.build({:account_id=>1, :amount=>15})
o.valid? #=>false
o.entries.build({:account_id=>2, :amount=>-15})
o.valid? #=>true
Now the form shown to the user in the case of basic operations is simplified to hide away the entries details, the accounts are selected among 5 default by the kind of operation requested by the user (intialise account -> equity to accout, spend assets->expenses, earn revenues->assets, borrow liabilities->assets, pay debt assets->liabilities ...) I want the entries created from default values.
I also want to be able to create more complex operations (more than 2 entries). For this second use case I will have a different form where the additional complexity is exposed.This second use case prevents me from including a debit and credit field on the Operation and getting rid of the Entry link.
Which is the best form ? Using the above code in a SimpleOperationController as I do for the moment, or defining a new method on the Operation class so I can call Operation.new_simple_operation(params[:operation])
Isn't it breaking the separation of concerns to actually create and manipulate Entry objects from the Operation class ?
I am not looking for advice on my twisted accounting principles :)
edit -- It seems I didn't express myself too clearly.
I am not so concerned about the validation. I am more concerned about where the creation logic code should go :
assuming the operation on the controller is called spend, when using spend, the params hash would contain : amount, date, description. Debit and credit accounts would be derived from the action which is called, but then I have to create all the objects. Would it be better to have
#error and transaction handling is left out for the sake of clarity
def spend
amount=params[:operation].delete(:amount)#remove non existent Operation attribute
op=Operation.new(params[:operation])
#select accounts in some way
...
#build entries
op.entries.build(...)
op.entries.build(...)
op.save
end
or to create a method on Operation that would make the above look like
def spend
op=Operation.new_simple_operation(params)
op.save
end
this definitely give a much thinner controller and a fatter model, but then the model will create and store instances of other models which is where my problem is.
but then the model will create and store instances of other models which is where my problem is.
What is wrong with this?
If your 'business logic' states that an Operation must have a valid set of Entries, then surely there is nothing wrong for the Operation class to know about, and deal with your Entry objects.
You'll only get problems if you take this too far, and have your models manipulating things they don't need to know about, like an EntryHtmlFormBuilder or whatever :-)
Virtual Attributes (more info here and here) will help with this greatly. Passing the whole params back to the model keeps things simple in the controller. This will allow you to dynamically build your form and easily build the entries objects.
class Operation
has_many :entries
def entry_attributes=(entry_attributes)
entry_attributes.each do |entry|
entries.build(entry)
end
end
end
class OperationController < ApplicationController
def create
#operation = Operation.new(params[:opertaion])
if #operation.save
flash[:notice] = "Successfully saved operation."
redirect_to operations_path
else
render :action => 'new'
end
end
end
The save will fail if everything isn't valid. Which brings us to validation. Because each Entry stands alone and you need to check all entries at "creation" you should probably override validate in Operation:
class Operation
# methods from above
protected
def validate
total = 0
entries.each { |e| t += e.amount }
errors.add("entries", "unbalanced transfers") unless total == 0
end
end
Now you will get an error message telling the user that the amounts are off and they should fix the problem. You can get really fancy here and add a lot of value by being specific about the problem, like tell them how much they are off.
It's easier to think in terms of each entity validating itself, and entities which depend on one another delegating their state to the state of their associated entries. In your case, for instance:
class Operation < ActiveRecord::Base
has_many :entries
validates_associated :entries
end
validates_associated will check whether each associated entity is valid (in this case, all entries should if the operation is to be valid).
It is very tempting to try to validate entire hierarchies of models as a whole, but as you said, the place where that would be most easily done is the controller, which should act more as a router of requests and responses than in dealing with business logic.
The way I look at it is that the controller should reflect the end-user view and translate requests into model operations and reponses while also doing formatting. In your case there are 2 kinds of operations that represent simple operations with a default account/entry, and more complex operations that have user selected entries and accounts. The forms should reflect the user view (2 forms with different fields), and there should be 2 actions in the controller to match. The controller however should have no logic relating to how the data is manipulated, only how to receive and respond. I would have class methods on the Operation class that take in the proper data from the forms and creates one or more object as needed, or place those class methods on a support class that is not an AR model, but has business logic that crosses model boundaries. The advantage of the separate utility class is that it keeps each model focused on one purpose, the down side is that the utility classes have no defined place to live. I put them in lib/ but Rails does not specify a place for model helpers as such.
If you are concerned about embedding this logic into any particular model, why not put them into an observer class, that will keep the logic for your creation of the associated items separate from the classes being observed.

Resources