Ruby on Rails Tutorial by Michael Hartl, Chapter 8, "Current User" section - railstutorial.org

I'm new to Rails and have been going through the Rails Tutorial by Michael Hartl. Up until this point, I've been able to understand everything in the tutorial but now I think I'm stuck midway through Chapter 8. I'd like some confirmation from more experienced Rails programmers, that I'm following the author's explanation correctly. I'm talking specifically about Chapter 8, Section 8.2.3 below:
In Section 8.2.3, the "sign_in" method has the line:
self.current_user = user
So by using "self" instead of just "current_user", current_user has been made part of the Session, but not yet stored in the database, correct?
If that's the case, the methods listed in Listing 8.22 are getter and setter methods, correct? The first to store current_user in the DB, and the second to retrieve it as needed?
And finally, the getter and setter methods are needed because its bad practice to expose an instance variable in a method (in this case "sign_in")? In other words, it would be bad to replace:
self.current_user = user
with
#current_user = user
in the "sign_in" method?
I appreciate any help and advice.
Thank you.

I am not highly proficient in Rails, but here is my layman's understanding of the methods in SessionsHelper.
Firstly, #current_user is an instance variable of module SessionsHelper. This means that it persists for subsequent calls of the methods in SessionsHelper.
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
So by using "self" instead of just "current_user", current_user has been made part of the Session, but not yet stored in the database, correct?
self.current_user = user does not make 'current_user' a part of anything.
self refers to itself, SessionsHelper.
self.current_user = effectively calls its own method, the setter method, def current_user=(user).
If that's the case, the methods listed in Listing 8.22 are getter and
setter methods, correct?
Yes.
def current_user=(user) # Setter.
#current_user = user
end
The first to store current_user in the DB,
No. The first method, current_user=, does not store anything into the database. All it does is set the instance variable #current_user to user.
def current_user # Getter.
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
and the second to retrieve it as needed?
The second method, current_user, returns an existing #current_user value.
If #current_user is nil(null), it runs
||= User.find_by_remember_token(cookies[:remember_token])
which retrieves and sets #current_user as the User from the database based on the browser's remember_token cookie.
And finally, the getter and setter methods are needed because its bad practice to expose an instance variable in a method (in this case "sign_in")?
In my opinion, the getter and setter methods are not exactly needed. It is possible to just use #current_user throughout the helper.
However, Devise (a popular user authentication gem) utilises the current_user method in its helpers. This comment is relevant as well.
In other words, it would be bad to replace: self.current_user = user with #current_user = user in the "sign_in" method?
There should be nothing wrong with replacing it with #current_user = user. This question (same link as linked comment above) shows you that the functionality is identical in the case of the tutorial.

Related

How to use recursive_update

I just started to use DBx::Class and begun slightly to understand, but it's complex.
I want to call "recursive_update" but I was not able to manage how I can use it.
If I understand the documentation right, I have to include it in the .../My/Schema.pm which was create by DBIx::Class::Schema::Loader?
__PACKAGE__->load_namespaces(default_resultset_class => '+DBIx::Class::ResultSet::RecursiveUpdate');
When I want to update the data I use the ResultSet with the relationships.
my $result = $schema->resultset('Table')->find($id});
$result->recursive_update({
'rel_table' => [....),
});
unfortunately I got an error:
Can't locate object method "recursive_update" via package My::Schema::Result::Table"
Where is my fault?
recursive_update has to be called on a ResultSet object, not a Result object.
You might want to add a helper method to your Result base class (if you already have one else create it, as it makes sense for many things) which gets a ResultSet restricted to the Result object it is called on and calls recursive_update on it.

Logging user activity in another logical layer without global HttpContext

I'm looking for a good solution to log DB changes in a web application developed using Play/Scala/ReactiveMongo. I need to know who changed what.
I have a separate layer namely Services in which all data access and business logics happens. All saves/updates/removes are done by certain methods so I can log them safely in just 3 or 4 methods but I need user identity there.
I don't have access to current user in services! There is no global HttpContext or Request or something like that which let me get the user identity (I think this way of getting user identity was incorrect of course).
I have a solution:
Add an implicit parameter to all methods of services which have side effects (change DB) and pass user identity to them.
def save(model: A)(implicit userIdentity: Option[UserIndentity] = None) = { ... }
As I wrapped default request, it can extend UserIdentity trait so that the implicit request matches the implicit parameters.
class MyRequest[A](...) extends WrappedRequest[A](request) extends UserIdentity
Finally, actions can use services like this:
def index() = MyAction { implicit request =>
//...
defaultService.save(model)
//...
}
The bad thing is that I have to add implicit parameters to those service methods. Isn't there another solution to get current user without polluting method signatures.
What's the problem with simply adding UserIdentity as an argument to your functions? Knowing who the user is seems to be important for your business logic - after all, today you want to log who performed the operation, tomorrow you will want to make sure this particular user is allowed to do it.
And I would just use a real UserIdentity object, not some hack with WrappedRequest, your services don't need to mess with a WrappedRequest instance.

Understanding Zend_Controller_Request_Abstract and other core Zend classes

I am digging deep into the Zend framework and at this point I am a little confused. I am particularly checking out the Zend_Controller_Action (*_Action), Zend_Controller_Request_HTTP(*_HTTP) and Zend_Controller_Request_Abstract (*_Abstract).
The *_Abstract class as it's name suggests is an abstract class hence cannot be instantiated and mostly provides method stubs along with a few final implementations. The actual implementation is in *_HTTP and *_Simple classes that subclass *_Abstract. Fair enough.
Now I am looking at the *_Action class, right here: http://framework.zend.com/apidoc/1.0/Zend_Controller/Zend_Controller_Action.html
Taking a look at $_request variable, it states that it is an instance of type *_Abstract. At this point, I am confused since I do not know why it should be of type *_Abstract and not *_Http since one cannot technically have an instance of an abstract class.
So my question:
Why is an instance of an abstract class being declared here.
Moving on, I want to override the $_request classes's getParams() method since this is how our application retrieves all parameters and I would like to apply some common sanitization and blacklisting rules to all of our input right here.
Unfortunately, when in my BaseController (the Main Controller that is subclassed by all other controllers) I declare something to the effect of :
$_request = new RequestClass(); //RequestClass subclasses Zend_Controller_Request_Http and overrides getParams()
my application does not launch itself the way it ought (I get a blank screen).
For those more curious, RequestClass() getParams() does nothing fancy but:
getParams()
{
$params = parent::getParams();
//sanitization rules over $params;
return $params;
}
The type hint of Zend_Controller_Request_Abstract effectively means the request must be an instance of a class that extends Zend_Controller_Request_Abstract.
Unless you're delibrately not using ZF's routing, you'll probably be better off doing your sanitation of parameters through the routes. Otherwise, if you're getting a blank screen, that means display_errors is turned off and the PHP error or exception is being logged instead. Check your web server error log to see what the actual problem is.

Magento: Accessing models/blocks from a phtml

Hi I have a situation where I need to look up the number of recently viewed products on catalog/product/view.phtml. In the recently viewed 'product_viewed.phtml' file it calls
$_products = $this->getRecentlyViewedProducts()
to get the recently viewed. How would I access this method from within the catalog/product/view.phtml file?
I don't know where this method is. I've tried searching for it but it doesn't seem to exist. When I write click it in Netbeans and click go to declaration it takes me to
class Mage_Reports_Block_Product_Viewed extends Mage_Reports_Block_Product_Abstract
Actually on the class itself. This class only has _toHtml(), getCount(), and getPageSize() methods.
I just need to know whether there are any recently viewed products.
Any help most appreciated!
Billy
If you look into 'Mage_Reports_Block_Product_Viewed', you will notice:
$this->setRecentlyViewedProducts($this->getItemsCollection());
That 'getItemsCollection' method is defined in the abstract class... And you will notice this abstract class will create a model based on $_indexName defined in the (subclassed) block.
If you just want the collection, you can probably get away with:
$_products = Mage::getModel('reports/product_index_viewed')->getCollection();
And then adding whatever you want to the collection:
$_products
->addAttributeToSelect('*')
->setAddedAtOrder();
// optionally add other methods similar to Mage_Reports_Block_Product_Abstract::getItemsCollection
Another approach that might be more suited would be to create the original block:
$productViewedBlock = $this->getLayout()->createBlock('reports/product_viewed');
On which you can simply call whatever you want:
$_collection = $productViewedBlock->getItemsCollection();
$_count = $productViewedBlock->getCount();
The getRecentlyViewedProducts function is a magical getter that gets the data that was set with setRecentlyViewedProducts in app/code/core/Mage/Reports/Block/Product/Viewed.php (which builds it using app/code/core/Mage/Reports/Block/Product/Abstract.php's function _getRecentProductsCollection).
This is complicated stuff that you don't want to reproduce; its better, IMO to make your own Block that extends Mage_Catalog_Block_Product_Abstract that will give you access to the same functionality, and drop your new block into the page you're working on.

Linq-to-entities: How to create objects (new Xyz() vs CreateXyz())?

What is the best way of adding a new object in the entity framework. The designer adds all these create methods, but to me it makes more sense to call new on an object. The generated CreateCustomer method e.g. could be called like this:
Customer c = context.CreateCustomer(System.Guid.NewGuid(), "Name"));
context.AddToCustomer(c);
where to me it would make more sense to do:
Customer c = new Customer {
Id = System.Guid.NewGuid(),
Name = "Name"
};
context.AddToCustomer(c);
The latter is much more explicit since the properties that are being set at construction are named. I assume that the designer adds the create methods on purpose. Why should I use those?
As Andrew says (up-voted), it's quite acceptable to use regular constructors. As for why the "Create" methods exist, I believe the intention is to make explicit which properties are required. If you use such methods, you can be assured that you have not forgotten to set any property which will throw an exception when you SaveChanges. However, the code generator for the Entity Framework doesn't quite get this right; it includes server-generated auto increment properties, as well. These are technically "required", but you don't need to specify them.
You can absolutely use the second, more natural way. I'm not even sure of why the first way exists at all.
I guess it has to do with many things. It looks like factory method to me, therefore allowing one point of extension. 2ndly having all this in your constructor is not really best practice, especially when doing a lot of stuff at initialisation. Yes, your question seems reasonable, i even agree with it, however, in terms of object design, it is more practical as they did it.
Regards,
Marius C. (c_marius#msn.com)