looping over records with condition - ruby-on-rails

I have a Customer model that has the attribute private. This attribute is only visible for a total of 3 users in the database. If this method (check_box) gets checked by one of those 3 users the Customer is only visible by them.
I'm currently looping over all of the Customers like this:
<% #customers.where(:private => false).each do |single_customer| %>
My question is how can I accomplish when one of the 3 users is signed in that :private => false gets changed to #customers.each do |single_customer| because then I don't want to filter the private attribute anymore.

you need to change the build up of the loop. This snippet is a bit of meta programming, but you can do it like this:
# in the controller for example.
#customers = Customer.where(private: false)
#customers = Customer.all if current_user.is_my_special_user?
In the view you then simply do this: <% #customers.find_each do |customer| %>
use find_each for better performance if your collection is huge.
by default you use the private: false
if you detect your user is logged in, you overwrite the #customers

Related

How do I use a button to update the order of a filtered category?

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

Return list based on has_many relationship

A State can have many Units. A Unit can only have one State.
On the home page I want to return a list of ONLY the states that actually have units. Right now they are all listed by their name.
So if Arizona and Colorado are the only ones with units...they will only show up on the home page.
I'm new to Rails and I've searched and searched. But, I'm missing something. Seems like it would be some sort of query or filter on the controller? So it is only returning the States that have Units? Any help would be appreciated.
Static page controller
def home
#units = Unit.all
#states = State.order('long_name')
end
Home view
<% #states.each do |state| %>
<%= link_to state.long_name, state %>
<% end %>
JOIN should be enough:
#states = State.joins(:units).order('states.long_name ASC')
or, you can move this to model scopes:
scope :with_units, joins(:units).order('states.long_name ASC')
and call it in controller with:
#states = State.with_units
Controller is not a place for that. It is a typical model concern. This should get you working:
States.all.select {|state| !state.units.empty?}
However it would be much nicer to create the following method for State class:
class State <ActiveRecord::Base
...
def class.with_units
select {|state| !state.units.empty?}
end
end
This will give you:
State.with_units #=> All states with units
But can also be used on association:
user.favourite_states.with_units #=> All user's favourite states filtered to ones with units.

need assistance with some ruby array code, please

I am making an app where current_user (logged in user) can write reviews, and make each review public or private,
with a radio button.
If public, every other user can see that review. If private, only current_user can see it.
visible.true and visible.false, depending on which radio button is selected.
I'm trying to come up with the code/syntax to get this working?
Something like:
#review.user is the person who wrote a particular review
#if review.user is not current_user, and the review is
#marked as false, then don't show that review
If review.user != current_user
&& review.visible = false
don't show review.
At present in a reviews_helper.erb I have:
def review_block(review, options = {})
options = {:review => review}
render 'reviews/review', options
end
And in my view, show.html.erb:
<div class="reviews" >
<% #reviews.each do |review| %>
<%= review_block review %>
<% end %>
</div>
Any chance you could tell me how I should modify my helper to get it working, or any other suggestions?
In the case you want to filter the #reviews array you could do something like this:
#reviews.select { |review| review.visible_to?(current_user) }.each do |review|
render 'reviews/review', :review => review
end
The Array's #select method filters a given array with the condition passed as block. I would move the visibility logic to the Review model to the method call visible_to? which would be something like you said above:
# review.rb
def visible_to?(user)
self.user.id == user.id || # assuming they have an ID
visible == true
end
Better yet, if you are using Rails you can completely remove the select method call from the view and create a scope in the Review class.
Edit: Using a scope
#review.rb
scope :visible_to, lambda { |user| conditions( 'user_id = ? or visible = ?', user.id, true ) }
This way, when you are building your #reviews array - presumably in a controller action, you can do something like this:
#reviews_controller.rb
#reviews = Review.visible_to(current_user)
You can obviously nest several scopes - like order, limit, where, etc - and filter the review the way you want. Nevertheless the utility visible_to? method should also be defined for the instance itself alongside with the scope.
Always keep in mind to have your views as dumber as you can, i.e. your views should know the least about your models and your business logic. This will ensure there are no tight dependencies between your views and your models.

Looping through a list of checkboxes array to insert multiple record in ruby on rails 3.1 in controller

I have a list of check boxes created with check_box_tag (<%= check_box_tag "user_ids[]", user.id %>).
Now i want to loop through all the check-boxes based on users_ids in controller and insert data of all the users selected.
As Meltemi says, the usual way to iterate in ruby is .each. In your case, probably something like this, in the controller that receives the form:
params[:user_ids].each do |user_id|
u = User.find(user_id)
u.do_something_to_that_user #call a method or some such on the user
something_else.users << u #associate that user with something else
end
Alternatively, it can often be more efficient to do all of this in one go, though the exact form thereof depends on what you're doing with the user. For instance, if you want to associate the checked users with some record:
Record.user_ids = params[:user_ids]
Or if you want to update all of those users in some way:
User.where(:id => params[:user_id]).update_all(:attribute => some_value)

Using a block to name and create instance variables [Ruby-on-Rails]

I'm working on a CRUD interface for managing users in my application. I have a constant User::ROLES that is an array with the names of valid user roles in my app (admin, teacher, student).
What I'm wanting to do is, in the index action of the controller, have a block that iterates through the ROLES constant and creates a scoped instance variable from #users (which is already initialized before the block). Here's what the index method looks like so far:
def index
#users = user.all
##students = #users.where(:role => "student") # This works by itself
User::ROLES.each do |r|
#r = #users.where(:role => r.to_s)
end
end
So I want to be able to name the instance variable by what's passed through the block, so #r creates #admin, #teacher, etc. Since I'm new to ruby and rails, I don't quite understand the syntax for doing this. Who knows? There's probably a better way to do it anyways.
EDIT
Just to let everyone know, I'm hoping to use this in the view on the index action to display a list of users grouped by their role. Of course, it will be used in other ways too throughout the CRUD interface, which is why I didn't clarify the use-case earlier, since it's multi-purpose.
This is probably a bad idea. If you change a role in your model, you will need to update your view accordingly to use a different instance method. A better way to handle this would be to simply group users by their role:
#users = User.group(:role).all
Which would give you a hash with keys named after each role that you could easily use to build a dynamic view:
<% #users.each_pair do |role, users| %>
<h2><%= role.to_s.titlelize %></h2>
<% users.each do |user| %>
...
<% end %>
<% end %>
You can also access users with a specific role if needed:
<h2>Admin users:</h2>
<%= #users[:admin].map(&:name).to_sentence %>

Resources