Rails 3 subsequent forms submission (second dependent on first) - ruby-on-rails-3

I am trying to achieve a subsequent form submission. To clarify things -
I submit a form for #post
then once that #post is created I would immediately (under the hood) like to submit the form for #associations.
The catch is, this second form submission would require the post_id field from the newly created #post.
What would be the best way to achieve this? Would nested forms help me pull the newly created #post.id? Kindly help me with this.

If this is something that should happen whenever you create a Post, then you should use active callbacks to achieve that :
class Post < ActiveRecord::Base
after_create do |post|
# create your association using post.id
end
end
or, you can write it like that also :
class Post < ActiveRecord::Base
after_create :after_create_post
def after_create_post
# create your association using self.id
end
end
Otherwise, if this is something specific to a controller action, you should simple do something like this :
class PostsController < ApplicationController
def create
#post = current_user.posts.build(params[:post])
# then use the #post.id to build your association. something like
#post.associations.build(:prop1 => 'value1', :prop2 => 'value2')
end
end
Hope this helps!

Related

Getting previous HABTM values

In my app, I have several clients, and they have several elements (via has_many_through association) depending on a certain BusinessType to which Client belongs to so that instead of manually adding all the elements to the Client, I can just select the BusinessType and everything gets added automatically (business_type in Client is attr_readonly). BusinessType HABTM elements.
Here's the catch, after creation with the default BusinessType, the clients can update their elements and remove or add as they please (mostly add), so what I'm trying to do is the following:
Suppose one business_type has elements [1,2,3] and is assigned to one client, then, the following elements are added manually to the client = [4,5,6] so it ends up having [1,2,3,4,5,6], ok everything's fine here.
But after this, the business_type gets updated and has element 2 removed, so it ends up being [1,3]. Here's the deal, I want the client to be updated by removing the 2, but not the [4,5,6] that do not correspond to the business_type in question so that it ends up [1,3,4,5,6], I'm using an after_update callback to update the clients' elements but the _was method doesn't work for HABTM relationships (to get the old business_type's elements.
I've tried using a before_update callback to first to client.elements = client.elements - business_type.elements to store momentarily in the DB [1,2,3,4,5,6] - [1,2,3] = [4,5,6], and in the after_update do client.elements = client.elements + business_type.elements to get [4,5,6] + [1,3] = [1,3,4,5,6]but this has already the new value of [1,3]. How can I get the old business_type.elements value in the before_update or after_update?
Thanks in advance for your help!
I had a similar problem in an app, and the only solution I could come up with was to store the values before doing update_attributes in the controller.
Example code:
Models
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories, :join_table => "categories_products"
def remember_prev_values(values)
#prev_values = values
end
def before_update_do_something
puts #prev_values - self.category_ids # Any categories removed?
puts self.category_ids - #prev_values # Any categories added?
end
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :products, :join_table => "categories_products"
end
In the update method in the products controller I do the following:
class ProductsController < ApplicationController
...
def update
#product.remember_prev_values(#product.category_ids)
if #product.update_attributes(params[:product])
flash[:notice] = "Product was successfully updated."
redirect_to(product_path(#product))
else
render :action => "edit"
end
end
...
end
It is not ideal, but it is then possible to "catch" the habtm inserts/removes before they are executed.
I do think it is possible to do in a callback, but you might need to "hack" into ActiveRecord.
I did not spend much time on trying to dig into ActiveRecord internals, as this is a simple implementation that works.
You should use after_initialize callback to store previous values.
after_initialize do #previous_elements = elements.map{|x| x} end
Note that here we make a copy of assosiations by map function call.

Opposite of includes in Rails

Assuming I have
#post.rb
class Post < ActiveRecord::Base
default_scope includes(:user)
what do I do when I don't want to include the user when I fetch a post?
for instance, when I delete a post
You can use the unscoped scope. Method reference: http://apidock.com/rails/ActiveRecord/Scoping/Default/ClassMethods/unscoped
For example, when trying to delete the Post object :
def destroy
#post = Post.unscoped.find(params[:id])
# destroy code here
end
This will search in your database without any scope.

What is the most rails like way to structure the following

I am using devise for two different types of user in my app. They are called user and professional.
I currently have a simple resource based controller called MessagesController which pulls out messages for the current professional like this
class MessagesController < ApplicationController
def index
#messages = Message.find_all_by_profession_id(current_professional.id)
end
end
I want to find the best way of keeping this controller but changing the query based on the type of user that is logged in. I want the same to happen for all actions in the resource (index, new, create, update etc)
I know I can do this
if current_user
#messages = Message.find_all_by_user_id(current_user.id)
else
#messages = Message.find_all_by_profession_id(current_professional.id)
end
but this would be bulky and messy across all actions. I'm sure there must be a better way. What is the most rails like way of doing this? Should I be creating a completely new controller do handle user based messages?
I can think of two ways:
You can put your code inside the initialize method of your controller:
def initialize
if current_user
#messages = Message.find_all_by_user_id(current_user.id)
else
#messages = Message.find_all_by_profession_id(current_professional.id)
end
super
end
Or you can create a before_filter :
class MessagesController < ApplicationController
before_filter :get_messages
private
def get_messages
if current_user
#messages = Message.find_all_by_user_id(current_user.id)
else
#messages = Message.find_all_by_profession_id(current_professional.id)
end
end
end
IMHO, i think you can move this chunk of code to the model, so the controller only make a call passing the user parameter and gets all messages from the model.
# messsages_controller.rb
#messages = Message.find_all_messages(current_user, current_professional)
# message.rb
def self.find_all_messages(user, professional)
if user
self.find_all_by_user_id(user.id)
else
self.find_all_by_profession_id(professional.id)
end
end
I think it is better for this kind of code to be on your model. Of course you can improve the if/else code, but i am out of ideas now.

Paginate on two collections on the same model

I am planning to add some pagination to a couple of models in my application and I feel like I am missing a key concept here. Here is my application:
class Gallery < ActiveRecord::Base
has_many :photos
has_many :comments
end
class GalleryController < ApplicationController
def show
# some process here
#gallery = Gallery.find(params[:id])
end
end
I would like to be able to paginate independently on both the photos and comments for the given gallery that I am displaying. I need this to be done with AJAX and I have a feeling that calling 'show' with a parameter for photos or gallery is overkill (ie. why would I need to find the gallery if I am only looking for photos or comments).
How should I design this feature?
What is the alternative to calling GalleryController.show here?
I'd set it up like this:
class GalleriesController < ApplicationController
def index
#galleries = Gallery.paginate(params)
end
end
class CommentsController < ApplicationController
def index
#comments = parent.comments.paginate(params)
end
def parent
#parent ||= Gallery.find(params[:gallery_id])
end
end
class PhotosController < ApplicationController
def index
#photos = parent.photos.paginate(params)
end
def parent
#parent ||= Gallery.find(params[:gallery_id])
end
end
# config/routes.rb
resources :galleries do
resources :photos
resources :comments
end
Then you just request:
/galleries/1/comments
/galleries/1/photos
Check out the inherited_resources gem for this nested model pattern.
But ideally you'd want to get this all in one request. Check out this post on the Presenter Pattern in Rails.
Hope that helps.
yes, i'd absolutely recommend using inherited_resources, as it saves you a lot of boilerplate code. just set up these resources as described by viatropos, add JSON-responder (with inherited_resources, its as easy as adding respond_to :json, :html to your controller). then on the ajax side use a tempting plugin, like jquery.tmpl or mustache.js or whatever, pre-render an item template for each resource, and then use ajax to retrieve your paginated data as JSON and render it into your templates. it's incredibly easy, but if you need a step by step howto, just ask.

How to access devise current_user from model

Just start to develop with devise for my app authentication, however running into trouble with accessing current_user from a model. I have no problem accessing this from a controller.
For my scenario I need to get the current user id to keep track who has edit the blog post, which I save the user id with before_save filter.
Appreciate your help!
Thanks
You can only pass it down in a parameter.
class SomeModel
def my_nifty_method(user)
end
end
in controller:
class SomeModelsController < ApplicationController
def some_method
sm = SomeModel.new
sm.my_nifty_method(current_user)
end
end
You should be able to do this in your model. 'c' here being the model object and setting it's user_id attribute before create, or save in your case.
before_create {|c| c.user_id = Authorization.current_user.id}
I'm somewhat new to RoR so be warned.
Why are you trying to access to that method in the model?
You can access to the ID attributes via self.id (within the model)