I have a form drop down, that shows me all the emails in my User db table.
<%= f.collection_select(:accessor_id, User.all,:email ,:email) %>
I want to exclude from this list the value of the current's user email, which I can find with with current_user.email (already defined and working)
I know I can achieve this via the following query:
<%= f.collection_select(:accessor_id, User.select(:email).where("email !=?" , current_user.email),:email ,:email) %>
i wanted to know if it is possible to do this after User.all returned all of the values.
you mean something like
User.all.reject {|user| user == current_user}
or more precisely i would fetch all users somewhere in the controller
def index
#users = User.all
end
and use something like that in the form
<%= f.collection_select(:accessor_id, #users.reject {|user| user == current_user}.map(&:email)) %>
#phoet answer is correct, personally I would probably do this at database level anyhow, something along lines of
class User < ActiveRecord::Base
# ...
def self.all_without(excluded)
where("id NOT IN (?)", excluded)
end
end
<%= f.collection_select(:accessor_id, User.all_without([current_user]), :email ,:email) %>
try to keep the view 'clean' of the details, if possible
# and if you really do only want to pull email from the database, you can chain the query
User.all_without([current_user]).select(:email)
Related
I am new to Rails, but slowly making progress. I can't quite wrap my head around how to achieve my next task.
I have a controller (IdeasController) with an index that looks like this:
def index
if params[:round].blank? && params[:challenge].blank?
#ideas = Idea.all.order(params[:sort])
# #ideas = Idea.all.order(created_at: :desc, cached_votes_up: :desc)
end
if params[:round].present?
#round_id = Round.find_by(name: params[:round]).id
#ideas = Idea.where(round_id: #round_id).order("created_at DESC")
end
if params[:challenge].present?
#challenge_id = Challenge.find_by(name: params[:challenge]).id
#ideas = Idea.where(challenge_id: #challenge_id).order("created_at DESC")
end
end
I am updating the view and filtering by category with the above :round and :challenge with the code below in my index.html.erb:
<%= link_to "All", ideas_path %>
<% Round.all.each do |round| %>
<%= link_to round.name, ideas_path(round: round.name) %>
<% end %>
<% Challenge.all.each do |challenge| %>
<%= link_to challenge.name, ideas_path(challenge: challenge.name) %>
<% end %>
Now, my problem is that I want to create a button that orders by created_at DESC or ASC. I want the button to essentially be a toggle. I also want another button to order by cached_weighted_average DESC or ASC. This is from acts_as_votable so I can sort by vote counts.
The problem I am running into is that I can create a link or button that orders by created_at or cached_weighted_average, but it replaces all of the URL that was previously filtered by :round or :challenge. For example, if a user clicks "Round 1" and sees all ideas marked for "Round 1" and then they click the link to order by cached_weighted_average, the URL replaces:
/ideas?round=Round+1
With this:
/ideas?sort=cached_weighted_average+ASC
What I want is:
/ideas?round=Round+1&?sort=cached_weighted_average+ASC
I know this is a very new question, but everything I have tried has failed so far. It feels like I am missing something very easy. What I noticed I can do easily is inside the controller I can do something like:
if params[:round].present?
#round_id = Round.find_by(name: params[:round]).id
#ideas = Idea.where(round_id: #round_id).order("cached_weighted_average DESC")
end
Which is perfect. This button just needs to switch between cached_weighted_average DESC and created_at DESC.
Any help is appreciated, thanks.
passing multiple parameters is one way to handle:
<%= link_to object.name, object_path(first: something, second: something_else) %>
then alter your conditionals to contemplate presence of multiple params.
to differentiate between round and challenge when attempting to allow the user to choose how they'd like to sort you could use the same name and then pass it different values.
something like:
params["round_or_challenge"]
this would change your conditional to something like:
if params["round_or_challenge"] == "round" && params["asc_or_desc"] == "asc"
# query
elsif params["round_or_challenge"] == "challenge"
# query
end
or whatever. it's basically the same...just pass the values you need. you can also pass the existing parameters from the view the same way you access them in the controller.
Thanks for the response, #toddmetheny. I didn't implement your solution, but your solution helped me understand passing multiple parameters a bit more.
I ended up creating a helper, sortable. I also used the url_for to append at the end of whatever the current URL might be. I liked this approach because it meant I could sort on any parameter. I'm not sure that it's the best solution, but it works.
def sortable (name, sort)
link_to name, url_for(params.merge(sort: sort))
end
I am quite new at rails and I am having some trouble designing an admin dashboard.
What I want to achieve is this:
Have a list of multiple users from database.
Have the ability to select multiple records.
Have the ability to apply different actions to all of the
selected records.
The actions MAY not be directly translatable into SQL queries. (for example send an email)
I am not looking for a complete solution to the problem just a general description on how to approach this. I have a feeling I started on a wrong path.
So far I am doing this:
view
<%= form_tag("some_path", method: "get") do %>
<% #users.each do |user| %>
<%= check_box_tag "user_ids[]", users.id %>
<%end%>
<%= submit_tag("Send Email") %>
<%end%>
controller
def send_email
#recipients = User.find(params[:user_ids])
#recipients.each do |recipient|
Notifier.raw_email(recipient.email, params[:user_email][:subject], params[:user_email][:body]).deliver
end
end
This works as it is but i can only apply one action, send email that is.
I want to be able to choose an action to apply to all selected records or apply multiple actions to the selected records
Any thoughts?
You can use the send method to call methods of the model.
class User
def send_email(subject, body)
Notifier.raw_email(self.email, subject, body).deliver
end
end
Let /some_path also accept an array of actions
In our case actions = ['send_email']
In the action that some_path resolves to,
class SomeController < ActionController::Base
def some_action # that some_path resolves to in your config/routes.rb
#recipients = User.find(params[:user_ids])
#actions = params[:actions]
#recipients.each do |recipient|
#actions.each do |action|
recipient.send(action, params[:subject], params[:body])
end
end
end
end
In this way you can call multiple methods. Make sure you only accept valid action values or else the admin can simply call any of the User's methods.
You can have a select tag with the different actions you need.
Then on change of the select tag, you can update the action attribute of the form. eg, using jQuery.
$('#my-action-select').change(function() {
$('#myform').attr('action', $(this).val)
})
I'm trying to create a feed in Rails utilizing acts_as_taggable_on and I'm having a bit of trouble.
In my app there are Users who belong to Groups and every User selects several Tags for himself/herself using the acts_as_taggable_on context :user_tags. The Users create Posts which also have Tags, but of the context :post_tags.
What I'm trying to do is create a feed for the current User comprised of Posts from within his/her Group that have at least one :post_tag in common with the current User's :user_tags. So for example, if I have a User that chooses "Developer, Designer" as two :user_tags, I'd want to return all Posts from within his/her Group that have "Developer" and/or "Designer" as :post_tags.
After hours of fiddling around, here's what I tried in post.rb, but it gave me an error ("ERROR: column 'tag_id' does not exist LINE 1"), which seems odd given that tag_ids do exist for taggings.
# Returns posts with tags also attributed to the given user.
def self.from_posts_matching_tags_of(user)
matching_tag_ids = "SELECT tag_id FROM taggings
WHERE taggable_id = :user_id"
where("tag_id IN (#{matching_tag_ids}) OR user_id = :user_id", user_id: user)
end
Any suggestions for how to properly create such a feed method would be much appreciated. And if there's any other relevant code you want me to share, just let me know.
So, I figured out a solution that has worked thus far in my (admittedly limited) testing. Hope this is helpful to anyone else seeking to do something similar. Of course, if anybody knows a more efficient way to tackle this, I'm always open to improvements.
I added this to pages_controller.rb and it seems to do the trick:
def index
if logged_in?
#feed_items = Post.tagged_with([#current_user.user_tag_list], :on => :post_tags, :any => true)
end
end
And this on index.html.erb:
<% if #feed_items.any? %>
<% #feed_items.each do |post| %>
<%= link_to post.content, post %><br/>
<% end %>
<% end %>
I'm building a martial arts related database, currently I have the following associations set up:
Student has_and_belongs_to_many :styles
Style has_many :ranks
Student has_many :ranks, through: :gradings (and vice versa)
I'm generating a form as follows, depending on the student's styles:
So the headings are generated by the Style model (Tai Chi, Karate...), then their rankings listed below (taken from the Rank model), and the "Dojo" and "Date" fields should belong to the Grading model once created.
The question: I know how to build a form that creates one association (or one association + its children), but how do I build a form that creates multiple associations at once?
Also, what would be a clean way to implement the following:
Only lines which are ticked become associations
Dojo and date must be filled in for ticked lines to save successfully
If a line is unticked it will destroy any previously created associations
This is what I've currently implemented to retrieve the correct records:
class GradingsController < ApplicationController
before_filter :authenticate_sensei!
def index
#student = Student.includes(:styles).find(params[:student_id])
#ranks = Rank.for_student_styles(#student)
split_ranks_by_style
end
private
def split_ranks_by_style
#karate = #ranks.select_style("Karate")
#tai_chi = #ranks.select_style("Tai Chi")
#weaponry = #ranks.select_style("Weaponry")
end
end
# Rank model
def self.for_student_styles(student)
includes(:style).where("styles.id in (?)", student.styles.map(&:id))
end
def self.select_style(style)
all.map { |r| r if r.style.name == style }.compact
end
Complicated forms like this are best handled in a service object initiated in the primary resource's create or update action. This allows you to easily find where the logic is happening afterwards. In this case it looks like you can kick off your service object in your GradingsController. I also prefer formatting a lot of the data in the markup, to make the handling easier in the service object. This can be done a'la rails, by passing a name like "grade[style]" and "grade[rank]". This will format your params coming in as a convenient hash: {grade: {style: "karate", rank: "3"}}. That hash can be passed to your service object to be parsed through.
Without really grasping the full extent of your specific requirements, let's put together an example form:
<%= form_for :grading, url: gradings_path do |f| %>
<h1><%= #rank.name %></h1>
<%- #grades.each do |grade| %>
<div>
<%= hidden_field_tag "grade[#{grade.id}][id]", grade.id %>
<%= check_box_tag "grade[#{grade.id}][active]" %>
...
<%= text_field_tag "grade[#{grade.id}][date]" %>
</div>
<%- end %>
<%= submit_tag %>
<%- end %>
With a form like this, you get your params coming into the controller looking something like this:
"grade"=>{
"1"=>{"id"=>"1", "active"=>"1", "date"=>"2013-06-21"},
"3"=>{"id"=>"3", "date"=>"2013-07-01"}
}
Nicely formatted for us to hand off to our service object. Keeping our controller nice and clean:
class GradingsController < ApplicationController
def index
# ...
end
def create
builder = GradeBuilder.new(current_user, params['grade'])
if builder.run
redirect_to gradings_path
else
flash[:error] = 'Something went wrong!' # maybe even builder.error_message
render :action => :index
end
end
end
So now we just need to put any custom logic into our builder, I'd probably recommend just making a simple ruby class in your /lib directory. It could look something like this:
class GradeBuilder
attr_reader :data, :user
def self.initialize(user, params={})
#user = user
#data = params.values.select{|param| param['active'].present? }
end
def run
grades = data.each{|entry| build_grade(entry)}
return false if grades.empty?
end
private
def build_grade(entry)
grade = Grade.find(entry['id'])
rank = grade.rankings.create(student_id: user, date: entry['date'])
end
end
There will obviously need a lot more work to pass all the specific data you need from the form, and extra logic in the GradeBuilder to handle edge cases, but this will give you a framework to handle this problem in a maintainable and extensible way.
I'm designing a form where you need to add a relation to another object. Okay that's normally fine, but what I am hung up on is a clean way to make it easy for the user to enter the object. There are multiple ways that the user could know how to specify the object (unique identifiers).
Let's take the example of associating a user to a task. In this case, the models are laid out like this:
class User
has_many :tasks
# fields: phone_number, email, username
validates_uniqueness_of :phone_number
validates_uniqueness_of :email
validates_uniqueness_of :username
# other methods, validations, etc which are not important.
end
class Task
belongs_to :user
# other methods, validations, etc which are not important.
end
How would I write the controller and view form, if I want to be able to specify the user by username, email, or phone_number - I might know any one of these, and just one is enough to specify exactly the user that I want, in a clean way?
Currently my solution seems messy. I have a view similar to:
<% form_for #task do |f| %>
... Other stuff
User - choose one of the following ways: <br />
Username: <%= text_field_tag :user_name %> <br />
or phone number: <%= text_field_tag :user_phone %> <br />
or email: <%= text_field_tag :user_email %> <br />
... More other stuff
<% end %>
I then handle these fields explicitly in the controller, finding the actual user based on which ones are filled in:
class TasksController
def create
#task = Task.new(params[:task])
if params[:user_name]
#task.user = User.find_by_username(params[:user_name])
elsif params[:user_phone]
#task.user = User.find_by_phone_number(params[:user_phone])
elsif params[:user_email]
#task.user = User.find_by_email(params[:user_email])
end
if #task.save
redirect_to #task
else
render :action => 'new'
end
end
end
This seems like it's very specific, and there is a lot of code in my controllers, especially if I have lots of these on a form. Don't even talk to me about when you need to dynamically add multiple users to a task - it gets even more crazy in the controller parsing everything out.
I think the most unobtrusive way of doing this is with a few AJAX calls.
I'm thinking an observer on a text field that calls a remote function. That function should update your form with matching potential associations, that the user can select with a radio button or something.
It would look like something like this:
UserController:
def select_user
#user = case params[:query]
when !/\w/ #phone no
User.find_by_phone(params[:query])
when /#/ # email
User.find_by_email(params[:query])
else
User.find_by_username(params[:query])
end
end
views/select_user.rjs:
page.replace_html :matched_user, :inline => <<"PARTIAL"
<%=hidden_field_tag "task[user_id]", #user.id%>
User: <%=#user%>
PARTIAL
task form
<%form_for #task do |f|%>
...
<%= text_field_tag :query %>
<%= observe_field :query, {:controller => :users, :action => :select_user}%>
<div id="matched_user" />
<% end%>
May not work as advertised. I made assumptions and haven't tested it. But it should put you on the right track.
When it comes to multiple users all that really needs to change is the hidden field tag and the rjs file. but not by much. There's also nothing stopping you from using a more robust search mechanism.
Phone numbers are going to be a problem, unless you process the input to strip out hypens, parentheses, etc.
Having said that, here are a couple of options.
1 Roll your own.
In User:
def find_by_foo(search)
# TODO: Pre-process anything in search that looks like a phone number.
conditions = "username like ? or phone_number like ? or email like ?"
wildcard_search = "%#{search}%"
substitutions = []
3.times do
substitutions << wildcard_search
end
User.find(:first, :conditions => ([conditions] + substitutions))
end
2 Rolling your own is stupid when searchlogic already exists.
From their examples under "combining scopes":
User.username_or_first_name_like("ben")
=> "username LIKE '%ben%' OR first_name like'%ben%'"