I'm trying to create a validator that validates whether a user typed in a clean word. I'm using the Obscenity gem but created some of my own methods to ensure quality data.
class MyValidator < ActiveModel::Validator
#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")}
def validate (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
class User < ActiveRecord::Base
include ActiveModel::Validations
validates_with MyValidator
Error Message
NoMethodError: undefined method any? for nil:NilClass
I don't understand why I received that message.
You have defined #new_mysteries on the class, rather than on the instance.
Consider:
class MyValidator
#new_mysteries = ['a','b','c']
def validate
p #new_mysteries
end
end
MyValidator.new.validate
# => nil
Inside the validate method, # variables refer to variables on the instance (an instance created by new). Outside of a method definition, # variables refer to instances of the class itself. Hence the disconnect.
I would probably declare #new_mysteries inside the constructor:
class MyValidator
def initialize
#new_mysteries = ['a','b','c']
end
def validate
p #new_mysteries
end
end
MyValidator.new.validate
# => ["a", "b", "c"]
Further reading: http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
There is a issue of variable scope in your code. Try following:
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
Related
I am trying to access variable in ruby after initialize, but i didn't get that variable , anything wrong in that?
class Test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
def self.method1(params)
Test.new(params)
#can i get that two instance variable
end
end
You should probably set up attribute accessors, then use them this way:
class Test
attr_accessor :has_test
attr_accessor :limit_test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
def self.method1(params)
t = Test.new(params)
// can i get that two instance variable
// Yes:
// use t.has_test and t.limit_test
end
end
You are mixing an instance and a class method in your example.
If this is really what you want, then you have to define an accessor with attr_reader:
class Test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
attr_reader :has_test
attr_reader :limit_test
def self.method1(params)
obj = Test.new(params)
p obj.has_test
p obj.limit_test
end
end
Test.method1(has_test: 1, limit_test: 3)
It the instance/class-method is a mistake, then this example may help you:
class Test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
def method1()
p #has_test
p #limit_test
end
end
obj = Test.new(has_test: 1, limit_test: 3)
obj.method1
If you define also the accessors like in the first code, then you have again access from outside the class.
Just in case you don't want a reader, see also Access instance variable from outside the class
This works perfectly fine:
User.first.social_profiles.create!
On the other hand, this creates the social_profile but does not create the association between the two:
class SocialProfile < ActiveRecord::Base
def self.create_google( auth_info )
# if where(provider: auth_info["provider"], uid: auth_info["uid"]).empty?
create! do |google|
google.provider = auth_info["provider"]
google.uid = auth_info["uid"]
google.image_url = auth_info["info"]["image"]
google.email = auth_info["info"]["email"]
google.access_key = auth_info["credentials"]["token"]
google.refresh_token = auth_info["credentials"]["refresh_token"]
google.expires_at = Time.at(auth_info["credentials"]["expires_at"])
google.expires = auth_info["credentials"]["expires"]
end
# else
# where(provider: auth_info[:provider], uid: auth_info[:uid]).first
# end
end
end
Console:
2.1.2 :102 > User.first.social_profiles.create_google( ...the auth hash ...)
What's the problem here? How can I fix it?
This does work though
p = User.first.social_profiles.create_google(...the auth hash ...)
User.first.social_profiles << p
The User.first instance does not get carried into the SocialProfile.create_google method, and therefore the create! method wouldn't have the user instance available.
You can assign it yourself by passing it in:
class SocialProfile < ActiveRecord::Base
def self.create_google( user, auth_info )
create! do |google|
google.user_id = user.id,
...
end
end
end
And calling it with
SocialProfile.create_google( User.first, auth_info )
Alternatively, consider having the create_google_profile method in User, so that you can
class User < ActiveRecord::Base
def create_google_profile( auth_info )
self.social_profiles.create(
provider: auth_info["provider"],
...
)
end
end
and calling it with
User.first.create_google_profile( auth_info )
I have a model with a class method.
Like:
class School < ActiveRecord::Base
def self.mymethod
end
def instance_something
end
end
How can I find the source_location of a class method in Ruby?
If I want the source location of "instance_something" I do
School.new.method(:instance_something).source_location
But I can't do that with class method.
Any help?
I just did this in irb, and it works.
class Foo
def foo
end
def self.bar
end
end
f = Foo.new
m1 = f.method(:foo)
m1.source_location
=> ["(irb)", 2]
Foo.method(:bar)
m2 = Foo.method(:bar)
m2.source_location
=> ["(irb)", 4]
Why does the following code result in the error 'undefined local variable or method `foo_client' for Foo::People:Class'
class Foo::People
class << self
def get_account_balance(account_num)
foo_client.request :get_account_balance, :body => {"AccountNum" => account_num}
end
end
def foo_client
##client ||= Savon::Client.new do|wsdl, http|
wsdl.document = PEOPLE_SERVICE_ENDPOINT[:uri] + "?WSDL"
wsdl.endpoint = PEOPLE_SERVICE_ENDPOINT[:uri]
end
end
end
def get_account_balance is inside the class << self block, so it's a class method. def foo_client is not, so it's an instance method. So you can't call foo_client from get_account_balance because you're not calling it on an instance of People.
so this is what i want to do:
class A
ATTRS = []
def list_attrs
puts ATTRS.inspect
end
end
class B < A
ATTRS = [1,2]
end
a = A.new
b = B.new
a.list_attrs
b.list_attrs
i want to create a base class with a method that plays with the ATTRS attribute of the class. in each inherited class there will be a different ATTRS array
so when i call a.list_attrs it should print an empty array and if i call b.attrs should put [1,2].
how can this be done in ruby / ruby on rails?
It is typically done with methods:
class A
def attrs
[]
end
def list_attrs
puts attrs.inspect
end
end
class B < A
def attrs
[1,2]
end
end
modf's answer works... here's another way with variables. (ATTRS is a constant in your example)
class A
def initialize
#attributes = []
end
def list_attrs
puts #attributes.inspect
end
end
class B < A
def initialize
#attributes = [1,2]
end
end
I don't think it's a good idea to create the same array each time a method is called. It's more natural to use class instance variables.
class A
def list_attrs; p self.class.attrs end
end
class << A
attr_accessor :attrs
end
class A
#attrs = []
end
class B < A
#attrs = [1, 2]
end
A.new.list_attrs # => []
B.new.list_attrs # => [1, 2]
You can also use constants along the line suggested in the question:
class A
def list_attrs; p self.class.const_get :ATTRS end
ATTRS = []
end
class B < A
ATTRS = [1, 2]
end
A.new.list_attrs # => []
B.new.list_attrs # => [1, 2]