I am building a gem in which i have a module OtpGenerator. inside this module i have methods like generate_otp, verify_otp etc. This is just a begining for me and its very simple gem only to generate and verify and save and send, nothing else. Now anyone who uses this gem will have to include this module in their model. for e.g. there is a user model. now what i want is first i will create a instance
user = User.new(params[:user])
now i need to do the operation user.generate_otp, this will assign otp related things in the activerecord instance.
after that user.save, which is also fine.
But i also want a function generate_otp!, which will do all task like generates otp, than save it and sends it. My problem is that i am not getting how to achieve this functionality.
Note: I am very new to ruby. and really getting confused with mixins.
here is my code for otp.rb file
require 'securerandom'
module OtpGenerator
def generate_otp
#do something here
end
def verify_otp(otp)
#do something here
end
def regenerate_otp
#do something here
end
def matches?(generated, otp)
#do something here
end
def expired?(otp_expiry_time)
#do something here
end
end
This code is still in development, i just want to know that how to implement generate_otp! function, which will do all three operation,i.e,
(1) generates otp(user.generate_otp)
(2) saves otp(user.save)
(3) sends otp (i have created the send function, so thats not a problem.)
If this is a mixin in your model, then your model should also have access to it. Here is what I mean:
class User
include OtpGenerator
end
module OtpGenerator
...
def generate_otp!
generate_otp
save
send_generated_otp
end
end
When you call User.find(45).generate_otp!
That would work because of the way inheritances work in Ruby. Once the module is included within a class, it inherits all the methods of the module and the module has access to the context of the included class.
Hope that answers your question
Related
Is there a way to have a model such that only code within the same module can access it?
Something like:
module SomeModule
class SomeActiveRecordModel
# has attribute `some_attribute`
...
end
end
module SomeModule
class SomeOtherClass
def self.sum_of_attribute
SomeActiveRecordModel.sum(:some_attribute)
end
end
end
class OutsideOfModule
def self.sum_of_attribute
SomeModule::SomeActiveRecordModel.sum(:some_attribute)
end
end
SomeModule::SomeOtherClass.sum_of_attribute # works
OutsideOfModule.sum_of_attribute # raises error
Short answer is no. Here's why
Ideally, you want to implement this in your SomeModule. But when you call SomeModule::SomeOtherClass.sum_of_attribute in other classes, you are in a scope of SomeModule::SomeOtherClass.
SomeModule::SomeActiveRecordModel.sum(:some_attribute)
||
\/
module SomeModule
class SomeActiveRecordModel
def sum(*args)
# Here, self => SomeModule::SomeActiveRecordModel
# That's why you won't be able to do any meta trick to the module
# or classes in the module to identify if it's being invoked outside
end
end
end
So you wouldn't know who the original caller is.
You might be able to dig through the call stack to do that. Here's another SO thread you might find helpful if you want to go down that path.
In short, no. But this is more a question of Ruby's approach and philosophy. There are other ways of thinking about the code that allow you achieve something similar to what you're looking for, in a more Rubyesque way.
This answer covers the different ways of making things private.
I wrote ruby code which pulls content from Google API. It works as a standalone example.rb file. I need to add this to my RoR app. What is the standard way to do it? How should I call this code from the controller? Should I add this code in some model file, keep the code in /lib folder, or put the code in /vendor/plugins folder?
Either extract it out into a gem, or you could put it in lib if you wanted.
If you take the second approach, here's an example. Say you have it in a module (Google)
#lib/google.rb
module Google
class Uploader
def initialize
...
end
def foo
...
end
end
...
end
in your controller
require 'google'
class MyController < ApplicationController
def new
uploader = Google::Uploader.new # do whatever here
uploader.foo
end
end
There are many ways to modify / use this module approach, the given code is only one possibility.
I'm writing a script for my rails application and I'm trying to format the numbers with delimeters so they're easier to read. But I have a problem in calling the number_with_delimeter method from ActionView::Helpers::NumberHelpers
I tried
class MyClass < ActiveRecord::base
extend ActiveView::Helpers::NumberHelper
def self.run
puts "#{number_with_delimeter(1234567)}"
end
end
MyClass.run
but it just doesn't work. I always get undefined method errors. I tried it with include instead of extend and some other variations. None of them worked. I don't know how to proceed.
Is there any way to call this method in a script?
*Note: * I call the script with rails r script/my_script.rb
An elegant solution consists in delegation:
def self.run
puts "#{helper.number_with_delimiter(1234567)}"
end
def self.helper
Helper.instance
end
class Helper
include Singleton
include ActionView::Helpers::NumberHelper
end
Sidenotes:
including modules overloads your class
including the helpers didn't help because you were working at the class level.
formatting should not be model's job, you should extract this kind of logic within presenters.
I have a gem I'm developing that is based around using filters on ApplicationController. It's basically for logging, and one of the modules defines an around filter like so:
module LogExceptionFilter
self.included(base)
base.around_filter :do_a_bunch_of_logging_stuff
end
def do_a_bunch_of_logging_stuff
...
end
end
It happens to be an around filter where I deal with exception logging, but my question would apply for any filter.
So it's supposed to be used like this
class ApplicationController
include LogExceptionFilter
end
So what I'm worried about is if someone does:
class ApplicationController
include LogExceptionFilter
include LogExceptionFilter
end
I don't want to execute do_a_bunch_of_logging_stuff twice. So first
1)If do_a_bunch_of_logging_stuff is included twice, will rails apply the filter twice?
2)Is it my responsibility to protect the user from doing this? I could do so with a class variable, something like:
module LogExceptionFilter
class << self
cattr_accessor :filter_loaded
end
self.included(base)
unless filter_loaded
base.around_filter :do_a_bunch_of_logging_stuff
filter_loaded = true
end
end
def do_a_bunch_of_logging_stuff
...
end
end
This variable is not thread safe so it's something that I'd want to be careful about putting in. But I don't want to write a library that can be easily broken. Thanks.
Here are some relevant links:
http://www.ruby-forum.com/topic/95269
http://www.ruby-forum.com/topic/164588
Basically, a module will only be included once, but the included callback may be called multiple times.
Which would be the most elegant way to define static methods such as "generate_random_string", "generate_random_user_agent", which are called from different libraries?
What are the best practices?
Best practice as I've seen would include:
Put them in a module in /lib/
Include them as mixins in the rest of your application code.
Make sure they are thoroughly tested with their own rspecs (or whatever test tool you user).
Plan them as if you may at some point want to separate them out into their own gem, or potentially make them available as a service at some point. That doesn't mean design them as separate services from the beginning, but definitely make sure they have no dependencies on any other code in your application.
Some basic code might be something like:
module App::Services
def generate_random_string
# ...
end
def generate_random_user_agent
# ...
end
end
Then in your model or controller code (or wherever), you could include them like this:
class MyModelClass < ActiveRecord::Base
include App::Services
def do_something_here
foo = random_string
# whatever...
end
def random_string
generate_random_string
end
end
Notice I isolated the generate_random_string call in its own method so it can be used in the model class, but potentially be switched out for some other method easily. (This may be a step more than you want to go.)