Preface: I'm still a beginner to web development, let alone rails so I'm constantly in over my head.
In my rails application, I have a boolean called "accepted" in "Bids."
On the show page for Bids, I am trying to create a button_to called "Accept Bid" that will, obviously, update the boolean from false to true, and then later, I will make it do a few other things. I experimented a bit with this but ended up getting so confused, I thought I would come here for some inspiration/push in the right direction.
Here's my bid.rb
class Bid < ActiveRecord::Base
belongs_to :user
belongs_to :swarm_request
# Accepts a bid for a swarm request
def accept!
self.swarm_request.update_attributes(:accepted => true)
# also update the bid with any details here?
end
end
Am I on the right track with this? Or should I create an action in the bids controller instead? Is using button_to the best way to do this? My apologies if I'm using incorrect jargon, or not being clear enough. Like I said, newb.
Thanks in advance for any help!
yes, it's ok to have skinny controller and fat model. you can read about this at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model
and i don't see anything wrong with button_to for this job.
Related
I'm learning Rails by doing an app for a college project that is basically a SO copy. Consider the following routes:
resources :questions do
resources :answers
post :vote_up, :vote_down, :on => :member
end
resources :answers do
post :vote_up, :vote_down, :on => :member
end
While this works fine, I'm sure it isn't the best way to do it. I got a lot of duplicated code between the vote_up and vote_down actions on both controllers. My specs have a lot of duplication too.
I would like to know how can I approach this in the most DRY way possible. I guess a VotesController is needed, but I played around with routing and didn't get a practical solution. All I got was some big URL's and not what I really hoped.
Can you please point me in the right direction ?
It sounds like you're describing a vote_controller which can take a vote in either direction, rather than splitting out up and down into their own controllers.
The question to ask as regards splitting the up and down vote into its own controller is how different they are. If you had a vote_up and vote_down controller both of them would have to find the votes of the entity being voted on, add a new vote and then store the data. Almost everything there would be the same whether or not you are voting up or down. In fact, the way I would manage votes would be to have a vote type a little like this:
class Vote
attr_reader :direction, :user, :timestamp
end
That way a single vote type can record whether it is an up or down vote as well as recording who made the vote ( so if someone is trolling you can undo all their work at a go ) and the time the vote was made, which is always useful.
Then when you call vote_controller.up_vote you create a new Vote object with a positive direction, for vote_controller.down_vote you create one with a negative direction. Everything else is totally common, so all you would have in the vote controller would be vaguely like this:
def vote(direction)
myVote = vote.new(direction, Request.userId, Time.now )
voteOnObject.votes.add(myVote)
end
def up_vote
vote( 1 )
end
def down_vote
vote( -1 )
end
You will notice that this is nothing like working code. That is partly because I don't want to do your homework for you, partly because I haven't done much Ruby lately, so I'm a little creaky. The important thing is the principle here.
If you want to be properly DRY you would also design this in a way that the same vote controls could be attached to questions, answers, comments and so on ( so they have a parent object of a given type/implementing a given mixin whose votes they adjust rather than affecting a model level vote object ) without having to make any changes.
I've been trying to get my head around ActiveRecord associations but I have hit a bit of a brick wall, and no matter how much I review the ActiveRecord documentation, I can't work out how to solve my problem.
I have two classes:
Property -> has_one :contract
Contract -> belongs_to :property
In my contract class, I have a method to create_or_update_from_xml
First I check to make sure the property in question exists.
property_unique_id = xml_node.css('property_id').text
property = Property.find_by_unique_id(property_unique_id)
next unless property
And this is where I get stuck, I have a hash of attributes for the contract, and what I want to do is something like:
if property.contract.nil?
# create a new one and populate it with attributes
else
# use the existing one and update it with attributes
I know how I would go about it if it was raw SQL, but I can't get my head around hte ActiveRecord approach.
Any hints past this road block would be hugely appreciated.
Thanks in advance.
if property.contract.nil?
property.create_contract(some_attributes)
else
property.contract.update_attributes(some_attributes)
end
Should do the trick. When you have a has_one or belongs_to association then you get build_foo and create_foo methods (which are like Foo.new and Foo.create). If the association already exists then property.contract is basically just a normal active record object.
Just another way of doing it using ruby OR-Equal trick
property.contract ||= property.build_contract
property.contract.update_attributes(some_attributes)
Update:
#KayWu is right, the above ||= trick will create the contract object in the first line, rather than just building it. An alternative would be
property.build_contract unless property.contract
property.contract.update_attributes(some_attributes)
Property.all.each do |f|
c = Contract.find_or_initialize_by(property_id: f.id)
c.update(some_attributes)
end
I don't know whether this is the best solution, but for me it more succinctly
From Rails 6.1 an onwards:
property.build_contract unless property.contract
property.contract.update(some_attributes)
I am working on a app for my kids to log their chores. I have 3 children (Nick, Siena, Mac) and have a home page with each name hyperlinked ...
I have the following associations:
Child:
has_many :completions
has_many :chores, :through=>:completion
Completion:
belongs_to :child
belongs_to :chore
Chore:
has_many :completions
has_many :kid, :through=>:completion
How do I (upon clicking the child's name) save the child_id as a session object for posting completions to the completions models?
How do I clear / change that sesison to a new child when another child clicks their name in the homepage?
Any help is greatly appreciated. Thanks, CB
So josh went above and beyond. As a noob i was asking something much more simple and elementary for most folks. The answer was quite simple:
ApplicationController
private
def current_child
child = Child.find(params[:id])
session[:child_id] = child.id
end
end
This allowed me to store that child's id in a session and post to the completed model.
If I understand the question correctly (at least as described in your comment), I did something similar recently. See Building a nested attribute from multiple parent objects. Basically, I used polymorphic_url to make a link to create a new item (I would probably use #child.chores.build(params)) and pass the attribute :chore_id, i.e.
link_to "Mark as complete", polymorphic_url([:new, #child, :completion], :chore_id => #chore.id)
In your controller make sure that for your ChoresController#new you have something like
def new
#chore = <current_child>.chores.build(params)
end
Hope this helps.
Ugh - not sure why I'm having so much trouble with this.
Writing a simple question and answer app (see Rails - Where should this code go to stay true to MVC design? for some details)
Trying to stick to MVC principles and proper design - this app is simply a learning experience, so I want to make sure I'm doing things in a generally accepted way
The referenced question gave me advice to split up my functionality into the different models. However, trying to implement this, I find myself passing parameters all over the place and I just get a feeling that I'm not doing something right. So, here's the basic layout of the app and the tasks I am trying to accomplish - if someone could let me know if I'm on the right track...
Question Model: contains id(pkey), question_number(int), question_text(string), answer_text(string)
User Model: contains: id(pkey), uid(string), current_question(int), name(string)
I created both of the above with scaffold so they have all the default routes, controller actions, etc...
I created a gateway controller and set it to be my default page through routes.rb
The idea is, user browses to localhost:3000/?uid="whatever" and the index page displays the current question (Question.find_by_question_number(#user.current_question))
User enters answer in a form, which POSTs it to an action. In my first draft, this called an action in the gateway controller which checked if the answer was correct.
Now, I am trying to take vadim's advice in my last question and keep the user login in user and the question logic in question. So now my form POSTs to the users controller.
Here's where I get mixed up. The logic code shouldn't be in the controller, right? So I call a method in the model, passing it stuff like the user's answer and the question id since I can't read the session in the model. This works fine, I can now take care of the logic in the user model - so now the user model calls a method in the question model to actually check the answer. That means I have to instantiate my question object using the ID I passed, then call another method, passing (again!) the answer. Etc...etc...
See what I mean? I definitely understand the value of MVC in theory, but whenever I try and implement it I wind up with a mess like this. It this correct, and it just seems like overcomplicating things because my program is so simple?
Can someone walk me through how you would split the logic up? You don't need to post actual code, just what you would put where, like:
Gateway Controller:
-display question to user
-take answer and pass to XXX controller
XXX controller:
-call method Foo in XXX model, passing X and Y
The basic flow should be, user is shown a question, user answers question, answer is compared to the correct answer in the question model, message is returned based on result, and it answer was correct, user's current_question is incremented.
Thanks so much for the help, I have books and Google and have been reading my a$$ off, just lost in the sauce here. This is my first attempt to venture outside the safety of pre-written example code, so please be gentle!!
Thanks!!
In most cases in a Q&A app, you would have a Question Model, an Answer Model, and a User model. Your actions are:
displaying answers (the show method for the Questions controller)
Showing the new answer form
Posting to the create method on the Answers controller.
Some code:
class Question
has_many :answers
end
class Answer
belongs_to :question
has_many :users
validates_presence_of :user
validates_presence_of :question
validates_uniqueness_of :question_id, :scope => :user_id
end
class User
has_many :answers
end
Routes
resources :questions do
resources :answers
end
answers_controller
class AnswersController < ApplicationController
def create
#answer = Answer.new(params[:answer])
#answer.user = current_user
#answer.question = Question.find(params[:question_id])
if #answer.save
flash[:success] = "Saved!"
redirect_top #answer.question
else
render :new
end
end
end
The basic flow should be:
a method in the controller selects the question and displays a view with a form in it
the users submits this form
this is a POST to the controller
in the controller you check the result and display it to the user in another/the same view.
Now to the model. You can put methods in there that check certain things. Still, the controller handles the work, it calls the method and processes the results in the controller.
Well, I don't know if I'm completely wrong, but I really can't find a very practical and straight forward way to do something like this:
class User < ActiveRecord::Base
has_many :creations
end
but the thing is, I just want the user to have many creations if the user.developer == true
where user.developer is just a boolean field inside the Users table.
So any ideas on how exactly could I do it directly from the model?
Resuming, when the user is not a developer if you try to get User.first.creations, User.first.creations.new ... create...destroy, etc you get a NoMethodError but if it is a developer you can build a new creation.
The only way I managed to do it is extending the model and from the extension check if the proxy_owner.developer == true but by doing this I had to rewrite all the actions such new, create, update, etc...
Any help would be much appreciated
Thanks a lot
How about subclassing User and only specifying the has_many on the developer subclass? Developer would then pick up any logic from User and Users wouldn't have any creations.
class User < ActiveRecord::Base
end
class Developer < User
has_many :creations
end
Including this may work. If not you may have to resort to alias_method_chain, but I hear that has links to serious organised crime, so watch yourself.
module CreationsJustForDevelopers
def creations(*args)
if developer?
super
else
raise NoMethodError, "Only developers get creations."
end
end
end
Not sure what you are referring to with all that talk of overriding new, create, update etc… but the only other method I can think of to remove is creation_ids, but who cares about that?