I am using Rails Warden plugin. It defines a helper method 'user' that returns current user. See the link for the source code.
Now I have an business logic object that does not have any reference to the controller. But I would like to get the current user. Is there any way of accessing this?
I have tried
ActionController::Base.helpers.user
or even
RailsWarden::Mixins::HelperMethods.user
with no luck. Thanks.
Now I have an business logic object
that does not have any reference to
the controller. But I would like to
get the current user. Is there any way
of accessing this?
So why can't you just pass the current user to those methods?
Additionally you can mix them in.
I strongly discourage you to write the static helpers (it is not Java, it is Ruby!).
Instead, where you need those helpers include them as a module:
module SuperLogic
def calculate_stuff(current_user=nil)
(current_user || user || self).bills.sum
end
edn
Then include this where you need it:
# user model
class User
include SuperLogic
#it will get the `calculate_stuff` method
end
# controller
include SuperLogic
# so you can use it as one of
calculate_stuff user
calculate_stuff
and so on...
additionally where you access your business logic, you can just create an instance of the class instead of "static" methods (in ruby they are "class" methods):
# controller
def calculate
#result = BusinessLogic.new(user).calculate_stuff
end
This is probably the easiest thing you can do.
Really, you don't need to access whole HTTP context in your business objects (I'm not even talking about testing it).
The way I think of business logic, it's something that sits between the controller and the model. I think it would be ok to pass an instance of the request to the logic methods, and since you're using warden, you can get the user from 'request.env['warden'].user'.
I haven't encountered a good reason not to have logic methods be static (self.) methods of a module. Maybe Dmytrii's suggestion works for you, but I prefer to 'require' than to dynamically include one-off logic bits.
Related
module DummyModule
extend ActiveSupport::Concern
included do
class DummyClass
#I would like to use current_user and cookies here without initialising the class with any arguments!
end
end
end
I know it is possible by initialising this class with arguments outside of the class DummyClass.new(current_user, cookies) but is there any other prettier way to do it?
There is no magic way to do that.
session and current_user are methods that are only available in the context of a controller. When you want to use them in a model then you have to pass them to the model.
Or you have to assign them to something that is available in the context of the model. Thread.current or CurrentAttributes might be an option – as Josh already pointed out. But keep in mind these both ways have downsides too.
The clearest and safest way is to just pass them to the model in its initialize method.
I'm learning how to use Sinatra. I figured out that when I pass object as locals, e.g.:
product = FetchProduct.new.call(id) #function finds exact Product instance
erb :"products/show", locals: { product: product }
I can use product object in my views with all instance methods I declared. But I can't use any class method, any attempt to do so gives me uninitialized constant error. What should I do if I want to use Product.format_price(product.price) method? Is there any way to pass class methods to Sinatra views?
klass = const_get( product.class )
klass.format_price
But that doesn't really make sense because you already know you want Product.format_price. So why don't you use Product.format_price?
It's generally a bad idea to run that kind of logic in your views. Best practice is to, wherever possible, serve to the view whatever it needs.
NB the reason you can't run the class method in your view is because Product is not accessible in your view and, to be honest, it shouldn't be if you want to follow MVC principles.
If it's just the format_price method you need in the view (especially since you seem to be passing an instance of Product into Product.format_price which is rather strange and a big code smell), then either create a helper method called format_price that is accessible by the view or, probably better, create a helper method called format_price in your controller (or in a helper module included in your controller) and pass the return value as a local i.e.
get '/' do
product = FetchProduct.new.call(id)
erb :'products/show', locals: {
product: product,
price: format_price(product)
}
end
private
def format_price(product)
# awesome formatting logic
end
I am trying to use Grape to create an API using only Ruby objects. I do not want to use a database/Rails/ActiveSupport/etc -- just Rack, Ruby, and Grape.
I have defined a class for a Directory that I want to interact with through the API. So Directory#sort_by("last_name") returns JSON data with a list of People objects in my Directory. I also have a method Directory#create_person(attributes) that takes a string and uses it to add Person objects to the Directory. The directory is populated with people upon creation.
I am new to working with Rack and Grape, so I'm not sure where/how to create my Directory object and have it accessible through the GETs/POSTs in my Grape API class. Using a class variable inside this class appears to work, i.e.,:
module API
class DirectoryAPI < Grape::API
format 'json'
##directory = Directory.new("1.txt", "2.txt", "3.txt")
get 'last_name' do
##directory.sort_by("last_name")
end
end
end
but using class variables just seems wrong. Is there any better/cleaner way to create my Directory object? Perhaps inside my config.ru file? Or could I do it through a class method inside of Directory somehow?
What you are looking for is a singleton:
Usually singletons are used for centralized management of internal or external resources and they provide a global point of access to themselves.
Unfortunately, Ruby just doesn't play well with singletons. But you can use a "class consisting of only class methods," the second strategy advocated in this article.
I believe that you are working on a coding challenge that I completed a few months ago. In my answer, I used a "class consisting of only class methods" called API::Store. Here's the output from rspec -fd:
API::Store
::add
adds record to store
appends data line to file
::has_line?
instantiates a record from the data line
without the record in the store
should equal false
with the record in the store
should equal true
::new
should raise NoMethodError
::records
with original file
on initial access
should eq Records
on subsequent access
should eq Records
when file replaced
should eq OtherRecords
Finished in 0.07199 seconds (files took 2.68 seconds to load)
9 examples, 0 failures
Note that Store can't be instantiated; it throws a NoMethodError if you try. That's not a problem, though. In the Grape endpoint you can call Store.records to access the data.
As for sorting the records, this should be done in another class. Why should a Store or a Directory be sorting the data in its files?
Finally, you asked where to do the initial preparation (not initialization, of course). You can prepare your singleton in config.ru, so that it is ready when the application starts:
# config.ru
# load application code here
file = File.open('data/records.txt', 'a+')
at_exit { file.close }
API::Store.file = file
run API::Base
The challenge's instructions say "You may use any resources you need to complete it," so presumably, asking on Stack Overflow is allowed. If you are doing this challenge for a job application, please do mention so when you ask questions, because it's only fair for those answering to be informed. It would be wise to also mention at your interview that you got help on SO. Good luck, and happy coding.
The main problem I see with your example is not the use of class variables exactly, but instantiating your data inline in the API controller code. Ideally the data should be more self-contained, so you can access the exact same data from other places in your code. If you make an API similar to a light-weight data access module, then you will be using a familiar pattern in your route controllers - also it will become easy to migrate to using SQL or other data store if and when you need to.
There are lots of valid approaches, but I might create a new singleton object to represent your data source, and connect that to your hard-coded data as if it were tables. The end result here would feel a little like using Sequel (but you could follow any other pattern that you prefer):
inline_data.rb
module InlineDB
TABLES = Hash[
:directory => Directory.new("1.txt", "2.txt", "3.txt")
]
def self.[] table_name
TABLES[table_name]
end
end
app.rb
require_relative 'inline_data' # actual path may be more structured
module API
class DirectoryAPI < Grape::API
format 'json'
get 'last_name' do
InlineDB[:directory].sort_by("last_name")
end
end
end
Say I have a user model. It has an instance method called status. Status is not an association. It doesn't follow any active record pattern because it's a database already in production.
class User < ActiveRecord::Base
def status
Connection.where(machine_user_id: self.id).last
end
end
So I do this.
#users = User.all
First of all I can't eager load the status method.
#users.includes(:status).load
Second of all I can't cache that method within the array of users.
Rails.cache.write("user", #users)
The status method never gets called until the view layer it seems like.
What is the recommended way of caching this method.
Maybe this instance method is not what I want to do. I've looked at scope but it doesn't look like what I want to do.
Maybe I just need an association? Then I get the includes and I can cache.
But can associations handle complex logic. In this case the instance method is a simple query. What if I have complex logic in that instance method?
Thanks for any help.
Have You tried to encapsulate this logic inside some plain Ruby object like this (I wouldn't use this for very large sets though):
class UserStatuses
def self.users_and_statuses
Rails.cache.fetch "users_statuses", :expires_in => 30.minutes do
User.all.inject({}) {|hsh, u| hsh[u.id] = u.status; hsh }
end
end
end
After that You can use some helper method to access cached version
class User < ActiverRecord::Base
def cached_status
UserStatuses.users_and_statuses[id]
end
end
It doesn't solve Your eager loading problem, Rails doesn't have any cache warming up techniques built in. But by extracting like this, it's easily done by running rake task in Cron.
Also in this case I don't see any problems with using association. Rails associations allows You to submit different options including foreign and primary keys.
I am currently building a Thor executable that supports several different tasks. Some tasks inoke other tasks. For example
Auth
authenticates the local user
Create
Invoke Auth
Does something
Invoke Configure
Configure:
Invoke Auth
Does something
The nice thing about thor is that I can now call all of the three tasks separately, but be sure that authenticate for example is only called once through invoke.
But what is now the best way to share Data between those tasks. For example when Authenticating I want to set the User-ID of the current user. Thus when I want to request Data from our webservice I can take the user id from anywhere in my code the same way.
Right now I am implementing this through Modules, but I am not really sure if this is the best way to do it.
Using Singleton would be another way, but somehow this seems like a little bit too much for this purpose.
Maybe you guys have experience with a better way to do this, or maybe using modules is the best way to store the information anyway.
I would have done something like this:
module PRJ
class << self
attr_accessor :api_key, :account_name, :api_version, :api_url, ......
def configure
yield self
end
end
end
require "#{directory_of_all_other_classes}"
require 'thor'
class CLI < Thor
include Thor::Actions
def login
...
PRJ.api_key = response[:api_key]
end
As you want to invoke Auth in every cases, so I think it should be in main CLI file.
Regarding storing and using data it would be better to use attributes instead of constant. I may not be right about this but I prefer using class attributes.
Now you can access and change PRJ.api_key from anywhere (require PRJ module in every file)
P.S. The structure of code snippet might not be accurate, I just wanted to share some basic skeleton. Do something like above for remaining classes.
If you just need to store simple data structures I would go for common class variables
class Test < Thor
##my_shared_var = nil
desc 'my_first_task','sdfasdf'
def my_first_task
##my_shared_var = 'foo'
end
desc 'my_second_task','sdfasdf'
def my_second_task
invoke :my_first_task
puts ##my_shared_var
end
end