I have database full of data, in database I have fields like title, name, surname. I can retrieve whole record as JSON by doing GET request on /users/1.json. What if I want to retrieve just name field for that record, is something like this /users/1/name.json possible?
You can try the following:
# routes.rb
resources :users do
member do
match ':attribute_name' => "users#get_attribute_value"
end
end
# User controller
class UsersController < ApplicationController
def get_attribute_value
attribute_name = params[:attribute_name]
#user = User.where(id: params[:id])
if User.column_names.include?(attribute_name.to_s)
render json: { "#{attribute_name}" => #user.send(attribute_name) }
else
# trying to access an attribute that does not exists
end
end
end
Related
Each User has_one :family_tree.
So the family_tree route looks like a normal resources :family_trees.
I have a route that looks like this:
get "dashboard/my_tree" => "dashboard#my_tree", as: :my_tree, path: "/my_tree"
What I want to happen is, whenever someone goes to family_tree/:my_id they should be redirected to (or just shown the URL path for) /my_tree. Please note: that the :my_id is the ID of the family_tree that belongs to the current_user.
The issue is that my FamilyTree#Show controller action looks like this:
def show
#user = #family_tree.user
#memberships = #family_tree.memberships
#memberships_grouped_by_relations = #memberships.includes(user: :family_tree).group_by(&:relation)
#nodes = #family_tree.nodes
render "dashboard/my_tree"
end
And my DashboardController#MyTree looks like this:
def my_tree
#user = current_user
#family_tree = #user.family_tree
#memberships_grouped_by_relations = #family_tree.memberships.group_by(&:relation)
end
Both work, but I just don't know how to mask the URL of family_tree/51 to redirect to my_tree. But, obviously, I don't want all requests to family_tree/:id to show /my_tree. E.g. if the family_tree associated with the current_user is id=51, then when that user goes to family_tree/52, that URL should say 'family_tree/52`.
# app/controllers/family_trees_controller.rb
class FamilyTreesController < ApplicationController
# family_tree GET /family_trees/:id(.:format)
#
# #note !IMPORTANT
# Should only have 1 or 2 instance vars per action
def show
# #user = #family_tree.user #=> not needed, available on the primary instance var (#family_tree)
if current_user == family_tree.user
redirect_to my_tree_index_path
else
# #memberships = #family_tree.memberships
# #memberships_grouped_by_relations #=> too long of a name!
# There is currently only one grouped membership,
# why not rename it to:
#grouped_memberships = family_tree.memberships.includes(user: :family_tree).group_by(&:relation)
# Shouldn't create another ivar if it's available on the primary ivar
# #nodes = #family_tree.nodes
end
end
protected
def family_tree
#family_tree ||= FamilyTree.find(params[:id])
end
end
# app/controllers/my_trees_controller.rb
class MyTreesController < ApplicationController
# my_tree_index GET /my_tree(.:format)
def index
#grouped_memberships = current_user.family_tree.memberships.group_by(&:relation)
end
end
And the routes:
# config/routes.rb
My::Application.routes.draw do
resources :family_trees
resources :my_tree, only: :index
end
I pass parameters from a view to a controller. The parameter is an array that is generated by the user. The user can add as many items to the array as they want. I want to iterate through this array to create multiple model objects in the DB. How can I go about doing this?
A person can create a meal, and within the meal form, there are options to add as many food items as they wish.
def create
#meal= Meal.new(question_params)
food_options = params[:food_options]
i = 0
if #meal.save
food_options.each do |x|
#meal.foods.Create(:drink => food_option[i], :meal => #meal)
i = +1
end
redirect_to #meal
else
render 'new'
end
end
Any guidance would be much appreciated
Use accepts_nested_attributes_for and let Rails handle it for you.
In the models/meal.rb
class Meal < ActiveRecord::Base
has_many :foods
accepts_nested_attributes_for :foods # <==========
...
end
and in the controller, include the nested attributes:
class MealsController < ApplicationController
...
def create
#meal= Meal.new(question_params)
redirect_to #meals
else
render 'new'
end
...
def question_params
params.require(:meal).permit(...., foods_attributes: [ :drink, .... ]) # <====
end
end
I am new to rails developement and to the MVC architecture. I have a little application where I can add Videos' URLs from Dailymotion or Youtube and get the tweets related to that URL using the twitter gem in Ruby on Rails.
Now i'm able to store the tweets like this : (This is the video controller)
def show
#video = Video.find(params[:id])
# Creating a URL variable
url = #video.url
# Search tweets for the given video/url
#search = get_client.search("#{#video.url} -rt")
# Save tweets in database
#search.collect do |t|
tweet = Tweet.create do |u|
u.from_user = t.user.screen_name.to_s
u.from_user_id_str = t.id.to_s
u.profile_image_url = t.user.profile_image_url.to_s
u.text = t.text.to_s
u.twitter_created_at = t.created_at.to_s
end
end
I'm not sure if this is the right way to do it (doing it in the controller ?), and what I want to do now is to specify that those tweets that have just been stored belong to the current video. Also I would like to have some sort of validation that makes the controller look in the database before doing this to only save the new tweets. Can someone help me with that ?
My models :
class Video < ActiveRecord::Base
attr_accessible :url
has_many :tweets
end
class Tweet < ActiveRecord::Base
belongs_to :video
end
My routes.rb
resources :videos do
resources :tweets
end
This is an example of a "fat controller", an antipattern in any MVC architecture (here's a good read on the topic).
Have you considered introducing a few new objects to encapsulate this behavior? For example, I might do something like this:
# app/models/twitter_search.rb
class TwitterSearch
def initialize(url)
#url = url
end
def results
get_client.search("#{#url} -rt")
end
end
# app/models/twitter_persistence.rb
class TwitterPersistence
def self.persist(results)
results.map do |result|
self.new(result).persist
end
end
def initialize(result)
#result = result
end
def persist
Tweet.find_or_create_by(remote_id: id) do |tweet|
tweet.from_user = screen_name
tweet.from_user_id_str = from_user_id
tweet.profile_image_url = profile_image_url
tweet.text = text
tweet.twitter_created_at = created_at
end
end
private
attr_reader :result
delegate :screen_name, :profile_image_url, to: :user
delegate :id, :user, :from_user_id, :text, :created_at, to: :result
end
Notice the use of find_or_create_by ... Twitter results should have a unique identifier that you can use to guarantee that you don't create duplicates. This means you'll need a remote_id or something on your tweets table, and of course I just guessed at the attribute name (id) that the service you're using will return.
Then, in your controller:
# app/controllers/videos_controller.rb
class VideosController < ApplicationController
def show
#tweets = TwitterPersistence.persist(search.results)
end
private
def search
#search ||= TwitterSearch.new(video.url)
end
def video
#video ||= Video.find(params[:id])
end
end
Also note that I've removed calls to to_s ... ActiveRecord should automatically convert attributes to the correct types before saving them to the database.
Hope this helps!
I'm using devise_invitable with devise in my Rails 3 app. I want to give my users the ability to invite other users and group those invited users in advance of the invitees signing up.
My problem is that once the invitee comes along and signs up (but doesn't use the invite URL), the destroy_if_previously_invited around_filter comes along, destroys the original user record and recreates a new record for the user, retaining the invitation data but not transferring the user_groups records along with it.
I'd like to simply override this around_filter by doing a search for any user_groups that match the originally invited user_id and saving them with the newly created user_id.
I keep getting the error:
LocalJumpError in Users::RegistrationsController#create
no block given (yield)
My route looks like this:
devise_for :users, :controllers => { :registrations => "users/registrations" }
I've set this as the override in app/controllers/users/registrations_controller.rb:
class Users::RegistrationsController < Devise::RegistrationsController
around_filter :destroy_if_previously_invited, :only => :create
private
def destroy_if_previously_invited
invitation_info = {}
user_hash = params[:user]
if user_hash && user_hash[:email]
#user = User.find_by_email_and_encrypted_password(user_hash[:email], '')
if #user
invitation_info[:invitation_sent_at] = #user[:invitation_sent_at]
invitation_info[:invited_by_id] = #user[:invited_by_id]
invitation_info[:invited_by_type] = #user[:invited_by_type]
invitation_info[:user_id] = #user[:id]
#user.destroy
end
end
# execute the action (create)
yield
# Note that the after_filter is executed at THIS position !
# Restore info about the last invitation (for later reference)
# Reset the invitation_info only, if invited_by_id is still nil at this stage:
#user = User.find_by_email_and_invited_by_id(user_hash[:email], nil)
if #user
#user[:invitation_sent_at] = invitation_info[:invitation_sent_at]
#user[:invited_by_id] = invitation_info[:invited_by_id]
#user[:invited_by_type] = invitation_info[:invited_by_type]
user_groups = UserGroup.find_all_by_user_id(invitation_info[:user_id])
for user_group in user_groups do
user_group.user_id = #user.id
user_group.save!
end
#user.save!
end
end
end
I may also just be going about this all wrong. Any ideas would be appreciated.
First, there is no need to set the around_filter again, as devise_invitable already sets this. All you need to do is redefine the methods in your UsersController and those will be called instead of the devise_invitable ones.
Second, it looks like you are combining the two methods when they should remain seperate and overridden (I am basing this off latest version of devise_invitatable 1.1.1)
Try something like this:
class Users::RegistrationsController < Devise::RegistrationsController
protected
def destroy_if_previously_invited
hash = params[resource_name]
if hash && hash[:email]
resource = resource_class.where(:email => hash[:email], :encrypted_password => '').first
if resource
#old_id = resource.id #saving the old id for use in reset_invitation_info
#invitation_info = Hash[resource.invitation_fields.map {|field|
[field, resource.send(field)]
}]
resource.destroy
end
end
end
def reset_invitation_info
# Restore info about the last invitation (for later reference)
# Reset the invitation_info only, if invited_by_id is still nil at this stage:
resource = resource_class.where(:email => params[resource_name][:email], :invited_by_id => nil).first
if resource && #invitation_info
resource.invitation_fields.each do |field|
resource.send("#{field}=", #invitation_info[field])
end
### Adding your code here
if #old_id
user_groups = UserGroup.find_all_by_user_id(#old_id)
for user_group in user_groups do
user_group.user_id = resource.id
user_group.save!
end
end
### End of your code
resource.save!
end
end
When user's create a post I'd like to set the user_id attribute first. I'm trying to do this using alias_method_chain on the arrtibutes method. But I'm not sure if this is right as the problem I thought this would fix is still occurring. Is this correct?
Edit:
When my users create a post they assign 'artist(s)' to belong to each post, using a virtual attribute called 'artist_tokens'. I store the relationships in an artist model and a joined table of artist_ids and post_ids called artisanships.
I'd like to to also store the user_id of whomever created the artist that belongs to their post (and I want it inside the artist model itself), so I have a user_id column on the artist model.
The problem is when I create the artist for each post and try to insert the user_id of the post creator, the user_id keeps showing as NULL. Which is highly likely because the post's user_id attribute hasn't been set yet.
I figured to get around this I needed to set the user_id attribute of the post first, then let the rest of the attributes be set as they normally are. This is where I found alias_method_chain.
post.rb
attr_reader :artist_tokens
def artist_tokens=(ids)
ids.gsub!(/CREATE_(.+?)_END/) do
Artist.create!(:name => $1, :user_id => self.user_id).id
end
self.artist_ids = ids.split(",")
end
def attributes_with_user_id_first=(attributes = {})
if attributes.include?(:user_id)
self.user_id = attributes.delete(:user_id)
end
self.attributes_without_user_id_first = attributes
end
alias_method_chain :attributes=, :user_id_first
EDIT:
class ArtistsController < ApplicationController
def index
#artists = Artist.where("name like ?", "%#{params[:q]}%")
results = #artists.map(&:attributes)
results << {:name => "Add: #{params[:q]}", :id => "CREATE_#{params[:q]}_END"}
respond_to do |format|
format.html
format.json { render :json => results }
end
end
In your controller, why not just do this:
def create
#post = Post.new :user_id => params[:post][:user_id]
#post.update_attributes params[:post]
...
end
But it seems to me that it would be much better to create the artist records after you've done validation on the post rather than when you first assign the attribute.
EDIT
I would change this to a callback like this:
class Post < ActiveRecord::Base
attr_accessor :author_tokens
def artist_tokens=(tokens)
#artist_tokens = tokens.split(',')
end
after_save :create_artists
def create_artists
#artist_tokens.each do |token|
...
end
end
end