I'm a beginner trying out some aspects pertaining to enrollments in my rails app. Users have many enrollments; enrollments belong to a lesson; enrollments have many word_expositions and scrambled_words.
I wrote a helper method in the lessons controller to produce a list of enrolled users, to display in each lesson show page:
helper_method :enrolled_users
def enrolled_users
lesson = Lesson.find(params[:id])
enrollments = lesson.enrollments
enrolled_users = enrollments.map { |enrollment| enrollment.user }
end
I am now trying to implement a completed_expos helper so that I can indicate next to each user whether they have completed the expositional component of my app. What I have is:
helper_method :completed_expos
def completed_expos
lesson = Lesson.find(params[:id])
completed_expos = enrolled_users.map do |user|
current_enrollment = lesson.enrollment_for(user)
all_expos = current_enrollment.word_expositions
all_expos.all? { |expos| expos.completed == true }
end
end
I would like to add a notice next to each listed user regarding whether they completed the expos. So what I tried is:
<% enrolled_users.each do |user| %>
<ul>
<li><%= user.username %></li>
<% if completed_expos.include?(user) %>
completed
<% end %>
</ul>
<% end %>
but the effect is that, although enrolled users are listed, there is no indication as to expo completion in those places where it should list it. How can I go about selecting this subgroup of users?
From what I can get from your code is that your "completed_expos" method will always return an array of Boolean values (true, false) rather than a list of users, so it could never include the user you specify.
I guess what you might want to do was to use select instead of map.
enrolled_users.select do |user|
current_enrollment = lesson.enrollment_for(user)
all_expos = current_enrollment.word_expositions
all_expos.all? { |expos| expos.completed == true }
end
Also, let me say that you don't need to assign the values in each of your methods to a variable with the same name as the method. Eg.
def enrolled_users
lesson = Lesson.find(params[:id])
enrollments = lesson.enrollments
enrolled_users = enrollments.map { |enrollment| enrollment.user }
end
Could be
def enrolled_users
lesson = Lesson.find(params[:id])
enrollments = lesson.enrollments
enrollments.map { |enrollment| enrollment.user }
end
The problem is the helper wasn't returning a user, as to map it into the array:
helper_method :completed_expos
def completed_expos
lesson = Lesson.find(params[:id])
completed_expos = enrolled_users.map do |user|
current_enrollment = lesson.enrollment_for(user)
all_expos = current_enrollment.word_expositions
user if all_expos.all? { |expos| expos.completed == true }
end
end
Related
I am trying to pass stored_products from shopify into a Rails app but keep getting a home controller error at https://f588240c.ngrok.io/ i have made updates, with no luck and restarted the server a number of times with no luck.
Any help would be welcomed. Heres the code
class Api::V1::HomeController < ShopifyApp::AuthenticatedController
def index
#products = ShopifyAPI::Product.find(:all, params: { limit: 10 })
#products.each do |product|
StoredProduct.where(shopify_id: product.id)
.first_or_create do |stored_product|
stored_product.shopify_id = product.id
stored_product.shopify_title = product.title
stored_product.shopify_handle = product.handle
stored_product.shopify_image_url = product.image.src
stored_product.shop_id = #shop.id
stored_product.save
product.images.each do |image|
ProductImage.where(shopify_id: image.id)
.first_or_create do |product_image|
product_image.image_url = image.src
product_image.stored_product_id = stored_product_id
product_image.shopify_id = image.id
end
end
end
end
#stored_products = StoredProduct.belongs_to_shop(#shop.id)
end
end
From the authenticated controller
private
def set_shop
#shop = Shop.find_by(id: session[:shopify])
set_locale
end
from the store_products.rb file
class StoredProduct < ApplicationRecord
belongs_to :shop
has_many :product_images
scope :belongs_to_shop, -> shop_id { where(shop_id: shop_id) }
end
For this specific issue/code tutorial, the private set_shop method should be set like follows:
def set_shop
#shop = Shop.find_by(id: session[:shop_id])
set_locale
end
The other answer has params instead of session
The problem is that #shop is nil. The error message says it cannot call the method .id on NilClass.
In the image I can see that you have a shop_id in the params so you might just need to change your code here:
def set_shop
#shop = Shop.find_by(id: params[:shop_id])
set_locale
end
But that depends on your code, so please double check.
The search method is non-crud action and map is a private method, restaurant, dish, location, pictures are models. these models data contains an array. so how I write test case for map method and search method. restaurant and location has HABTM association, and also restaurant and dish has HABTM association, restaurant and pictures have a polymorphic association, and also dish and pictures has a polymorphic association
def search
map
if params[:name]
#items = Dish.search(params[:name])
end
if params[:price]
#items = Dish.sortby_price(params[:price]).search(params[:name])
end
if params[:ratings]
#items = Dish.sortby_ratings(params[:name])
end
if params[:rating]
#items = Dish.sortby_rating(params[:rating])
end
if params[:category]
#items= Dish.sortby_dietary(params[:category]).search(params[:name])
end
if params[:restaurant]
#restaurants =
Restaurant.find(params[:restaurant])
#items = #restaurants.dishes
end
end
private
def map
#items = Dish.search(params[:name])
restaurants = []
locations = []
pictures = []
#items.each do |d|
#restaurants = d.restaurants
restaurants.push(#restaurants)
d.restaurants.each do |r|
#pictures = r.pictures
pictures.push(#pictures)
#locations = r.locations
locations.push(#locations)
end
end
gon.restaurants = restaurants
gon.locations = locations
gon.pictures = pictures
x = []
#items.each do |d|
#restaurants = d.restaurants
d.restaurants.each do |r|
x.push(r.id)
end
end
y = []
x.each do |x|
r = Restaurant.find(x)
d = r.dishes.count
y.push(d)
end
gon.dishes_count = y
end
Some people say that there is no need to test private methods. But in a company i'm working for we do test private methods.
For your case I'd recommend to do this:
test method #map separately from action #search. You need to check that gon, #items, #restaurants, #pictures, #locations objects got populated correctly.
You can test private methods by using method #send.
Example:
describe '#map' do
subject { controller.send(:map) }
# you would need to stub params method
before { allow(controller).to receive(:params).and_return({ name: 'my name' }) }
it { expect(controller.instance_variable_get(:#items)).to include/not be_blank/... }
end
Test method #search without actually calling method map.
Example:
describe '#search' do
before { allow(controller).to receive(:map) }
# you can set different context where you test cases with different parameters
context 'when params[:name] and params[:ratings] exist' do
before { get :search, { name: '...', ratings: '...' } }
it {...}
end
end
In my application I have a number of pages where I need to display a list of people and allow the user to filter them with a form. And these pages are often similar looking. The filters share parts but still not the same.
I'm wondering how can I avoid repeating almost the same code for different controllers? I tried scopes but I still need to parse parameters and populate form in a view anyway.
Thanks!
Disclaimer: author of https://github.com/dubadub/filtered is here.
ActiveRecord offers a merge method for relations. It intersects two query parts which allows breaking query logic into parts.
Based on that idea I created a gem https://github.com/dubadub/filtered.
In your case it could be something like:
# app/controllers/people_controller.rb
class PeopleController < ApplicationController
before_action :set_filter
def index
#people = People.all.merge(#filter)
end
private
def set_filter
#filter = PersonFilter.new(filter_params)
end
def filter_params
params.fetch(:filter, {}).permit(:age, :active, :sorting)
end
end
# app/filters/person_filter.rb
class PersonFilter < ApplicationFilter
field :age
field :active do |active|
-> { joins(:memberships).merge(Membership.where(active: active)) }
end
field :sorting do |value|
order_by, direction = value.values_at("order", "direction")
case order_by
when "name"
-> { order(name: direction) }
when "age"
-> { order(age: direction) }
else
raise "Incorrect Filter Value"
end
end
end
# app/views/people/index.slim
= form_for(#filter, url: search_path, method: "GET", as: :filter) do |f|
.fields
span Age
= f.select :age, (18..90).map { |a| [ a, a ] }
.fields
span Active
= f.check_box :active
.fields
span Sorting
span Name
= f.radio_button :sorting, "name asc"
= f.radio_button :sorting, "name desc"
span Age
= f.radio_button :sorting, "age asc"
= f.radio_button :sorting, "age desc"
.actions
= f.submit "Filter"
Hope it helps!
Have you had a look at query objects?
https://mkdev.me/en/posts/how-to-use-query-objects-to-refactor-rails-sql-queries
They allow you to reuse the code in many places, you'd be able to simply pass the params.permit(...) and get get AR output.
# app/queries/user_query.rb
class UserQuery
attr_accessor :initial_scope
def initialize(scoped = User.all)
#initial_scope = initial_scope
end
def call(params) # is what you pass from your controller
scoped = by_email(#initial_scope, params[:email]
scoped = by_phone(scoped, params[:phone]
# ...
scoped
end
def by_email(scoped, email = nil)
email ? where(email: email) : scoped
end
def by_phone(scoped, phone = nil)
phone ? where(phone: phone) : scoped
end
end
# users_controller.rb
class UsersController < ApplicationController
def index
#users = UserQuery.new(User.all)
.call(params.permit(:email, :phone))
.order(id: :desc)
.limit(100)
end
end
# some other controller
class RandomController < ApplicationController
def index
#users = UserQuery.new(User.where(status: 1))
.call(params.permit(:email))
.limit(1)
end
end
You can probably refactor this example to reduce the upfront investment into writing these queries for richer objects, do post here if you come up with alternatives for so that others can learn how to use query objects.
I have my user gender stored as a string male/female (as this is how it is retrieved from Facebook API)
The goal is to render a partial showing users by gender depending on the gender of the logged in user, but I'm having a little trouble setting up a helper
def male
#male = #user.gender.male
end
then my view..
<% #users.male.each do |male| %>
<ul>
<li><%= male.firstname %></li>
<li><%= male.age %></li>
<li><%= male.location %></li>
</ul>
What am I doing wrong, been stuck on this for ages :(
You can use scopes:
class User < ActiveRecord::Base
scope :male, -> { where gender: 'male' }
scope :female, -> { where gender: 'female' }
end
Controller:
def index
#users = User.all
#males = #users.male # or use User.male, but #users might already be filtered
#females = #users.female
end
And in your view:
<%= #males.each do |user| %>
...
<% end %>
actually i use restful-authentication but ai need call method logged_in from MODEL (no controller) distinct of user.
can you help me...
example:
modelx.rb
def price
if logged_in?
#product.price = current_user.prices
else
#product.price = 0
end
end
It's a better design to pass that information in from where you are calling the method.
def price(logged_in = false)
if logged_in
#product.price = current_user.prices
else
#product.price = 0
end
end
Calling it from the controller or view:
#modelx.price(logged_in?)