I need some class or singleton object globally-accessible in controllers and easy to use. Now it is implemented in the libs/ folder like this:
class User
class << self
#user = nil
attr_reader :uid, :name
def init session
if session[:user_info].nil?
#user = nil
end
#user = session_data[:user]
end
def signed_in?
#user.nil? ? false : true
end
def guest?
not signed_in?
end
end
end
This code obviously is not good, as User initialized only once on application start, and in case of improper use User wouldn't be updated. I want to save ability to use the class or object without much addition steps, but have a new global instance for every new connection. How it should be done?
It looks like you're trying to create a standard "current user" method. I think you're complicating things a bit. All you need to do is load a user object based on session information and cache it in an instance variable. Something like this:
class ApplicationController < ActionController::Base
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
The first time you call it, it will look up the current user based on the ID stored in the session. Subsequent calls will return the user object you already loaded. If the user is not signed in, current_user will simply be nil.
You can add include Singleton to your User class definition and then use User.instance go get the user instance.
Place the code in Application Controller as this is the base class of all the classes. Doing so it will be globally-accessible in all the controllers as well.
Related
Using SimpleDelegator, I created a few decorators to add extra functionalities to my objects. I need to decorate an object twice, as below:
Tracked.new(Audited.new(User.new))).save
Here is the basic structure of the decorator(s):
class Tracked #or Audited
delegate :id, to: :__getobj__
def initialize(extened_object)
super(extened_object)
#extened_object = extened_object
end
def itself
__getobj__
end
def save
super
# the extended behavior
end
end
I want to access the class User from the object. In Audited, if I call #extended_object.class with Audited.new(User.new)).save, I get User. In Tracked, if I call #extended_object.class with Tracked(Audited.new(User.new))).save, I get Audited instead.
How can I get the Class of the extended_object regardless of the number of times I decorate it?
I don't think you can do this with SimpleDelegator.
You need to implement this method yourself.
For example:
class MyDelegator < SimpleDelegator
def original_object
obj = __getobj__
obj.is_a?(MyDecorator) ? obj.original_object : obj
end
end
And all of your decorators should be inherited from MyDelegator
I have a class called User and a few classes for user roles (e.g. Admin, Contributor, Member) that inherit form the User class:
class Admin < User; end
Following the STI principle, roles are stored as jsonb in the users table like so:
t.jsonb :roles, default: {
'admin': false,
'contributor': false,
'member': true
}
Is it possible to set the appropriate subclass to an instance of a User for the corresponding role. In other words, do something like:
u = User.create(...)
u.class #<= returns 'User'
u.grant_role(admin)
u.class #<= should return 'Admin'
I am thinking of using a before_save callback so that each time User instance is instantiated or updated, an appropriate class is selected. Am I on the right track?
I don't think it is possible to change the type of an instance on the fly, but I'm not 100% sure about this, considering that we're talking about Ruby here. :)
Rather than STI, which is changing the class of User, how about using the strategy pattern?
Have a set RoleStrategy classes which get instantiated depending on the value of role.
For your case, it could be something like this:
class User
# :foo and :bar are role dependent behaviors that you want to have.
delegate :foo, :bar, to: :role_strategy
private
def role_strategy
# Need to do some error handlings here, for example, if role is nil.
"#{role.classify}RoleStrategy".constantize.new(self)
end
end
class RoleStrategy
def initialize(user)
#user = user
end
def foo
do_it
end
def bar
do_something
end
end
class AdminRoleStrategy < RoleStrategy
def bar
do_something_else
end
end
The behavior would automatically change depending on what the value of role is.
I make an API call:
def set_youtube(user)
Youtube.get_subscribers(user)
Youtube.get_views(user)
end
Here's my service object:
class Youtube
class << self
def get_hash(user)
## code to return a youtube JSON hash containing subscribers and views
end
def get_subscribers(user)
youtube_hash = Youtube.get_hash(user)
## code to return a subscriber count
end
def get_views(user)
youtube_hash = Youtube.get_hash(user)
## code to return a view count
end
end
end
However, I find it more elegant to call the method directly on the user. I don't want to make two calls to the API to get subscribers and then get views. But I also don't want to do:
youtube_hash = Youtube.get_hash(user)
Youtube.get_subscribers(youtube_hash)
Youtube.get_views(youtube_hash)
I want to temporarily cache the variable in the instance of this object so that I can use it for both class methods. What's the correct way to handle this?
You could use class variables (prefixed with ## symbols) and cache the hash, however you will then have to maintain that and it could get messy, instead I suggest using a more OO approach?
You could make it an instance of "Youtube", and cache the hash
class Youtube
def initialize(user)
#user = user
end
def hash
#hash ||= ... #the logic used to get the user hash in your get_hash using the #user instance variable
end
def subscribers
#subscribers ||= ... #the logic used to get the user subscribers in your get_subscribers however using the hash getter method which in turn uses the #hash instance variable
end
def views
#views ||= ... #the logic used to get the user views in your get_views however using the hash getter method which in turn uses the #hash instance variable
end
end
Then you can do the following and it will use the cached hash:
yt = Youtube.new(user: user)
yt.views
yt.subscribers
Maybe "YoutubeUser" is a better name? Just a suggestion. This all also could be moved to the User model and just use the Youtube service object. For example:
class User
before_create :set_youtube
def set_youtube
youtube = Youtube.new(self)
self.youtube_subscribers = youtube.subscribers
self.youtube_views = youtube.views
end
end
I assume set_youtube is an instance method, therefore no need to pass the user, however the class method would be similar as well.
First, for the short version:
Isn't a method definition just a block? Why can't I do something like:
obj.instance_exec(&other_obj.method(:my_method))
with the goal of running some module method in the context of an instance of a separate class? The method is called, but it doesn't seem to be executed in the context of 'obj', despite the 'instance_exec' call.
The only way I can figure out how to accomplish this is to wrap all of the code of 'my_method' in a proc, then call in the following manner instead:
obj.instance_eval(&other_obj.my_method)
but I'd like to avoid encapsulating all of my module methods in procs.
Now, for the long version:
I'm attempting to create a modularized external provider system, where for any given class/method (generally controller methods,) I can call a corresponding method for a given provider (e.g. facebook).
Since there could be multiple providers, the provider methods need to be namespaced, but instead of simply including a bunch of methods like, for example, 'facebook_invitation_create', I'd like my InvitationsController instance to have a facebook member containing a create method - e.g.
class InvitationsController < ApplicationController
def create
...
# e.g. self.facebook.create
self.send(params[:provider]).create
...
end
end
Furthermore, I'd like the provider methods to not only function as if they were part of the controller itself - meaning they should have access to things like controller instance variables, params, session, etc. - but also to be (mostly) written as if they were part of the controller itself - meaning without any complex additional code as a result of being modularized.
I've created a simplified example below, in which MyClass has a greet method, which if called with a valid provider name (:facebook in this case), will call that providers greet method instead. In turn, the provider greet method accesses the message method of the including class, as if it were part of the class itself.
module Providers
def facebook
#facebook ||= FacebookProvider
end
module FacebookProvider
class << self
def greet
proc {
"#{message} from facebook!"
}
end
end
end
end
class MyClass
include Providers
attr_accessor :message
def initialize(message="hello")
self.message = message
end
def greet(provider=nil)
(provider.nil? or !self.respond_to?(provider)) ? message : instance_exec(&self.send(provider).greet)
end
end
This actually accomplishes almost everything I've previously stated, but I'm hung up on the fact that my provider functions need to be encapsulated in procs. I thought maybe I could simply call instance_exec on the method instead (after removing the proc encapsulation):
instance_exec(&self.send(provider).method(:greet))
...but then it seems like the instance_exec is ignored, as I get the error:
NameError: undefined local variable or method `message' for Providers::FacebookProvider:Module
Is there any way to call instance_exec on a defined method?
(I'm open to suggestions on how to better implement this as well...)
I think this is simpler than you might expect (and I realize that my answer is 2 years after you asked)
You can use instance methods from modules and bind them to any object.
module Providers
def facebook
#facebook ||= FacebookProvider
end
module FacebookProvider
def greet
"#{message} from facebook!"
end
end
end
class MyClass
include Providers
attr_accessor :message
def initialize(message="hello")
self.message = message
end
def greet(provider=nil)
if provider
provider.instance_method(:greet).bind(self).call
else
message
end
end
end
If your provider is a module, you can user instance_method to create an UnboundMethod and bind it to the current self.
This is delegation.
It's the basis for the casting gem which would work like this:
delegate(:greet, provider)
Or, if you opt-in to using method_missing from casting, your code could just look like this:
greet
But you'd need to set your delegate first:
class MyClass
include Providers
include Casting::Client
delegate_missing_methods
attr_accessor :message
def initialize(message="hello", provider=facebook)
cast_as(provider)
self.message = message
end
end
MyClass.new.greet # => "hello from facebook!"
I wrote about what delegation is and is not on my blog which is relevant to understanding DCI and what I wrote about in Clean Ruby
Maybe I'm not following along, but it seems like you are making this harder than it needs to be.
Why not implement a "dispatch" pattern in your class, where you have a hash of provider names and provider methods {:facebook=>"facebook_greet"} and then just "send" the incoming call to the correct handler via "Object#send" (http://ruby-doc.org/core-1.9.3/Object.html#method-i-send)? Send is very fast for dispatching methods, so unlike eval, you should get great performance.
Here's some code to demonstrate the way I'd solve it (assuming I am following along with what you're trying to accomplish):
module TwitterProvider
def providerInit(providers)
#providers[:twitter]="twitter_greet"
super(providers) if defined?(super)
end
def twitter_greet
"Hello Twitter User"
end
end
module FacebookProvider
def providerInit(providers)
providers[:facebook]="facebook_greet"
super(providers) if defined?(super)
end
def facebook_greet
"Hello Facebook User"
end
end
class MyClass
include FacebookProvider
include TwitterProvider
attr_accessor :message
def providerInit(providers)
super(providers) if defined?(super)
end
def initialize(message="hello")
#providers = {}
self.message = message
providerInit(#providers)
end
def greet(provider=nil)
if provider.nil? or !self.respond_to?(#providers[provider])
self.message
else
self.send(#providers[provider])
end
end
end
my_class = MyClass.new
puts my_class.greet
puts my_class.greet(:twitter)
puts my_class.greet(:facebook)
# Output:
# hello
# Hello Twitter User
# Hello Facebook User
I have a class, with some fake relationships I want to implement:
module FormStack
class Connection
def forms; end
def fields; end
end
end
I have metaprogramically generated classes for both forms, and fields (as they are RESTful resources, they share the same action names and params), and I want to include those methods in my fake relationships in my FormStack::Connection class. can this be done?
I essentially want <FromStack::Connection Instance>.forms to behave as if it were FormStack::Form, so I can do things like <connection>.forms.all or <connection>.forms.find(id).
Is this possible?
Any best practices I should maybe be looking at? (This seems a little strange to me, but I think it's an elegant way to have the methods implemented in a useful way, while still having an ActiveRecord-esque abstraction of the restful resources / objects).
Here is the code I'm working with, if you want to look: https://github.com/TinderBox/formstack/tree/connection_instances
Why not just use simple composition? Pass whatever object has the has_many FormStack::Form relation in when you initialize a new FormStack::Connection instance. Then you can directly invoke the #forms method on the FormStack::Form collection instance, or you can use delegation.
FormStack::Connection.new(FormStack::FormCollection.new(params[:form]) #sample class name -- obviously use whatever has the real has_many :forms
module FormStack
class Connection
def initialize(form_collection)
#form_collection = form_collection
end
def forms
#form_collection.forms
end
def fields
#form_collection.fields
end
end
end
Or
module FormStack
class Connection
extend Forwardable
def_delegators :#form_collection, :forms, :fields
def initialize(form_collection)
#form_collection = form_collection
end
end
end
Unless there is a better way, this is how I've solved my problem for now:
def method_missing(meth, *args, &block)
method_name = meth.to_s
if "forms" == method_name
FormStack::Form.connection = self
FormStack::Form
elsif ...
else
super
end
end
https://github.com/TinderBox/formstack/blob/082793bed97e97cc65c703c8ca3cb382cbdf743a/lib/formstack/connection.rb