Im creating an application where an user can see what workout of the day a trainer will tell him to do. This workout is going to be in the homepage of the app.
The trainer is going to upload the wods weekly, i want to show just one wod per day.
I thought that if i gave the WOD model an integer attribute called wday, so i can do the logic if Wod.wday == Date.today.cwday i would achieve this. This is not working at all, im getting an error at my static_pages controller.
Heres the code:
Wod model:
class Wod < ApplicationRecord
belongs_to :user
def this_day
Date.today.cwday == self.wday
end
end
User model:
class User < ApplicationRecord
has_many :wods
def feed
Wod.this_day
end
Static pages controller:
class StaticPagesController < ApplicationController
def home
if logged_in?
#wod = current_user.wods.build
#feed_items = current_user.feed
end
end
def contact
end
def about
end
end
Im getting the following error:
NoMethodError in StaticPagesController#home undefined method
`this_day' for #<Class:0x007f5870c1e7b8> Did you mean? third
I would like to know how to get this done, any help would be appreciated!
BTW: It works perfectly if i do in the user model Wod.all, but i dont want this
NoMethodError in StaticPagesController#home undefined method
`this_day' for #<Class:0x007f5870c1e7b8>
The problem lies here Wod.this_day. If you want to call this_day on WOD then you need to add it as a class method.
Change
def this_day
Date.today.cwday == self.wday
end
to
def self.this_day
Date.today.cwday == wday
end
Alternate solution is to use scopes.
class Wod < ApplicationRecord
belongs_to :user
scope :this_day, -> { where(wday: Date.today.cwday) }
end
I fixed it by changing the method this_day to:
def self.this_day
where("wday = ?", Date.today.cwday)
end
Related
I have the following models:
class Page < ApplicationRecord
has_one :architecture
end
class Architecture < ApplicationRecord
belongs_to :page
end
And after a new page is saved I need to capture it's architecture (number of paragraphs por example). I would like to know what is the proper way to do that. I'm not sure if I should leave that responsible for the Page model:
class Page < ApplicationRecord
has_one :architecture
after_create :scrape_architecture
private
def scrape_architecture
data = call_something_to_capture_architecture(url)
create_architecture(data)
end
end
class Architecture < ApplicationRecord
belongs_to :page
end
or if it should be the responsibility of the Architecture model:
class Page < ApplicationRecord
has_one :architecture
after_create :create_architecture
end
class Architecture < ApplicationRecord
belongs_to :page
before_create :scrape_page
private
def scrape_page
data = call_something_to_capture_architecture(page.url)
create(data)
end
end
Which is actually incorrectly because before_create runs after the validation – causing MySQL errors duo to non null constraints
Thank you.
I would just create a job or service object that handles scraping.
class PageScrapingJob < ApplicationJob
queue_as :default
def perform(page)
data = call_something_to_capture_architecture(page.url)
architecture = page.create_actitecture(data)
# ...
end
end
You would then call the service/job in your controller after saving the page:
class PagesController < ApplicationController
def create
#page = Page.new(page_params)
if #page.save
PageScrapingJob.perform_now(#page)
redirect_to #page
else
render :new
end
end
end
This gives you a perfect control of exactly when this is fired and avoids placing even more responsibilities onto your models. Even though your models may contain little code they have a huge amount of responsibilities such as validations, I18n, form binding, dirty tracking etc that are provided by ActiveModel and ActiveRecord. The list really goes on and on.
This instead creates a discrete object that does only one job (and hopefully does it well) and that can be tested in isolation from the controller.
For such things you could use a service pattern
class PageScrapper
Result = Struct.new(:success?, :data)
def initialize(url)
#url = url
end
def call
result = process(#url)
...
if result.success? # pseudo methods
Result.new(true, result)
else
Result.new(false, nil)
end
end
end
class Fetcher
...
def call
scrapper = PageScrapper.new(url)
result = scrapper.call
if scrapper.success?
page = Page.build(parsed_result_if_needed(result)
page.architecture.build(what_you_need)
page.save # here you need to add error handling if save fails
else
# error handling
end
end
There are a lot of resources about why callbacks are bad.
Here is one from Marcin Grzywaczewski but you can also google it "why callbacks are bad ruby on rails".
By using service you are liberating models from having too much business logic and they do not need to know about other parts of your system.
Once in a while when trying to fetch an order record for a particular user, a ActiveRecord::RecordNotFound is raised.
Some things to note here.
The error is raised when visiting /orders/:id, but not for all users. We track completed orders (meaning that you end up on a orders page) and around 50% gets a 404. Note that we're talking about 50% of the users, not the requests. If it displays 404 once for an order for a particular user, it will always display a 404.
The record exists as it can be accessed via the console using the same data that's being logged in the controller.
The problem disappears when re-deploying the application.
What could the problem be?
I'm running rails 4.2.0.
class OrdersController < ApplicationController
#
# GET /orders/:id
#
def show
Rails.logger.info "current_user=#{current_user.id}, params[:id]=#{params[:id]}"
#order = current_user.orders.find(params[:id])
end
end
class ApplicationController < ActionController::Base
def current_user
#_current_user ||= User.find_by_id(cookies.signed[:uid])
end
end
class User < ActiveRecord::Base
has_many :subscriptions
has_many :orders, through: :subscriptions
end
class Order < ActiveRecord::Base
has_one :user, through: :subscription
end
class Subscription < ActiveRecord::Base
belongs_to :user
end
Here's the log output
[User#id=2454266] Parameters: {"id"=>"1553"}
[User#id=2454266] current_user=2454266, params[:id]=1553 <==== Rails.logger.info
[User#id=2454266] Completed 404 Not Found in 240ms
[User#id=2454266]
ActiveRecord::RecordNotFound (Couldn't find Order with 'id'=1553):
app/controllers/orders_controller.rb:6:in `show'
Running User.find(2454266).orders.find(1553) in the console works.
Also note that it's possible to skip the relation and go directly to the order model, like this
class OrdersController < ApplicationController
#
# GET /orders/:id
#
def show
#order = Order.find(params[:id])
end
end
The way we arrived at the conclusion can be found by going through the comments list.
Our summary of findings are:
Either orders are cached, or current_user is outdated (along with cached associations)
Going straight to order works (i.e. Order.find / User.find(current_user.id).orders...
Solution that we arrived at is:
current_user.reload!
Before performing
current_user.orders.find(params[:id])
class ApplicationController < ActionController::Base
def current_user
#_current_user ||= User.find_by_id(cookies.signed[:uid])
end
def user_signed_in?
#_current_user.present?
end
end
class OrdersController < ApplicationController
def show
if user_signed_in?
#order = current_user.orders.find(params[:id])
else
return redirect_to <404 page or any>, alert: 'Order not found'
end
end
end
I think you have to check the presence of the user first, then you can trace the bug
I usually use this method when I got some issue
def show
if user_signed_in?
begin
#order = current_user.orders.find(params[:id])
rescue => e
binding.pry
end
else
return redirect_to <404 page or any>, alert: 'Order not found'
end
end
but you need 'rails-pry' gem, the advantage is rails will fire up rails console when there is exception
Tricky issue...
Assume the following models:
class Foo::Bar < ActiveRecord::Base
class Foo::Nut < ActiveRecord::Base
The following route:
namespace :admin do
resources :bars do
resources :nuts do
In the create action for nuts at /admin/bars/100/nuts, I create the model based on post data and would like to respond with:
#respond_with(:admin, #bar, #nut) (where bar and nut had been set up in the action)
I'm presented with this lovely error:
NoMethodError (undefined method `admin_foo_bar_foo_nut_url')
I'd like rails to look for admin_bar_nut_url and not admin_foo_bar_foo_nut_url.
Any ideas if I can get around this? Clearly something up with having my models define in modules...
Would prefer to have to abandon the model namespacing but can if I must.
Thanks so much!
I do not see why you need to go away with name space rather than using respond_to instead of respond_with
respond_to do |format|
format.html { redirect_to(admin_bar_nut_url(#bar, #nut)) }
end
To remove foo from your routes, override model_name in your models like:
class Foo::Bar < ApplicationRecord
def self.model_name
ActiveModel::Name.new(self, Foo)
end
end
class Foo::Nut < ApplicationRecord
def self.model_name
ActiveModel::Name.new(self, Foo)
end
end
This will result in Foo::Nut.model_name.route_key being "nuts" instead of "foo_nuts"
And now respond_with should work as desired.
I would like to use an after_save callback to set the updated_by column to the current_user. But the current_user isn't available in the model. How should I do this?
You need to handle it in the controller. First execute the save on the model, then if successful update the record field.
Example
class MyController < ActionController::Base
def index
if record.save
record.update_attribute :updated_by, current_user.id
end
end
end
Another alternative (I prefer this one) is to create a custom method in your model that wraps the logic. For example
class Record < ActiveRecord::Base
def save_by(user)
self.updated_by = user.id
self.save
end
end
class MyController < ActionController::Base
def index
...
record.save_by(current_user)
end
end
I have implemented this monkeypatch based on Simone Carletti's advice, as far as I could tell touch only does timestamps, not the users id. Is there anything wrong with this? This is designed to work with a devise current_user.
class ActiveRecord::Base
def save_with_user(user)
self.updated_by_user = user unless user.blank?
save
end
def update_attributes_with_user(attributes, user)
self.updated_by_user = user unless user.blank?
update_attributes(attributes)
end
end
And then the create and update methods call these like so:
#foo.save_with_user(current_user)
#foo.update_attributes_with_user(params[:foo], current_user)
I have a User & Profile Models. A user has_one profile IE.
class User < ActiveRecord::Base
has_secure_password
# Relationships
has_one :profile
class Profile < ActiveRecord::Base
# Relationships
belongs_to :user
I am then trying to test to see if the user has a profile. If not redirect them to the profile controller ie.
class User::BaseController < ApplicationController
# Filters
before_filter :check_for_profile
# Layout
layout "backend"
# Helpers
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def check_for_profile
if current_user.profile.empty?
redirect_to new_user_profile_path, :notice => "Please create a profile first."
end
end
end
No matter what I try I'm getting the error.
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.empty?
I'm pretty sure my relationships are right. Can anyone shed some light on what I'm doing wrong ?
Thank you in advance.
Lee
try profile.blank? instead. empty? is not defined for nil.
Check out the "blank?" method at the following link. The "present?" method should also be considered - they're basically the same.
http://api.rubyonrails.org/classes/Object.html#method-i-blank-3F