Attributes not saving with devise and accepts_nested_attributes_for - ruby-on-rails

I have an almost working sign up form with Devise. Whilst it seems to work, it's not saving the fields in my Custaddress table other than the user_id. Any help to work out how to have it save the rest of the information would be great. The following is greatly truncated!!
User.rb contains:
class User < ApplicationRecord
has_one :custaddress
accepts_nested_attributes_for :custaddress
end
Custaddress contains:
class Custaddress < ApplicationRecord
has_many :orders
belongs_to :user
end
Registrations controller contains:
As you can see, this just builds on the "standard" Devise controller. There is no create or new here as I assume it's using the standard methods.
class Users::RegistrationsController < Devise::RegistrationsController
invisible_captcha only: :create
protected
def build_resource(hash = {})
self.resource = resource_class.new_with_session(hash, session)
resource.build_custaddress
# Jumpstart: Skip email confirmation on registration.
# Require confirmation when user changes their email only
resource.skip_confirmation!
# Registering to accept an invitation should display the invitation on sign up
if params[:invite] && (invite = AccountInvitation.find_by(token: params[:invite]))
#account_invitation = invite
# Build and display account fields in registration form if enabled
elsif Jumpstart.config.register_with_account?
account = resource.owned_accounts.first
account ||= resource.owned_accounts.new
account.account_users.new(user: resource, admin: true)
end
end
def update_resource(resource, params)
# Jumpstart: Allow user to edit their profile without password
resource.update_without_password(params)
end
def sign_up(resource_name, resource)
if cookies[:ordernum]
order = Order.where(ordernum: cookies[:ordernum]).first
if order
order.update!(user: resource, custaddress: resource.custaddress)
cookies.delete "ordernum"
end
end
sign_in(resource_name, resource)
# If user registered through an invitation, automatically accept it after signing in
if params[:invite] && (account_invitation = AccountInvitation.find_by(token: params[:invite]))
account_invitation.accept!(current_user)
# Clear redirect to account invitation since it's already been accepted
stored_location_for(:user)
end
end
end
My new.html.erb contains:
<%= form_with(model: resource, as: resource_name, url: registration_path(resource_name, invite: params[:invite])) do |f| %>
<%= f.fields_for :custaddress do |cust| %>
<div class="form-group">
<%= cust.label "Apartment/Unit Number", class: "font-bold" %>
<%= cust.text_field :apartment, class: "form-control", placeholder: "Unit 2 or Apartment 307" %>
</div>
And much more!
My application controller has:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SetCurrentRequestDetails
include SetLocale
include Jumpstart::Controller
include Accounts::SubscriptionStatus
include Users::NavbarNotifications
include Users::TimeZone
include Pagy::Backend
include CurrentHelper
include Sortable
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :masquerade_user!
before_action :store_user_location!, if: :storable_location?
protected
# To add extra fields to Devise registration, add the attribute names to `extra_keys`
def configure_permitted_parameters
extra_keys = [:avatar, :first_name, :last_name, :time_zone, :preferred_language]
signup_keys = extra_keys + [:terms_of_service, :invite, owned_accounts_attributes: [:name], custaddress_attributes: [:address, :apartment, :city, :state, :country, :postcode, :mobile]]
devise_parameter_sanitizer.permit(:sign_up, keys: signup_keys)
devise_parameter_sanitizer.permit(:account_update, keys: extra_keys)
devise_parameter_sanitizer.permit(:accept_invitation, keys: extra_keys)
end
def after_sign_in_path_for(resource_or_scope)
stored_location_for(resource_or_scope) || super
end
# Helper method for verifying authentication in a before_action, but redirecting to sign up instead of login
def authenticate_user_with_sign_up!
unless user_signed_in?
store_location_for(:user, request.fullpath)
redirect_to new_user_registration_path, alert: t("create_an_account_first")
end
end
def require_current_account_admin
unless current_account_admin?
redirect_to root_path, alert: t("must_be_an_admin")
end
end
private
def storable_location?
request.get? && is_navigational_format? && !devise_controller? && !request.xhr?
end
def store_user_location!
# :user is the scope we are authenticating
store_location_for(:user, request.fullpath)
end
end
My logs are showing:
Processing by Users::RegistrationsController#create as JS
17:08:57 web.1 | Parameters: {"authenticity_token"=>"uVphxW4gCQntvHFxRb33dl9cqxv9vlL69Wc2zOMoF1M+pUk8c2HnHwgQFIkMbfmxYraVI7rYBVCPgfSD1u7OHg==", "user"=>{"first_name"=>"[FILTERED]", "last_name"=>"[FILTERED]", "email"=>"[FILTERED]", "password"=>"[FILTERED]", "time_zone"=>"Sydney", "custaddress_attributes"=>{"apartment"=>"", "address"=>"XXXXXXXX", "city"=>"XXXXXXX", "state"=>"XXXXX", "postcode"=>"XXXX", "mobile"=>"XXXXXXXX", "country"=>"XXXXXXX"}, "terms_of_service"=>"1"}, "enc-rmjxhdab"=>"", "button"=>""}
So, I know it's getting the information from the form. However I have no idea where it's saving the Custaddress record. It only seems to be associating the user_id:
Custaddress Create (0.2ms) INSERT INTO "custaddresses" ("user_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["user_id", 3], ["created_at", "2020-09-25 02:20:39.109187"], ["updated_at", "2020-09-25 02:20:39.109187"]]
12:20:39 web.1 | (33.9ms) COMMIT
Can anyone please help me work out why the custaddress isn't saving. I've spent hours on this and read every article on google (well it certainly feels like it).
I've tried to track this down, to no avail.

Adding an answer for completeness.
Devise's create action calls the build_resurce method that you are overriding. the problem is that you overrid it for the new action but didn't take into account the create action.
So, you are doing this:
def build_resource(hash = {})
self.resource = resource_class.new_with_session(hash, session)
resource.build_custaddress
For the new action there's no issue, resource.build_custaddress will instantiate a new custaddress object for you to be able to use the fields_for helper.
The problem is that, for the create action, at that point resource already has a custaddress with the values from the request (set by the previous line), then you do resource.build_custaddress and replace the current custaddress with a new empty one.
The solution is to only build a custaddress if it's not nil:
def build_resource(hash = {})
self.resource = resource_class.new_with_session(hash, session)
resource.build_custaddress if resource.custaddress.nil?
That way you'll have a new empty custaddress for the new action, but respect the values from the request for the create, edit or update actions.

Related

How to access nested objects in strong params

Hello I'm quite new to rails API. I'm having trouble on how can I access the :guest object from the params. What I want to to do is to create a new record in Booking and Guest. Thank you.
Booking Controller
attr_accessor :guest
def create
booking = Booking.new(reservation_params)
booking.guest.build({booking_id: booking.id, first_name: ?, last_name: ?})
end
def reservation_params
params.require(:booking).permit(:start_date, :end_date :guest => [:first_name, :last_name])
end
POST
{
"start_date": "2021-03-12",
"end_date": "2021-03-16",
"guest": {
"first_name": "John",
"last_name": "Doe",
}
}
1. You're assigning a local variable - not an instance variable.
attr_accessor :guest
def create
booking = Booking.new(reservation_params)
end
Here you might assume that since you declared a setter with attr_accessor that this would set the instance variable #booking so that you can access it from the view? Wrong. When performing assignment you need to explicitly set the recipient unless you want to assign a local variable.
attr_accessor :guest
def create
self.booking = Booking.new(reservation_params)
end
But you could actually just write #booking = Booking.new(reservation_params) since that setter is not actually doing anything of note.
2. Models don't have an id until they are saved.
This line:
booking.guest.build({booking_id: booking.id, first_name: ?, last_name: ?})
Is actually equivilent to:
booking.guest.build(booking_id: nil, first_name: ?, last_name: ?)
One big point of assocations is that the ORM takes care of linking the records for you. Let it do its job. If you're ever assigning an id manually in Rails you're most likely doing it wrong.
3. You're not saving anything to the DB
.new (build is just an alias for new) just instanciates an new model instance. You need to actually save the object for it to have any effect beyond the current request.
How do I fix it?
If you want to use that parameter structure it can be done with a bit of slicing and dicing:
def create
#booking = Booking.new(reservation_params.except(:guest)) do |b|
b.guest.new(reservation_params[:guest])
end
if #booking.save
redirect_to #booking
else
render :new
end
end
The reason you use except(:guest) to remove the guest param is that the setter defined by the assocation expects an instance of guest and not a hash so it will blow up otherwise
Nested attributes
accepts_nested_attributes is the Rails way of passing attibutes through another model. It expects the parameter to be named guest_attributes not guest.
class Booking < ApplicationRecord
belongs_to :guest
accepts_nested_attributes_for :guest
end
If you really need to use the existing params structure you can just alter the parameters in your whitelisting method:
class BookingsController < ApplicationController
# ...
def create
#booking = Booking.new(reservation_params)
if #booking.save
redirect_to #booking
else
render :new
end
end
private
def reservation_params
params.require(:booking)
.permit(:start_date, :end_date, guest: [:first_name, :last_name])
.tap do |p|
# replaces the key :guests with :guest_attributes
p.merge!(guest_attributes: p.delete(:guest))
end
end
end
You're trying to create two associated models as once, you can use:
accepts_nested_attributes_for.
In the booking model add:
# models/booking.rb
accepts_nested_attributes_for :guest
And then in the controller:
def reservation_params
params.require(:boking).permit(:start_date,...,
guest_attributes: [:first_name, :last_name])
end
And then you can just create the booking including the guest like so:
booking = Booking.new(reservation_params)
Find more info here:
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Check whether a record exists in controller

While registration, I want to check whether the given email by a new user already exists or not in my controller.
class LoginsController < ApplicationController
skip_before_action :verify_authenticity_token
def index
#subscriber = Subscriber.new()
end
def sign_up
subscriberNew = Subscriber.new
subscriberNew.name = params[:name]
subscriberNew.cus_user_name = params[:user_name]
subscriberNew.cus_password = params[:password]
subscriberNew.cus_email = params[:email]
subscriberNew.mobile_no = params[:phone]
#if Email exists sends and error message
#...................
#if email does not exist, save the response to database
result = subscriberNew.save
respond_to do |format|
msg = {:status => "ok", :message => "Success!"}
format.json {render :json => msg}
end
end
end
How can I do this?
There are multiple ways to validate unique records, one of the better approaches may be altering your database to set a unique index for the email:
add_index :users, :username, unique: true (in your migration)
The DB index approach is better in long terms performance (see this for example)
You can also validate it in your controller before_action:
before_action :validate_email, only: [:sign_up]
...
private
def validate_email
# Or whatever way of sending a message you prefer
flash[:notice] = "A user with this email already exists"
redirect_to root_path if User.where(email: params[:email]).exists?
end
I'd recommend further reading about Active Record validations in the Rails Guides.
Add a validation for the email with uniqueness: true https://guides.rubyonrails.org/active_record_validations.html#uniqueness
You can do something like:
class Subscriber < ApplicationRecord
validates :email, uniqueness: true
end
and on the action:
subscriberNew.valid?
if subscriberNew.errors[:email].present?
#show_error
else
#success
end
I'd really recommend you to read about rails naming conventions, validations using activerecord and also conventions when creating a form (with form_for helper) and Strong Parameters https://guides.rubyonrails.org/action_controller_overview.html#strong-parameters.

Unpermitted parameters on belong_to association

I have an association as follows:
class Membership < ApplicationRecord
belongs_to :user
end
and
class User < ApplicationRecord
has_many :memberships
end
Now I have a form to create a new membership. Within this form I also want to input new user information. I am using SimpleForm. My basic structure is as follows (using HAML not erb files):
= simple_form_for #membership do |f|
...
= simple_fields_for #user do |uf|
.field= uf.input :firstname, label: 'First Name', required: true
...
= f.button :submit, 'Submit'
#user here is an instance variable set in the new action on the Memberships controller (#user = User.new). I would like to keep it this way so I can use SimpleForm's inference on user attributes (i.e., uf.input :firstname maps to the firstname attribute on the User model)
Now given this background, when I hit submit the goal is to create a new membership and a new user associated to that membership. How can I permit parameters for the single associated user?
At the moment I have:
# Never trust parameters from the scary internet, only allow the white list through.
def membership_params
params.require(:membership).permit(users_attributes: [:id, :firstname] )
end
Here is the request:
Started POST "/memberships" for ::1 at 2018-10-15 15:08:42 -0600
Processing by MembershipsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"omitted==", "membership"=>{"user"=>{"firstname"=>""}}, "commit"=>"Submit"}
Unpermitted parameter: :user
Unpermitted parameter: :user
Unpermitted parameter: :user
##################### {}
No template found for MembershipsController#create, rendering head :no_content
Completed 204 No Content in 264ms (ActiveRecord: 0.0ms)
I have no template rendering on purpose for now.
Given this parameter structure:
"membership"=>{"user"=>{"firstname"=>""}}
I have also tried the following for permitted parameters:
params.require(:membership).permit(user_attributes: [:id, :firstname] )
params.require(:membership).permit(user: [:id, :firstname] )
Notice I the ################ {}. This is a manual puts I have in the create action. puts '################ ' + membership_params.to_json. As you can see it yields nothing. Also why do I get the 'Unpermitted parameters' logged three times?
UPDATE 1 controller code:
class MembershipsController < ApplicationController
load_and_authorize_resource
# GET
def new
#membership_plans = Plan.active.sort_by { |plan| plan.sequence }
#user = #membership.build_user
end
# POST
def create
debug_puts(membership_params.to_json)
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def membership_params
params.require(:membership).permit(user_attributes: [:id, :firstname, :lastname] )
end
end
UPDATE 2
I don't deem this as an acceptable answer (which is why I'm not formally "answering" my question), but what I have decided to do is invert my form. The relationship is still the same among Memberships and Users, but the parent form is for a User:
= simple_form_for #user do |f|
...
= f.simple_fields_for :memberships_attributes do |mf|
...
This means I put accepts_nested_attributes_for :membership on the User model (a has_many association with memberships) and all the rendering and param permitting is done within the Users Controller
# new action in users_controller.rb
def new
#membership_plans = Plan.active.sort_by { |plan| plan.sequence }
#user = User.new
#user.build_membership
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(memberships_attributes: [:id, :field1, :field2] )
end
You need to build the user for membership in the new action of memberships_controller.
def new
#membership = Membership.new
#user = #membership.build_user
end
and make sure you have user_attributes not users_attributes in the membership_params
params.require(:membership).permit(user_attributes: [:id, :firstname] )
Update:
There is one more important piece of code which need to be fixed.
This
= simple_fields_for #user do |uf|
should be
= f.simple_fields_for #user do |uf|

Rails4: What is the correct way to use form_for for submitting a hidden field

I am trying to pass a hidden field from a form whose value is derived from a text blob that user can edit on the webpage. (I use bootstrap-editable to let the user edit the blurb by clicking on it)
Here is the actual workflow:
User goes on 'Invitations page' where they are are provided with a form to enter friends email and shown a default text that will be used in the email
If the user want they can click on the text and edit it. This will make a post call via javascript to update_email method in Invitation controller
After the text is updated user is redirected back so now the user sees the same page with updated text. This works and user sees the updated text blurb instead of default [1-3] can happen any number of times
When the user submits the form , I expect to get the final version of email that I can save in the db and also trigger an email invitation to the users friend
Problem:
I keep getting default text from form parameters. Any idea what I am doing wrong?
Here is the form (Its haml instead of html)
#new-form
= form_for #invitation, :url=> invitations_path(), :html => {:class => 'form-inline', :role => 'form'} do |f|
.form-group
= f.text_field :email, :type=> 'email', :placeholder=> 'Invite your friends via email', :class=> 'form-control invitation-email'
= f.hidden_field :mail_text, :value => #invitation_email
= f.submit :class => 'btn btn-primary submit-email', :value => 'Send'
Here is the invitation controller:
class InvitationsController < ApplicationController
authorize_resource
before_filter :load_invitations, only: [:new, :index]
before_filter :new_invitation, only: [:new, :index]
before_filter :default_email, only: [:index]
#helper_method :default_email
def create
Invitation.create!(email: params[:invitation][:email], invited_by: current_user.id, state: 'sent', mail_text: params[:invitation][:mail_text], url: {referrer_name: current_user.name}.to_param)
redirect_to :back
end
def update_email
#invitation_email = params[:value]
flash[:updated_invitation_email] = params[:value]
redirect_to :back
end
private
def invitation_params
params.require(:invitation).permit!
end
def load_invitations
#invitations ||= current_user.sent_invitations
end
def new_invitation
#invitation = Invitation.new
end
def default_email
default_text = "default text"
#invitation_email = flash[:updated_invitation_email].blank? ? default_text : flash[:updated_invitation_email]
end
end
Assuming you are using Rails 4 then you need to permit the mail_text parameter:
class InvitationsController < ApplicationController
# ...
private
def invitation_params
params.require(:invitation).permit(:email, :mail_text) #...
end
end
Depending on your settings rails strong parameters will either raise an error or just silently null un-permitted params.
I have to say that your flow is a bit weird and that it may be better if you actually use a
more RESTful pattern:
1. User goes on 'Invitations page' where they are are provided with a form to enter friends email and shown a default text that will be used in the email
Send a AJAX POST request to /invitations (InvitationsController#create) it should return a JSON representation of the UNSENT invitation, store the returned invitation id on the form.
Note that you may need to setup the validations on your Invitation model so that it allows :email and :mail_text to be blank on creation
class Invitation < ActiveRecord::Base
validates :email, allow_blank: true
# ...
# Do full validation only when mail is being sent.
with_options if: :is_being_sent? do |invitation|
invitation.validates :email #...
invitation.validates :mail_text #...
end
# ...
def is_being_sent?
changed.include?("state") && state == 'sent'
end
end
2. User edits text
Send a AJAX PUT or PATCH request to /invitations/:id and update the invitation.
3. User clicks send
Send a POST request to /invitations/:id/send. Update the state attribute and validate.
If valid send invitation. Display a message to user.
class InvitationsController < ApplicationController
# ...
# POST /invitations/:id/send
def send
#invitation = Invitation.find(params[:id])
# Ensure we have latest values from form and trigger a more stringent validation
#invitation.update(params.merge({ state: :sent })
if #invitation.valid?
#mail = Invitation.send!
if #mail.delivered?
# display success response
else
# display error
end
else # record is invalid
# redirect to edit
end
end
# ...
end

Undefined method include? for Nil:NilClass

Thanks for answering my previous question, but I ran into a new problem.
I'm creating a custom validator that validates whether a user typed in a clean word. This
is used on my UsersController as a validation method.
I am using the Obscenity gem but created some of my own methods to ensure quality data.
Error Message
NoMethodError: Undefined method include? for Nil:NilClass
The problem with this is that my methods work if a record already exists, but they don't work during record creation. I've tried to combat this problem by using
:on => [:create, :update]
but I still receive the same error.
Validation Methods
class MyValidator < ActiveModel::Validator
def mystery_setup
#mystery_words = # This is a mystery, I can't tell you.
#mystery_c = #mystery_words.map(&:capitalize)
#mystery_u = #mystery_words.map(&:upcase)
#mysteries = #mystery_words + #mystery_c + #mystery_u
#new_mysteries = #mysteries.map{|mystery|mystery.tr("A-Za-z", "N-ZA-Mn-za-m")}
end
def validate (user)
mystery_setup
if Obscenity.profane?(user.name) \
|| #new_mysteries.any?{|mystery|user.name.include?(mystery)} \
|| #new_mysteries.any?{|mystery|user.email.include?(mystery)} \
|| #new_mysteries.any?{|mystery|user.password.include?(mystery)}
user.errors[:name] << 'Error: Please select a different username'
end
end
end
User.rb(Model)
class User < ActiveRecord::Base
include ActiveModel::Validations
validates_with MyValidator
has_many :favorites, foreign_key: "user_id", dependent: :destroy
has_many :pictures, through: :favorites
has_secure_password
before_create :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates_presence_of :name, :password, :email
validates_uniqueness_of :name, :email
validates :name, length: { in: 3..20 }
validates :password, length: { minimum: 6 }
validates :email, format: { with: VALID_EMAIL_REGEX }, length: { in: 8..50 }
validates_confirmation_of :password, if: lambda { |m| m.password.present? }
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
I have also tried using an unless statement
def validate (user)
mystery_setup
unless User.all.include?(user)
if (Obscenity.profane?(user.name)
|| #new_mysteries.any {|mystery|user.name.include?(mystery)}) \
|| #new_mysteries.any?{|mystery|user.email.include?(mystery)} \
|| #new_mysteries.any?{|mystery|user.password.include?(mystery)}
user.errors[:name] << 'Error: Please select a different username'
end
end
end
end
I tried testing if there was a user by using the unless statement but that didn't work either.
Following advice from a similar question here, I changed my migrations file to combat this area.
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name, default: 'new'
t.string :password, default: 'new'
t.string :email, default: 'new'
t.timestamps
end
end
end
Question Link
undefined method `include?' for nil:NilClass with partial validation of wizard gem
Reference for Code
http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations
Changing the migration file by changing the default values didn't solve this question so I decided to ask a new question here.
This method works for updating records but not for creating new records.
Help is appreciated. Thank you in advanced.
Edit
Just received an excellent suggestion to pass in the attributes in bracket format. My code now looks like this
def validate (user)
mystery_setup
unless User.all.include?(user)
if (Obscenity.profane?(user[:name]) ||
#new_mysteries.any?{|mystery|user[:name].include?(mystery)}) \
||#new_mysteries.any?{|mystery|user[:email].include?(mystery)}
||#new_mysteries.any?{|mystery|user[:password].include?(mystery)}
user.errors[:name] << 'Error: Please select a different username'
end
end
end
Right now, it only has an error with the email and password attributes. If I delete the last two ||#new_mysteries.any? lines, my method works for filtering the name.
I would like to keep this professional though, so I'd like to get it to work with the other two methods. Possibly has to do with my use of parentheses or the || symbol?
Solid progress guys, keep it up.
Edit
Also, if I would like to call these validation methods on other classes, would it be better to put this in a helper file?
New Update
Here is my Users Controller code
class UsersController < ApplicationController
before_action :signed_in_user, only: [:edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "Congratulations #{#user.name}! You have successfully created an account"
redirect_to games_path
else
render 'new'
end
end
def edit
end
def update
#user = User.find(params[:id])
end
def favorites
#user = User.find(current_user)
end
def destroy
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url notice: "Please sign in."
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
end
You could write that like this:
def validate (user)
mystery_setup
user.errors[:name] << 'Tsk! Tsk! Please select a different username' if
Obscenity.profane?(user[:name]) ||
[:name, :email, :password].product(#new_mysteries).any? { |sym, mystery|
(str = user.public_send sym) && str.include?(mystery) }
end
Thanks to #Arup for the fix.
If you wished to reduce the number of instance variables, you could change the first line to:
new_mysteries = mystery_setup
and change #new_mysteries to new_mysteries.
|| #new_mysteries.any?{|mystery|user.name.include?(mystery)} \
|| #new_mysteries.any?{|mystery|user.email.include?(mystery)} \
|| #new_mysteries.any?{|mystery|user.password.include?(mystery)}
This error means that user name, email or password is nil. To deal with it you need to change each line to:
user.name && user.name.include?(mystery)
However highly recommend andand gem, which will allow you to write the above as:
user.name.andand.include?(mystery)
try this out:
def validate (user)
mystery_setup
unless User.all.include?(user)
if user.name && user.email && user.password
if (Obscenity.profane?(user.name)
|| #new_mysteries.any {|mystery|user.name.include?(mystery)}) \
|| #new_mysteries.any?{|mystery|user.email.include?(mystery)} \
|| #new_mysteries.any?{|mystery|user.password.include?(mystery)}
user.errors[:name] << 'Error: Please select a different username'
end
end
end
end
end
class MyValidator < ActiveModel::Validator
def mystery_setup
mystery_words = # This is a mystery, I can't tell you.
mystery_c = mystery_words.map(&:capitalize)
mystery_u = mystery_words.map(&:upcase)
mysteries = mystery_words + mystery_c + mystery_u
mysteries.map{ |mystery| mystery.tr("A-Za-z", "N-ZA-Mn-za-m")}
end
def validate (user)
# No need to pollute the class with instance variables, just pass it back in a return
new_mysteries = mystery_setup
if Obscenity.profane?(user.name.to_s) ||
#new_mysteries.any?{ |mystery| user.name.to_s.include?(mystery) ||
user.email.to_s.include?(mystery) ||
user.password.to_s.include?(mystery)}
user.errors[:name] << 'Error: Please select a different username'
end
end
end
I have refactored the code a bit, let me know if this works:
def validate (user)
mystery_setup
if Obscenity.profane?(user.name)
user.errors[:name] << 'Error: Please select a different username'
end
%w(name email password).each do |attr|
value = user.send(attr)
if value.present? and #new_mysteries.grep(/#{value}/).present?
user.errors[attr] << "Error: Please select a different user#{attr}"
end
end
end
You have an error in this part, the first line.
#mystery_words = # This is a mystery, I can't tell you.
#mystery_c = mystery_words.map(&:capitalize)
This should be
#mystery_words = [] # This is a mystery, I can't tell you.
#mystery_c = mystery_words.map(&:capitalize)
Since nil is the representation of atomic nothingness in Ruby, it never includes anything. The include? method can be simply defined on it as:
def nil.include? arg
false
end

Resources