Posted about 1 year back at RailsJitsu
In this article I am going to give you a primer on adding AJAX, custom routes and controllers to Mephisto to extend it’s functionality. For this example we will add digg/dzone type voting to articles, which means we will be hacking the Content model and forcing the CachedPage to expire.
To start off we need to add a custom controller to our Mephisto code to handle the AJAX based voting. We also need to create a model for the votes and a migration to hold the vote data.
1
2
3
|
./script/generate controller votes vote
./script/generate model ContentVote
./script/generate migration add_voting_record
|
We will start with our migration file by adding our table as well as the code for reverse migration. We also need columns to track the content_id, ip_address of the voter, and whether it was an up or down vote, which we handle with a boolean value.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class AddVotingRecord < ActiveRecord::Migration
def self.up
create_table :content_votes, :force => true do |t|
t.integer :content_id
t.string :ip_address
t.boolean :up
end
end
def self.down
drop_table :content_votes
end
end
|
Next we to add the code to handle the vote record.
The code works like this; we add a one to many relationship to our ContentVote model, as every Content object can have many ContentVotes. The vote method takes a direction (up, or down) and an ip_address to prevent double voting. If the user has already voted we just short circuit. If not then we create a vote record, then set the boolean based direction and ip_address, then save our new vote record.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Content < ActiveRecord::Base
filtered_column :body, :excerpt
belongs_to :user, :with_deleted => true
belongs_to :site
[:year, :month, :day].each { |m| delegate m, :to => :published_at }
# Content Voting
has_many :content_votes
def vote( direction, ip_address )
return if self.content_votes.collect(&:ip_address).include?(ip_address)
vote = self.content_votes.new
vote.up = (direction == 'up') ? true : false
vote.ip_address = ip_address
vote.save
end
end
|
Now we add the relationship to our new ContentVote model.
1
2
3
|
class ContentVote < ActiveRecord::Base
belongs_to :content
end
|
On to our the controller that will be handling the AJAX call. We need to know what article the vote is for, and then call our vote method on the Content model. Then we need to expire the article page so that other people will see the new vote. The expiry part gave me some trouble, so let me elaborate how the expire_cached_pages method works.
The first parameter of the expire_cached_pages method is the controller making the request, this is our controller so we reference self. Next we pass a message to be logged, then we find all pages referenced by the article (as an article can live in multiple places simultaneously).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class VotesController < ApplicationController
session :off
def vote
@article = Content.find params[:id]
@article.vote( params[:direction], request.remote_ip )
#expire article page
site.expire_cached_pages(
self,
"Expired pages referenced by #{@article.class} ##{@article.id}",
site.cached_pages.find_by_reference(@article)
)
end
end
|
In order for us to be able to call a custom controller in Mephisto we need to add a route. In a plugin this is handled by Mephisto::Plugin.add_route, but we aren’t making a plugin just yet, so we have to patch it into the /lib/mephisto/routing.rb file, inside the self.connect_with(map) method which handles the Mephisto routing scheme. Also, since I am a big fan of resources we will do it that way, which also means we will add a :member action to delegate the call to and designate the request a :post.
Note the position of our added route, if you put it in the wrong place it won’t work. I like to put my custom routes just below the Plugin custom routes code.
1
2
3
4
5
6
|
# Custom voting route
map.resources :votes, :member => { :vote => :post }
# Original Mephisto code below here
map.dispatch '*path', :controller => 'mephisto', :action => 'dispatch'
map.home '', :controller => 'mephisto', :action => 'dispatch'
|
Of course, if we actually want our Liquid template to have access to the value of the votes we need to add it to the drop. In your /app/drops/article_drop.rb file you need to add this just above the protected methods:
1
2
3
4
5
6
7
8
|
# VOTING
def up_vote_count
@up_vote_count ||= liquify(*@source.content_votes.inject(0) {|sum,vote| sum + ((vote.up) ? 1 : 0) })
end
def down_vote_count
@down_vote_count ||= liquify(*@source.content_votes.inject(0) {|sum,vote| sum + ((!vote.up) ? 1 : 0) })
end
|
We are almost done! The last thing we need to do is implement this AJAX call in our Liquid template, and the rjs code to change the vote numbers. This part will vary based on what theme you are using. I will give my code and you will have to figure out where in your theme to put it.
Also, since these themes use Liquid, we cannot call our rails helpers, so what I did was create a temp rails app, insert the link_to_remote call, render it in the browser and then copy out the produced AJAX code.
1
2
3
4
|
<span id="votes_up">{{ article.up_vote_count }}</span>
<a onclick="new Ajax.Request('/votes/{{article.id}}/vote?direction=up', {asynchronous:true, evalScripts:true}); return false;" href="#"><img src="/images/vote_up.gif"></a><br/>
<span id="votes_down">{{ article.down_vote_count }}</span>
<a onclick="new Ajax.Request('/votes/{{article.id}}/vote?direction=down', {asynchronous:true, evalScripts:true}); return false;" href="#"><img src="/images/vote_down.gif"></a>
|
And our vote.rjs file (placed into the /app/views/votes/vote.rjs):
1
2
|
page.replace_html 'votes_up', @article.content_votes.inject(0) {|sum,vote| vote.up ? sum+1 : sum}
page.replace_html 'votes_down', @article.content_votes.inject(0) {|sum,vote| !vote.up ? sum+1 : sum}
|
And that is it! Run the migration, copy the up and down images to your theme folder, restart your mongrel processes, and you have content voting in Mephisto (this can be applied to comments as well with only changes to the _comment.liquid as comments and articles are the same base model)! I hope you enjoyed the ride, if you have trouble post in the comments and I will try and help.
A final note on customizing Mephisto in this way; if you do an svn up your code will get blasted into oblivion. So what to do? The way I handle this is to use a local Git repository. Git is amazing at doing code merges.
So I have 2 folders, one where the Mephisto trunk lives, and stays the way the core team wants it, and then a blog folder where my customizations live. When the core team updates trunk I do an svn up there, and then a Git merge to my blog directory.
So far this has never made me make a hand-merge, and it has never eaten my customizations.
Posted about 1 year back at RailsJitsu
In this article I am going to give you a primer on adding AJAX, custom routes and controllers to Mephisto to extend it’s functionality. For this example we will add digg/dzone type voting to articles, which means we will be hacking the Content model and forcing the CachedPage to expire.
In this article I am going to give you a primer on adding AJAX, custom routes and controllers to Mephisto to extend it’s functionality. For this example we will add digg/dzone type voting to articles, which means we will be hacking the Content model and forcing the CachedPage to expire.
To start off we need to add a custom controller to our Mephisto code to handle the AJAX based voting. We also need to create a model for the votes and a migration to hold the vote data.
1
2
3
|
./script/generate controller votes vote
./script/generate model ContentVote
./script/generate migration add_voting_record
|
We will start with our migration file by adding our table as well as the code for reverse migration. We also need columns to track the content_id, ip_address of the voter, and whether it was an up or down vote, which we handle with a boolean value.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class AddVotingRecord < ActiveRecord::Migration
def self.up
create_table :content_votes, :force => true do |t|
t.integer :content_id
t.string :ip_address
t.boolean :up
end
end
def self.down
drop_table :content_votes
end
end
|
Next we to add the code to handle the vote record.
The code works like this; we add a one to many relationship to our ContentVote model, as every Content object can have many ContentVotes. The vote method takes a direction (up, or down) and an ip_address to prevent double voting. If the user has already voted we just short circuit. If not then we create a vote record, then set the boolean based direction and ip_address, then save our new vote record.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Content < ActiveRecord::Base
filtered_column :body, :excerpt
belongs_to :user, :with_deleted => true
belongs_to :site
[:year, :month, :day].each { |m| delegate m, :to => :published_at }
# Content Voting
has_many :content_votes
def vote( direction, ip_address )
return if self.content_votes.collect(&:ip_address).include?(ip_address)
vote = self.content_votes.new
vote.up = (direction == 'up') ? true : false
vote.ip_address = ip_address
vote.save
end
end
|
Now we add the relationship to our new ContentVote model.
1
2
3
|
class ContentVote < ActiveRecord::Base
belongs_to :content
end
|
On to our the controller that will be handling the AJAX call. We need to know what article the vote is for, and then call our vote method on the Content model. Then we need to expire the article page so that other people will see the new vote. The expiry part gave me some trouble, so let me elaborate how the expire_cached_pages method works.
The first parameter of the expire_cached_pages method is the controller making the request, this is our controller so we reference self. Next we pass a message to be logged, then we find all pages referenced by the article (as an article can live in multiple places simultaneously).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class VotesController < ApplicationController
session :off
def vote
@article = Content.find params[:id]
@article.vote( params[:direction], request.remote_ip )
#expire article page
site.expire_cached_pages(
self,
"Expired pages referenced by #{@article.class} ##{@article.id}",
site.cached_pages.find_by_reference(@article)
)
end
end
|
In order for us to be able to call a custom controller in Mephisto we need to add a route. In a plugin this is handled by Mephisto::Plugin.add_route, but we aren’t making a plugin just yet, so we have to patch it into the /lib/mephisto/routing.rb file, inside the self.connect_with(map) method which handles the Mephisto routing scheme. Also, since I am a big fan of resources we will do it that way, which also means we will add a :member action to delegate the call to and designate the request a :post.
Note the position of our added route, if you put it in the wrong place it won’t work. I like to put my custom routes just below the Plugin custom routes code.
1
2
3
4
5
6
|
# Custom voting route
map.resources :votes, :member => { :vote => :post }
# Original Mephisto code below here
map.dispatch '*path', :controller => 'mephisto', :action => 'dispatch'
map.home '', :controller => 'mephisto', :action => 'dispatch'
|
Of course, if we actually want our Liquid template to have access to the value of the votes we need to add it to the drop. In your /app/drops/article_drop.rb file you need to add this just above the protected methods:
1
2
3
4
5
6
7
8
|
# VOTING
def up_vote_count
@up_vote_count ||= liquify(*@source.content_votes.inject(0) {|sum,vote| sum + ((vote.up) ? 1 : 0) })
end
def down_vote_count
@down_vote_count ||= liquify(*@source.content_votes.inject(0) {|sum,vote| sum + ((!vote.up) ? 1 : 0) })
end
|
We are almost done! The last thing we need to do is implement this AJAX call in our Liquid template, and the rjs code to change the vote numbers. This part will vary based on what theme you are using. I will give my code and you will have to figure out where in your theme to put it.
Also, since these themes use Liquid, we cannot call our rails helpers, so what I did was create a temp rails app, insert the link_to_remote call, render it in the browser and then copy out the produced AJAX code.
1
2
3
4
|
<span id="votes_up">{{ article.up_vote_count }}</span>
<a onclick="new Ajax.Request('/votes/{{article.id}}/vote?direction=up', {asynchronous:true, evalScripts:true}); return false;" href="#"><img src="/images/vote_up.gif"></a><br/>
<span id="votes_down">{{ article.down_vote_count }}</span>
<a onclick="new Ajax.Request('/votes/{{article.id}}/vote?direction=down', {asynchronous:true, evalScripts:true}); return false;" href="#"><img src="/images/vote_down.gif"></a>
|
And our vote.rjs file (placed into the /app/views/votes/vote.rjs):
1
2
|
page.replace_html 'votes_up', @article.content_votes.inject(0) {|sum,vote| vote.up ? sum+1 : sum}
page.replace_html 'votes_down', @article.content_votes.inject(0) {|sum,vote| !vote.up ? sum+1 : sum}
|
And that is it! Run the migration, copy the up and down images to your theme folder, restart your mongrel processes, and you have content voting in Mephisto (this can be applied to comments as well with only changes to the _comment.liquid as comments and articles are the same base model)! I hope you enjoyed the ride, if you have trouble post in the comments and I will try and help.
A final note on customizing Mephisto in this way; if you do an svn up your code will get blasted into oblivion. So what to do? The way I handle this is to use a local Git repository. Git is amazing at doing code merges.
So I have 2 folders, one where the Mephisto trunk lives, and stays the way the core team wants it, and then a blog folder where my customizations live. When the core team updates trunk I do an svn up there, and then a Git merge to my blog directory.
So far this has never made me make a hand-merge, and it has never eaten my customizations.
Posted about 1 year back at Jonathan.inspect
Rails give us the ability to write inline RJS via render :update syntax, as in :
render :update do |page|
page['addressPreviewStatus'].update 'Address Not Found'
end
Previous code will update the content of tag with “addressPreviewStatus†id with ‘Address Not Found’.
But how can we spec that out ? I needed to search a little as there seems to be very little examples.
First in rails there is a special assertion assert_select_rjs, merged from the assert_select plugin, it let you test your RJS with a syntax similar to RJS itself.
RSpecOnRails has a special matcher wrapping assert_select_rjs : has_rjs.
You can use it on response to specify what should be generated, for exemple you can :
# Specify response should contains an update or insert of some kind
response.should have_rjs
# Specify response should contains an update or insert for the tag with given id
response.should have_rjs('id')
# Specify response should contains a specific update, insert, etc. for the given tag
response.should have_rjs(:replace, 'id')
You get the point. Now, a nice syntax allows you to write RJS this way :
render :update do |page|
page['id'].update('replacement text')
end
So my first try was to use :
response.should have_rjs(:update, 'id', 'replacement text')
but this fail miserably with an error Unknown RJS statement type update. I tried different syntax but none worked.
Finally browsing through source of assert_select_rjs I found what I was looking for. When using this syntax you should use one of :chained_replace or :chained_replace_html depending what you want to test :replace or :update.
Now here is the solution :
response.should have_rjs(:chained_replace_html, 'id', 'replacement text')
Posted about 1 year back at Jonathan.inspect
Rails give us the ability to write inline RJS via render :update syntax, as in :
render :update do |page|
page['addressPreviewStatus'].update 'Address Not Found'
end
Previous code will update the content of tag with “addressPreviewStatus” id with ‘Address Not Found’.
But how can we spec that out ? I needed to search a little as there seems to be very little examples.
First in rails there is a special assertion assert_select_rjs, merged from the assert_select plugin, it let you test your RJS with a syntax similar to RJS itself.
RSpecOnRails has a special matcher wrapping assert_select_rjs : has_rjs.
You can use it on response to specify what should be generated, for exemple you can :
# Specify response should contains an update or insert of some kind
response.should have_rjs
# Specify response should contains an update or insert for the tag with given id
response.should have_rjs('id')
# Specify response should contains a specific update, insert, etc. for the given tag
response.should have_rjs(:replace, 'id')
You get the point. Now, a nice syntax allows you to write RJS this way :
render :update do |page|
page['id'].update('replacement text')
end
So my first try was to use :
response.should have_rjs(:update, 'id', 'replacement text')
but this fail miserably with an error Unknown RJS statement type update. I tried different syntax but none worked.
Finally browsing through source of assert_select_rjs I found what I was looking for. When using this syntax you should use one of :chained_replace or :chained_replace_html depending what you want to test :replace or :update.
Now here is the solution :
response.should have_rjs(:chained_replace_html, 'id', 'replacement text')
Posted about 1 year back at Blog Posts : Nex3
After many months of development,
make_resourceful version 0.2.0 is finally ready for release.
We’ve now got a full set of specs (i.e. tests)
and full RDoc documentation.
The code is cleaner, bugs are fixed, the filesize is smaller1.
Oh, and there are new features.
New, awesome features.
Installation
Before we go into the features, though, let’s talk about installing the thing.
There are actually three different ways to install it.
See, we didn’t just release 0.2.0 today;
we released 0.2.2 as well2.
What’s the difference?
0.2.0 is Rails 1.2.3 compatible.
0.2.2 is not.
All this really means is that 0.2.2 uses the new-style URL helpers.
Instead of child_path(@parent, @child),
current_object will now call parent_child_path(@parent, @child).
This is compatible with the Rails 2.0 API as well.
Now then. To install version 0.2.0, do
./script/plugin install http://svn.hamptoncatlin.com/make_resourceful/tags/rel_0-2-0
mv vendor/plugin/rel_0-2-0 vendor/plugin/make_resourceful
For version 0.2.0, do
./script/plugin install http://svn.hamptoncatlin.com/make_resourceful/tags/make_resourceful
Finally, if you want to take a bit of a risk,
you can install from trunk:
./script/plugin install http://svn.hamptoncatlin.com/make_resourceful/trunk
mv vendor/plugin/trunk vendor/plugin/make_resourceful
Note that, like version 0.2.2,
trunk is incompatible with Rails 1.2.3 and lower versions.
New Features
I talked about publish
and serialization earlier.
That’s probably the most visible change, but there’s plenty of other stuff as well.
For instance, there are several handy new accessors.
save_succeeded? and save_failed? return whether or not a database update,
like that done by create, update, and destroy,
completed successfully.
plural_action? and singular_action? return whether the current action is plural,
like index, or singular, like show.
There are also a bunch of new additions that aren’t glamorous, like new declarations and accessors,
but are important and useful all the same.
For example, in 0.2.0 all the default responses have an empty Javascript response.
This means that RJS templates will work automatically
without any modifications of the controller.
Another useful tweak: all the callback declarations (before, after, response_for)
now allow you to easily declare the same callback for multiple events.
All you have to do is pass in multiple arguments, like so:
before :show, :index do
@page_title = "Check out my resourceful controller."
end
Singular Resources
Thanks to an idea of Peter Baker’s,
make_resourceful 0.2.0 includes support for singular resources.
These are resources defined by a has_one relationship.
For instance, if Person has_one Hat,
you’d make a HatController rather than a HatsController.
make_resourceful would notice that the controller was singular3
because of the singular controller name
(no extra code required!),
and act accordingly.
This means that it wouldn’t define an index action,
and for current_object,
it would look up Person.find(params[:person_id]).hat instead of Hat.find(params[:id]).
URL Helpers
One of the handy things that was around in m_r 0.1.0
was the URL helpers: object_path and objects_path.
They automatically managed all the hassles with Rails URL generation.
A single call to object_path might be equivalent to a call to child_path(@grandparent, @parent, @child).
Unfortunately, these two helpers didn’t really cover all the URLs that one might have wanted to generate.
What if you wanted a URL for the new action?
They also were both _path methods: they returned the path of the resource,
without the domain name or protocol.
Thus, in 0.2.0, we’ve added a whole slew of other URL helpers.
There’s new_object_path and edit_object_path,
which return the paths to the new and edit actions, respectively.
Then, for each _path helper,
there’s now a _url helper, which returns the domain name as well.
For instance, on this blog’s PostsController,
new_object_url returns “http://nex-3.com/posts/new”.
resourceful_scaffold
make_resourceful 0.2.0 improves on more than just the API.
It also comes bundled with a generator
in the style of the built-in scaffold_resource generator.
The syntax is even the same:
./script/generate resourceful_scaffold ResourceName attribute1:type1 attribute2:type2 ...
This generates the model, views, tests,
and of course made_resourceful controller for the given resource.
It works out of the box with a make_resourceful installation.
It should be noted that the views it generates are Haml, not ERB,
to encourage people to make their views as beautiful as their controllers will be.
Haml is awesome; if you haven’t checked it out, you should4.
Deprecations
There are a few things that we’ve decided to deprecate or outright remove in make_resourceful 0.2.0.
Most of these are accessors that didn’t make sense to keep separate from the methods that used them.
They’d never make sense for users to directly call.
Rather than overriding them, we now reccommend that you override the method that called them.
Note that some of these were only introduced after 0.1.0 was released.
However, lots of people have been using trunk anyway,
so I’ll mention them anyway.
The first of these accessors we removed was current_param.
This just returned params[:id] and was only called by current_object.
Now if you want to change which parameter is used, just change current_object.
The same is true for model_includes.
It returned an empty hash,
and was supposed to be overridden to provide a value that would be passed to the :include option
of current_model.find in current_objects.
This was inconsistent with our general “override current_objects to customize it” policy,
so we chucked it.
The last accessor we deprecated was namespace_prefix.
This returned a prefix for the URL-generating methods called by object_path and friends.
The accessor is actually still around, but it’s been given a more general name: url_helper_prefix.
Override that instead.
Finally, we removed the associated_with declaration.
We did this because
associated_with :current_user, :source => :user
could in fact be done in one line using only the pre-existing declarations:
before(:create) { current_object.user = current_user }
Contributors
According to my records,
the following people contributed patches or ideas
that went into 0.2.0:
- Cristi Balan5
- Tom Stuart
- Don Petersen6
- Alex Ross6
- James Golick
- Mike Ferrier
- Jeff Hardy
- Hampton Catlin
- Myself
The Future
So what’s on the plate for 0.3.0?
Our first order of business will be to add in a patch by Jonathan Linowes
that will get rid of support for deeply nested resources
and add support for polymorphic nesting.
See his writeup
for more information.
We’ll also add a more integrated exception handling mechanism,
and some sort of better way of dealing with stuff like flash messages.
And then… who knows?
If you have some feature you particularly want,
don’t hesitate to let us know.
Posted about 1 year back at Simplistic Complexity - Home
One of the sweet things Rails provides is practically free error notification on forms. With just a little bit of
<%= error_messages_for :post %>
validations on your model, and some logic in your controller, bam, instant error messaging. Very simple.
But what if your form is using AJAX to post? The page doesn't re-render, so how is our buddy, error_messages_for, going to work? Well its not, we're gonna have to use AJAX to display the errors. It turns out this isn't too touch, but as usual we want to keep it DRY.
The Controller
class PostsController < ApplicationController
def create
@post = Post.new(params[:post])
@post.save!
Â
# automatically renders create.rjs.erb
Â
rescue ActiveRecord::RecordInvalid
Â
@model = @kit_item
render :template => 'shared/validation_error.rjs.erb'
Â
end
end
We will attempt to create the Post. If all goes well, then the create rjs action will fire and all is well. But if the record fails validation, we catch it, assign the current model to @model, and render shared/validaiton_error.rjs.erb. This is our generic AJAX action for rendering errors for Models.
The AJAX
Create shared/validation_error.rjs and use this little snippit, and thats pretty much it for this part.
page.replace_html "errors_for_#{@model.class.name.underscore}", "Oops! <ul>" + @model.errors.collect{|k,v| "<li>The #{k} #{v}</li>"}.to_s + "</ul>"
page.visual_effect :highlight, "errors_for_#{@model.class.name.underscore}", :startcolor => "'#ff0000'", :endcolor => "'#aca2b6'"
So, the errors just get collected and printed out to a div in the page. Let's go add this fancy div.
The View
This part is pretty simple. You just need to open up your form partial, posts/_form.html.erb and add this little view helper to the form:
<%= error_div_for post %>
This does nothing but prints out a div with a specific ID to prepare for our RJS action, if it's needed. So let's go make the view helper.
module ApplicationHelper
Â
def error_div_for(model)
%{<div id="errors_for_#{model.class.name.underscore}"></div>}
end
Â
end
Now we can just call this simple view helper and pass in any model and it will dynamically build a div with the appropriate ID inside.
And thats pretty much it. To go through it again, when the form is loaded, the error div will be placed in the page, waiting to be used. Once the form is submitted in the background, the controller's create action will attempt to save it. If the record is invalid with validation errors, then the universal validation_errror RJS template will be fired off and replace all the errors from the failed validation, into the div we placed in the page. Oh and of course a little red flash action to make sure the user sees the errors.
Posted about 1 year back at Simplistic Complexity - Home
One of the sweet things Rails provides is practically free error notification on forms. With just a little bit of
<%= error_messages_for :post %>
validations on your model, and some logic in your controller, bam, instant error messaging. Very simple.
But what if your form is using AJAX to post? The page doesn't re-render, so how is our buddy, error_messages_for, going to work? Well its not, we're gonna have to use AJAX to display the errors. It turns out this isn't too touch, but as usual we want to keep it DRY.
The Controller
class PostsController < ApplicationController
def create
@post = Post.new(params[:post])
@post.save!
# automatically renders create.rjs.erb
rescue ActiveRecord::RecordInvalid
@model = @kit_item
render :template => 'shared/validation_error.rjs.erb'
end
end
We will attempt to create the Post. If all goes well, then the create rjs action will fire and all is well. But if the record fails validation, we catch it, assign the current model to @model, and render shared/validaiton_error.rjs.erb. This is our generic AJAX action for rendering errors for Models.
The AJAX
Create shared/validation_error.rjs and use this little snippit, and thats pretty much it for this part.
page.replace_html "errors_for_#{@model.class.name.underscore}", "Oops! <ul>" + @model.errors.collect{|k,v| "<li>The #{k} #{v}</li>"}.to_s + "</ul>"
page.visual_effect :highlight, "errors_for_#{@model.class.name.underscore}", :startcolor => "'#ff0000'", :endcolor => "'#aca2b6'"
So, the errors just get collected and printed out to a div in the page. Let's go add this fancy div.
The View
This part is pretty simple. You just need to open up your form partial, posts/_form.html.erb and add this little view helper to the form:
<%= error_div_for post %>
This does nothing but prints out a div with a specific ID to prepare for our RJS action, if it's needed. So let's go make the view helper.
module ApplicationHelper
def error_div_for(model)
%{<div id="errors_for_#{Inflector.underscore(model.class)}"></div>}
end
end
Now we can just call this simple view helper and pass in any model and it will dynamically build a div with the appropriate ID inside.
And thats pretty much it. To go through it again, when the form is loaded, the error div will be placed in the page, waiting to be used. Once the form is submitted in the background, the controller's create action will attempt to save it. If the record is invalid with validation errors, then the universal validation_errror RJS template will be fired off and replace all the errors from the failed validation, into the div we placed in the page. Oh and of course a little red flash action to make sure the user sees the errors.
Posted about 1 year back at for i in infinity
Here is a simple tutorial to demonstrate the use of prototype Window login form with RubyOnRails and Ajax. The idea behind this is to override Ok event of Dialog.confirm() form and send Ajax request to controller. The demo application makes use of RJS for Ajax callbacks.
See Live Demo
To ...
Posted about 1 year back at Cody Fauser
Have you ever wanted to write something, but felt so bogged down by the tools that you gave up? Even worse is if you don't even have the option of giving up. When I was writing RJS Templates for Rails using the O'Reilly Word template there were many moments when I thought that I may end it all. Thankfully, that didn't happen.
I have a profound hatred for Microsoft Word. I feel like its entire codebase exists solely to make my writing experience miserable. Capitalizing everything I need lowercase, formatting my paragraphs in ways that I don't want, just messing with my document to ruin my weekend. I'm sure it is possible to make Word do what I want, but I just don't feel like it is worth the effort. I don't want to waste my time figuring out how to make Word work for me, when what I want to do is really simple. I just want to write text. Is this a lot to ask for?
Thankfully, Tobias Lütke let me know about Scrivener. What can I say? Scrivener is fantastic. Scrivener not only gets out of my way when I'm writing, it actually has features that help with the process of writing. Scrivener helps in all the areas where it counts when it comes to writing. Organization, taking notes, keeping lists of reference material, storing research documents right in the project. Scrivener feels like a combination of a simple text editor with the power of an organizational tool like Omni Outliner built right in. You can create an outline in minutes, reorganize by dragging sections around, look at high level views of a section in a cork board view, and then turn off the entire UI when you're ready to get down to writing by entering the full screen typewriter mode.
There are many, many more killer features that I won't get into here. Do yourself a favour and get the demo. Better yet, just buy it. It only costs $34.99, which is less than you're going to spend next week at Starbucks anyway. You can get up to speed with all the features in less than an hour with the included tutorials.

Posted about 1 year back at Cody Fauser
Have you ever wanted to write something, but felt so bogged down by the tools that you gave up? Even worse is if you don't even have the option of giving up. When I was writing RJS Templates for Rails using the O'Reilly Word template there were many moments when I thought that I may end it all. Thankfully, that didn't happen.
I have a profound hatred for Microsoft Word. I feel like its entire codebase exists solely to make my writing experience miserable. Capitalizing everything I need lowercase, formatting my paragraphs in ways that I don't want, just messing with my document to ruin my weekend. I'm sure it is possible to make Word do what I want, but I just don't feel like it is worth the effort. I don't want to waste my time figuring out how to make Word work for me, when what I want to do is really simple. I just want to write text. Is this a lot to ask for?
Thankfully, Tobias Lütke let me know about Scrivener. What can I say? Scrivener is fantastic. Scrivener not only gets out of my way when I'm writing, it actually has features that help with the process of writing. Scrivener helps in all the areas where it counts when it comes to writing. Organization, taking notes, keeping lists of reference material, storing research documents right in the project. Scrivener feels like a combination of a simple text editor with the power of an organizational tool like Omni Outliner built right in. You can create an outline in minutes, reorganize by dragging sections around, look at high level views of a section in a cork board view, and then turn off the entire UI when you're ready to get down to writing by entering the full screen typewriter mode.
There are many, many more killer features that I won't get into here. Do yourself a favour and get the demo. Better yet, just buy it. It only costs $34.99, which is less than you're going to spend next week at Starbucks anyway. You can get up to speed with all the features in less than an hour with the included tutorials.

1 2 3 4 5 6 ... 8