Rails 4: ActionMailer Preview cannot find Class - ruby-on-rails-4

I am trying to preview an email and I'm running into some trouble.
# test/mailers/previews/food_order_preview.rb
class FoodOrderPreview < ActionMailer::Preview
def food_order_email
#food_order = FoodOrder.first # line with error
#fields = #food_order.with_values
FoodOrder.food_order_email(order, fields)
end
end
When I load the preview I see:
NoMethodError (undefined method `first' for FoodOrder:Class):
test/mailers/previews/food_order_preview.rb:6:in `food_order_email'
Why would this occur?

Thanks to this SO Question I was able to diagnose the issue. There was an issue with the migration I ran while creating the mailer.
THE ROOT CAUSE OF THE PROBLEM
When I created the Invite mailer, I ran rails g mailer Invite instead
of rails g mailer InviteMailer.
Because of this, Invite as a mailer override Invite as a model, hence
creating errors as soon as methods were applied to instances of the
Invite model.
HOW WE FIXED IT
Once we had identified the issue, fixing it was pretty
straightforward:
We changed the name of the Invite mailer from invite.rb to
invite_mailer.rb
In the newly renamed invite_mailer.rb file, we
replaced class Invite < ApplicationMailer with class InviteMailer <
ApplicationMailer
Hope this helps someone else!

Related

Google API Scope Changed

edit: I solved it easily by adding "https://www.googleapis.com/auth/plus.me" to my scopes, but I wanted to start a discussion on this topic and see if anyone else experienced the same issue.
I have a service running on GCP, an app engine that uses Google API. This morning, I've received this "warning" message which threw an 500 error.
It has been working fine for the past month and only threw this error today (5 hours prior to this post).
Does anyone know why Google returned an additional scope at the oauth2callback? Any additional insight is very much appreciated. Please let me know if you've seen this before or not. I couldn't find it anywhere.
Exception Type: Warning at /oauth2callback
Exception Value:
Scope has changed from
"https://www.googleapis.com/auth/userinfo.email" to
"https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/plus.me".
This line threw the error:
flow.fetch_token(
authorization_response=authorization_response,
code=request.session["code"])
The return url is https://my_website.com/oauth2callback?state=SECRET_STATE&scope=https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/plus.me#
instead of the usual https://my_website.com/oauth2callback?state=SECRET_STATE&scope=https://www.googleapis.com/auth/userinfo.email#
edit: sample code
import the required things
SCOPES = ['https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/calendar',
# 'https://www.googleapis.com/auth/plus.me' <-- without this, it throws the error stated above. adding it, fixes the problem. Google returns an additional scope (.../plus.me) which causes an error.
]
def auth(request):
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES)
flow.redirect_uri = website_url + '/oauth2callback'
authorization_url, state = flow.authorization_url(
access_type='offline', include_granted_scopes='true',
prompt='consent')
request.session["state"] = state
return redirect(authorization_url)
def oauth2callback(request):
...
# request.session["code"] = code in url
authorization_response = website_url + '/oauth2callback' + parsed.query
flow.fetch_token(
authorization_response=authorization_response,
code=request.session["code"])
...
We discovered the same issue today. Our solution has been working without any hiccups for the last couple of months.
We solved the issue by updating our original scopes 'profile email' to https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile and by doing some minor changes to the code.
When initiating the google_auth_oauthlib.flow client, we previously passed in the scopes in a list with only one item which contained a string in which the scopes were separated by spaces.
google_scopes = 'email profile'
self.flow = Flow.from_client_secrets_file(secret_file, scopes=[google_scopes], state=state)
Now, with the updated scopes, we send in a list where each element is a separate scope.
google_scopes = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile'
self.flow = Flow.from_client_secrets_file(secret_file, scopes=google_scopes.split(' '), state=state)
Hope it helps, good luck!
I am using requests_oauthlib extension and I had the same error. I fix the issue by adding OAUTHLIB_RELAX_TOKEN_SCOPE: '1' to environment variables. So my app.yaml file is similar to this:
#...
env_variables:
OAUTHLIB_RELAX_TOKEN_SCOPE: '1'
For my case I added the following line in that function that the authentication is happening in
os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1'
flow = InstalledAppFlow.from_client_config(client_config, scopes=SCOPES)
At a guess from your error it looks like you're using a depreciated scope. See:
https://developers.google.com/+/web/api/rest/oauth#deprecated-scopes
I'm also guessing that you may be using the Google+ Platform Web library and maybe the People:Get method. Perhaps try using one of the following scopes instead:
https://www.googleapis.com/auth/plus.login
or
https://www.googleapis.com/auth/plus.me
Given the timing, you might be effected by this change by Google:
"Starting July 18, 2017, Google OAuth clients that request certain sensitive OAuth scopes will be subject to review by Google."
https://developers.google.com/apps-script/guides/client-verification

Pundit::AuthorizationNotPerformedError attempting to adapt microposts to Devise/Pundit

I'm new to Rails and I'm working through Michael Hartl's excellent Rails Tutorial for a second time, this time I'm trying to adapt the chapter 11 and chapter 12 microposts to a simple Devise/Pundit application I'm working on. I am able to create microposts through the seed file and display them, but I'm getting an authorization error with Pundit when I actually try to create a new post through the site. The error I'm getting is:
Pundit::AuthorizationNotPerformedError in MicropostsController#create
My Microposts Controller looks like this:
class MicropostsController < ApplicationController
before_action :authenticate_user!
after_action :verify_authorized
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to current_user
else
#feed_items = []
flash[:danger] = "Unable to create micropost!"
end
end
def destroy
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
end
I'm thinking that do not have the authorization set up properly for the 'create' action, but I'm not sure exactly how it should be set. I do not have a policy for Pundit for Microposts. I tried adding a simple one but it didn't change anything. I'm learning to put all these pieces together, would someone point me in the right direction?
There is one after action filter verify_authorized because of which you are getting this error. If you have created a policy for the create action then use that to get rid of the error.

Rails 4 Action Mailer Previews and Factory Girl issues

I've been running into quite an annoying issue when dealing with Rails 4 action mailer previews and factory girl. Here's an example of some of my code:
class TransactionMailerPreview < ActionMailer::Preview
def purchase_receipt
account = FactoryGirl.build_stubbed(:account)
user = account.owner
transaction = FactoryGirl.build_stubbed(:transaction, account: account, user: user)
TransactionMailer.purchase_receipt(transaction)
end
end
This could really be any action mailer preview. Lets say I get something wrong (happens every time), and there's an error. I fix the error and refresh the page. Every time this happens I get a:
"ArgumentError in Rails::MailersController#preview
A copy of User has been removed from the module tree but is still active!"
Then my only way out is to restart my server.
Am I missing something here? Any clue as to what is causing this and how it could be avoided? I've restarted my server 100 times over the past week because of this.
EDIT: It may actually be happening any time I edit my code and refresh the preview?
This answers my question:
https://stackoverflow.com/a/29710188/2202674
I used approach #3: Just put a :: in front of the offending module.
Though this is not exactly an answer (but perhaps a clue), I've had this problem too.
Do your factories cause any records to actually be persisted?
I ended up using Factory.build where I could, and stubbing out everything else with private methods and OpenStructs to be sure all objects were being created fresh on every reload, and nothing was persisting to be reloaded.
I'm wondering if what FactoryGirl.build_stubbed uses to trick the system into thinking the objects are persisted are causing the system to try and reload them (after they are gone).
Here's a snippet of what is working for me:
class SiteMailerPreview < ActionMailer::Preview
def add_comment_to_page
page = FactoryGirl.build :page, id: 30, site: cool_site
user = FactoryGirl.build :user
comment = FactoryGirl.build :comment, commentable: page, user: user
SiteMailer.comment_added(comment)
end
private
# this works across reloads where `Factory.build :site` would throw the error:
# A copy of Site has been removed from the module tree but is still active!
def cool_site
site = FactoryGirl.build :site, name: 'Super cool site'
def site.users
user = OpenStruct.new(email: 'recipient#example.com')
def user.settings(sym)
OpenStruct.new(comments: true)
end
[user]
end
site
end
end
Though I am not totally satisfied with this approach, I don't get those errors anymore.
I would be interested to hear if anyone else has a better solution.

pundit policies with namespaces

I have Question model in my application.
app/models/question.rb
class Question < ActiveRecord::Base
...
end
I'm using 'pundit' gem for authorization. There are two controllers to do some changes in questions: one for registered user, one for admin.
I'm trying to create separate policies for controllers.
app/controllers/questions_controller.rb
class QuestionsController < ApplicationController
...
end
app/policies/question_policy.rb
class QuestionPolicy < ApplicationPolicy
...
end
app/controllers/admin/questions_controller.rb
class Admin::QuestionsController < Admin::ApplicationController
...
end
app/policies/admin/question_policy.rb
class Admin::QuestionPolicy < Admin::ApplicationPolicy
...
end
When I'm trying to use 'authorize' method in Admin::QuestionsController it uses app/policies/question_policy.rb class not from admin folder.
Gem's documentation says that is should work like I described above (https://github.com/elabs/pundit#namespaced-policies).
Can somebody help me with that?
I was trying to get separated policies for the main app and the ActiveAdmin and ended up with a working solution by creating a customized PunditAdapter to be used in config/initializers/active_admin.rb
class NamespacedPunditAdapter < ActiveAdmin::PunditAdapter
def get_policy(subject, user, resource)
"ActiveAdmin::#{subject}Policy".constantize.new(user, resource)
end
def retrieve_policy(subject)
case subject
when nil then get_policy(subject, user, resource)
when Class then get_policy(subject, user, subject.new)
else
if subject.class.to_s.split('::')[0] == 'ActiveAdmin'
Pundit.policy!(user, subject)
else
get_policy(subject.class, user, subject)
end
end
end
def scope_collection(collection, _action = Auth::READ)
return collection if collection.class != Class
scope = "ActiveAdmin::#{collection}Policy::Scope".constantize
scope.new(user, collection).resolve
rescue Pundit::NotDefinedError => e
if default_policy_class && default_policy_class.const_defined?(:Scope)
default_policy_class::Scope.new(user, collection).resolve
else
raise e
end
end
end
Another option would be to use an ActiveSupport::Concern as pointed out here
I've created issue in github source code and it was closed with such explanation:
The docs refer to the currently unreleased master branch. You can use it by referring to the github source in your Gemfile.
# Gemfile
gem 'pundit', github: 'elabs/pundit'
A bundle install later your code should work.
You can switch back to a released version on Rubygems as soon as 0.3.0 is out. We're still discussing a few namespacing issues, but it will come soon.
If anyone is still looking for this functionality, I needed it as well for splitting up authorizations between ActiveAdmin and my end-user facing site. I built a Pundit compatible gem for controller-based namespaced authorizations (your policies will work), and I plan to follow any features released for pundit. It also includes an ActiveAdmin adapter.

Why is Digest::SHA1 preventing proper annotation of a model?

I am using annotate in my app and all models are successfully annotated except for user.rb, which shows the following error when I annotate:
Unable to annotate user.rb: wrong number of arguments (0 for 1)
Outside of annotating, everything else works fine. User creation, updating, deletion, login, sign out, it all works properly. I have determined that the problem is with the Digest::SHA1, which I use to create session tokens, as demonstrated below in the snippet from user.rb.
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.hash(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
remember_token = User.hash(User.new_remember_token)
end
If I remove the second (def User.hash(token)) and instead do the following:
def User.new_remember_token
SecureRandom.urlsafe_base64
end
private
def create_remember_token
remember_token = Digest::SHA1.hexdigest(User.new_remember_token.to_s)
end
then annotate is happy and successfully annotates user.rb. However, this isn't really the ruby way as my session helper utilizes that User.hash(token) call several times. What am I not understanding about Digest::SHA1.hexdigest or the way that I am utilizing it?
Looks like you're working through The Rails Tutorial.
The likely reason you're seeing issues with your User.hash method is nothing to do with Digest::SHA1, but is because the method itself is inadvertently overriding Ruby's Object#hash method, which is giving you some cryptic errors. Link to Github issue about it.
So, like this commit to the Sample App repository, rename all your instances of User.hash to User.digest and hopefully that should fix your errors.