$ rails c
You just have to know that when you save a model, the lifecycle accepts that validations are running in the certain point.
These are a bunch of functions that I can provide to control what happens to that point, and validation happens at a specific point in the cycle, so I have controlled before validations happen, and after the validations have run.
This is a form of Aspect-oriented programming. These functions will be injected before and after each model save, or model.valid?.
Make Checks DRY
Goal: enforce that movie names must be less than 40 characters
– Call a “check” function from every place in app where a Movie might get created or edited? That’s not DRY!
Everywhere you want to save do as follows:
check consistency # you might miss to call these. It is not DRY
• How do we DRY out cross-cutting concerns: Logically centralized, but may appear multiple places in implementation?
• Advice is a specific piece of code that implements a cross-cutting concern
• Pointcuts are the places you want to “inject” advice at runtime
• Advice+Pointcut = Aspect"
• Goal: DRY out your code
• Specify declaratively in model class
Validation is advice in AOP sense
– many places in app where a model could be modified/updated
– including indirectly via associations!
• So where are the pointcuts? (as shown above in the picture)
even though there isn't necessarily something in the source code that says, " Call this validation function," you essentially want the ability to annotate the code in different places saying, " These are the places that I would like to have it called."
So, we would want to check that the changes are valid, so the question is, " Where are the pointcuts here?" This brings to the idea of model lifecycle callbacks. This is what you can think of as rails very limited implementation of a certain type of Aspect- Oriented Programming. What actually happens when you use the active record calls like create or update attributes is that before they actually do their thing and talk to the database, there's a number of hooks that are provided where you can essentially cause other things to happen. They're called callbacks ... lifecycle callbacks.
But the idea is that this is a limited version of Aspect- Oriented Program because we don't get to decide where the pointcuts are, but the pointcuts have been determined to be, " This is where validations are run, and these are other specific places where you can add your functionality," so when you call valid explicitly, validation gets triggered; you could do that.
that we think of Validation as kind of Aspect- Oriented is because nowhere in the code in a typical active record model are you going to find something that says, " Call this everything that does validation." It's implicit that it's going to happen along these two paths.
anytime that you're going to modify model information in the database if you're doing it via one of the active record calls, they all have to funnel down one of these two paths (create/update), and both paths include the ability to call validation.
If you think of goto as bad, think of this as come-from :) of that, if Go To is bad, think of this as Come From. Like you find yourself in a validation method, and how did you get here because there isn't a call to that method that's obvious anywhere. You just have to know that when you save a model, the lifecycle accepts that validations are running in the certain point.
So far we saw checking constraints on a model; what about constraints on call and controller actions?
The classic example of this is in most interesting applications, there are certain actions you can only do after you have logged in. There are other actions that anybody can do whether they're logged in or not, so what you really like to do is rather than putting a specific check in front of every action that requires you to be logged in, you would like declare that we say, " The following list of actions should actually do some check before calling the actual controller method to make sure ..." Let's say that the user's logged in.
want to set a filter for any action (any controller action) that requires you to be logged- in,
Means that this is something that's going to happen before the controller action is called.
Which controller actions does it apply to?
Well, we're saying it applies to everything except whatever action is matched by this route.
Notice where I'm putting this filter, I'm putting it in Application Controller which is the parents in the class hierarchy sense of all of your applications' other controllers, so when you put a filter in Application Controller, it will apply to all the actions across all the controllers.
-You can also create more controller specific filter e.g. they only apply to actions on Movies.
inside the filter I am redirecting to login.
This means that the regular Controller Action that was going to run when this before filter was triggered doesn't get to run because I have terminated things with the redirect.
Controlled filters can actually change the flow of execution
Filters declared in a controller also apply to its subclasses
– Corollary: filters in ApplicationController apply to all controllers
• A filter can change the flow of execution!
– by calling redirect_to or render
– You should add something to the flash to explain to the user what happened, otherwise will manifest as a “silent failure”!
you want sites to possibly be able to share information with each other about you, for your benefit, you don't want to do this by revealing your login information to different sites.
The idea is pretty neat. You actually model a session as its own entity. So if you think of a session beginning at the time that I log in and ending either with me logging out or with it timing out, like my abandoning. Then we can think of a session controller, whose job it is to create and delete the session and that's where we can centralize the logic that's going to negotiate with the remote service and try to act on my behalf.
• Now you can say:
@movie.reviews # Enumerable of reviews
• And also go the other way:
@review.movie # what movie is reviewed?
• You can add new reviews for a movie: Association proxy methods!
@movie = Movie.where("title='Fargo'")
@movie.reviews.build(:potatoes => 5)
OR @movie.reviews.create(:newspaper=>'Chron', ...) # how are these different from just new() & create()?
OR @movie.reviews << @new_review # instantly updates @new_review's FK in database!
@movie.reviews.find(:first,:conditions => '...')
To add a one to many association:
2. Create migration to add foreign key to owned site that references owning side
3. Apply migration
Suppose we have setup the foreign key
because we have already added the foreign key so the datasbe does it's job. These macros add abstraction for rails to fetch foreign keys so that we don't have to. so if we add it to one side we can use it but if we forget it to the other side that side won't work.
moviegoer: has_many :reviews
movie: has_many :reviews
review: belongs_to :moviegoer
belongs_to :movie # belong to deals with foreign key management, and can belong to a lot of other model classes
All the movies reviewed by some person
moviegoer: has_many :reviews
movie: has_many :reviews
reviews: belongs_to :moviegoer belongs_to :movie
• Now you can do:
@user.movies # movies rated by user
@movie.users # users who rated this movie
• My potato scores for R-rated movies"
|r| r.movie.rating == 'R' }
If we only have movies and reviews in our schema, Which of these, if any, is NOT a correct way of saving a new association, given m is an existing movie:
does m.save also save new reviews associated with it?
yes. it might be counter intuitive as the movie table does not change but merely one review has been added but, we can save on movie itself. If you call save on an object that owns other objects it calls save on all objects it owns/has created.
how about m.destroy?
yes all reviews associated with that movie will be destroyed also
difference between option 2 and 3: in 3 all other pending changes that are for an object that belongs to the movie also get saved, but in 2 only the review gets saved.
If we had moviegoers also, we would have to use one of the above on movie or moviegoer and set the other foreignkey explicitly.
join tables express a relationship between existing model tables using FKs"
• Join table has no primary key!
• because there’s no object being represented!
movie has_and_belongs_to_many :genres
genre has_and_belongs_to_many :movies