AutoTest (part of ZenTest suite) is a very useful tool when you are working with Ruby On Rails development, but I always had problems configuring cool notifications for it, until now!
A friend of mine, started a little project to develop a gem (his first idea was just a lib for his own use, but now it turned to be an open source gem), that makes really easy to setup AutoTest to notify you of test results in a very cool way!
If you have a Mac, it will use Growl.
If you have windows, it will use Snarl.
If you have a Linux Box, it will use zenity or libnotify for Gnome and kdialog for KDE (I think libnotify works for any desktop environment).
And it is even cooler that this, if you run a Mac or Linux box, it can talk to you when your tests fail!
The project is hosted at GitHub, and to use it, you just need to run this two commands:
gem install carlosbrando-autotest-notification --source=http://gems.github.com
an-install
if you want it to talk to you, replace the last command with:
an-install -s
the voice code for linux is not in the main repository yet, it is on my fork, so if you run a linux box, and want to play with voice notification, you need to have espeak installed, and install my instead of the primary one (at least until the code is pushed back).
gem install urubatan-autotest-notification --source=http://gems.github.com
If you have a Windows box and want to play with voice notification, be a smart guy, format your machine ans install linux, you will be a lot happier that you are today ![]()
If this is not an option for you, you can always fork the project, write the code for a windows speech engine, and send us a pull request
Bellow a quote from the project readme about the dependencies of each platform:
* If you’re using a Mac:
You need to have Growl and growlnotify installed on your machine.Download the Growl [http://growl.info/index.php] and install it like any other application on your Mac
Then you must install the growlnotify.
In your shell, cd to the directory on the Growl disk image containing growlnotify, and type ./install.sh.
That script will install growlnotify to /usr/local/bin and the manpage to /usr/local/man.* If you’re using Windows (with cygwin):
You need to have Snarl and sncmd installed on your machine.Download Snarl [http://www.fullphat.net/] and install it like any other application on your machine.
Then download sncmd [http://www.k23productions.com/download.php?view.105] open the zip file and place
the executable from the zip in any directory in windows PATH (for example c:\windows).* If you’re using Windows (without cygwin):
You need to have Snarl, diffutils and ruby-snarl installed on your machine.Download Snarl [http://www.fullphat.net/] and install it like any other application on your machine.
Download DiffUtils for Windows [http://gnuwin32.sourceforge.net/packages/diffutils.htm] and follow the installation
instructions on the site.Run in the command prompt:
$ gem install ruby-snarl
After all that. You must update the environment variable PATH with the path to the bin of diffutils.
It’s the price that was paid for using Windows (try cygwin).* If you’re using Linux:
You need to have libnotify binaries installed.For ubuntu this means: sudo apt-get install libnotify-bin
Other distributions may package it with other names, do a search for libnotify using your distribution package
manager.If you use KDE and do not have libnotify-bin installed, it will try to use kdialog wich is part of KDE.
It also works if you have zenity installed.If you want to be notified with voice of the test results, instal espeak too, it is not needed.
And when running an-install, pass a “-s” switch, when you do not want voices notifications anymore, just run it
again without the “-s” switch.
I know that you can live without this cool new toy, but think about your work mates trying to figure out, how do your computer can tell you that your tests passed or not
If you enjoyed this post, make sure you subscribe to my RSS feed!
First of all, this is not my code, this is just a backport of a Rails EDGE feature to work with Rails 2.0.2 …
I searched through Rails code and found the points tha needed some changes to work with rails 2.0.2, and not the only thing you need if you want this features but can’t use EDGE right now, just place this code into a file in the config/initializers (I named it dirty.rb) …
So, let’s stop the cheap talk and show the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | module ActiveRecord class Base # Updates the associated record with values matching those of the instance attributes. # Returns the number of affected rows. def update(attribute_names = @attributes.keys) quoted_attributes = attributes_with_quotes(false, false, attribute_names) return 0 if quoted_attributes.empty? connection.update( "UPDATE #{self.class.quoted_table_name} " + "SET #{quoted_comma_pair_list(connection, quoted_attributes)} " + "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quote_value(id)}", "#{self.class.name} Update" ) end # Returns a copy of the attributes hash where all the values have been safely quoted for use in # an SQL statement. def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) quoted = {} connection = self.class.connection attribute_names.each do |name| if column = column_for_attribute(name) quoted[name] = connection.quote(read_attribute(name), column) unless !include_primary_key && column.primary end end include_readonly_attributes ? quoted : remove_readonly_attributes(quoted) end end # Track unsaved attribute changes. # # A newly instantiated object is unchanged: # person = Person.find_by_name('uncle bob') # person.changed? # => false # # Change the name: # person.name = 'Bob' # person.changed? # => true # person.name_changed? # => true # person.name_was # => 'uncle bob' # person.name_change # => ['uncle bob', 'Bob'] # person.name = 'Bill' # person.name_change # => ['uncle bob', 'Bill'] # # Save the changes: # person.save # person.changed? # => false # person.name_changed? # => false # # Assigning the same value leaves the attribute unchanged: # person.name = 'Bill' # person.name_changed? # => false # person.name_change # => nil # # Which attributes have changed? # person.name = 'bob' # person.changed # => ['name'] # person.changes # => { 'name' => ['Bill', 'bob'] } # # Before modifying an attribute in-place: # person.name_will_change! # person.name << 'by' # person.name_change # => ['uncle bob', 'uncle bobby'] module Dirty def self.included(base) base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was' base.alias_method_chain :write_attribute, :dirty base.alias_method_chain :save, :dirty base.alias_method_chain :save!, :dirty base.alias_method_chain :update, :dirty base.superclass_delegating_accessor :partial_updates base.partial_updates = true end # Do any attributes have unsaved changes? # person.changed? # => false # person.name = 'bob' # person.changed? # => true def changed? !changed_attributes.empty? end # List of attributes with unsaved changes. # person.changed # => [] # person.name = 'bob' # person.changed # => ['name'] def changed changed_attributes.keys end # Map of changed attrs => [original value, new value] # person.changes # => {} # person.name = 'bob' # person.changes # => { 'name' => ['bill', 'bob'] } def changes changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h } end # Clear changed attributes after they are saved. def save_with_dirty(*args) #:nodoc: save_without_dirty(*args) ensure changed_attributes.clear end # Clear changed attributes after they are saved. def save_with_dirty!(*args) #:nodoc: save_without_dirty!(*args) ensure changed_attributes.clear end def self.partial_updates? @partial_updates end private # Map of change attr => original value. def changed_attributes @changed_attributes ||= {} end # Handle *_changed? for method_missing. def attribute_changed?(attr) changed_attributes.include?(attr) end # Handle *_change for method_missing. def attribute_change(attr) [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr) end # Handle *_was for method_missing. def attribute_was(attr) attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr) end # Handle *_will_change! for method_missing. def attribute_will_change!(attr) changed_attributes[attr] = clone_attribute_value(:read_attribute, attr) end # Wrap write_attribute to remember original attribute value. def write_attribute_with_dirty(attr, value) attr = attr.to_s # The attribute already has an unsaved change. unless changed_attributes.include?(attr) old = clone_attribute_value(:read_attribute, attr) # Remember the original value if it's different. typecasted = if column = column_for_attribute(attr) column.type_cast(value) else value end changed_attributes[attr] = old unless old == typecasted end # Carry on. write_attribute_without_dirty(attr, value) end def update_with_dirty update_without_dirty(changed) end end end ActiveRecord::Base.send :include, ActiveRecord::Dirty #ActiveRecord::Base.partial_updates = true -- Now there is no way to disable this ugly partial updates hack |
I hope it can be usefull for others too ![]()
you can see how it works in this posts about rails EDGE.
If you have any problem, just leave a comment.
PS.: yes, it can be cleaned up a little, but it is already working fine for me, and it is just a backport
If you enjoyed this post, make sure you subscribe to my RSS feed!
I have been using this little plugin I wrote for some time now.
The main idea is that I always start working in the migrations, and there I define: not null fields, length for string fields, what fields are numbers and what are not, references to other models (this at least can be guessed with the _id ending), unique fields or field groups and things like that.
After the migration is executed, I need to rewrite almost it all, with even more information in the model, for example: the maximum length for a string field is not enough anymore, now you need the minimum length too.
And I really do not like idea of doing the same thing more than once (I think some of you agree with me, even if you do not agree with my solution).
To solve this problem, I created this little plugin, that is really a generator
You want to see some code? So, let’s start with a migration: ./script/generate migration example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Example < ActiveRecord::Migration def self.up create_table :examples do |t| t.string :name, :null => false t.text :description t.integer :a_number t.references :example end end def self.down drop_table :examples end end |
Now, let’s run rake db:migrate && ./script/generate drymodel example
And it will generate the following code for the model:
1 2 3 4 5 6 7 8 9 | class Example < ActiveRecord::Base #Relationships belongs_to :example #Validations validates_presence_of :name validates_numericality_of :a_number, :allow_nil => true validates_length_of :name, :maximum => 255, :allow_nil => true validates_associated :example, :allow_nil => true end |
Up to now, it generates validations for uniques, non nulls, numerics, associations, …
pretty much, every thing that you have already wrote on the migration.
Liked it?
to install, just add the github gem source (you need to do it only once):
gem sources -a http://gems.github.com/
and to install the plugin gem:
gem install urubatan-mydry_generator
Now you are ready to play!
If you want to take a look at the code, it is hosted in GitHub.
There is also a dryscaffold generator, that will do the same as the standard scaffold (with REST and all), but it list the columns from the database and you do not need to name them on the command line.
But I do not think it is that useful
If you enjoyed this post, make sure you subscribe to my RSS feed!
Following the sequence I started here, but changing the proposed order, let’s talk a little about Rails Generators.
Generators are one of the coolest things in Rails!
Of course you have already seem them, remember the first screencast you’ve seen about rails? Yes, that one where the guy wrote an entire CRUD application in two minutes …
Generators are a way to, generate code
But the generated code can be what you want, and rails already has a great support for writing generators, because it is already used in the rails core.
OK, but why would you want to write a generator?
Think about the project that is starting today, remember all that CRUD forms you’ll need to create, or that almost equal start point for every page of the application …
Now think about all the applications for your company …
Got it?
Ok, I agree that “scaffolding” a generic CRUD is not really useful, but if that scaffold is done following your company standards, it can improve a lot the development performance …
Now, I guess you are thinking: Good, generators seem cool, but stop the easy talk e show me the code!
So, let’s play a little …
Wi’ll start creating a new rails project: rails plugins102
Now from the project directory run: script\generate plugin my_generator
(if you think this is kind of a flash back, maybe you read my previous post about creating rails plugins
)
Now that you have already created your brand new plugin, let’s write some code!
To create a generator you need to create a generators directory inside your plugin’s directory, a directory with the name you want for your generator (for example test_gen), and a templates directory inside the previous one, for example, it will look like this:
Ok, you do not really need a plugin to create a generator, you can of course, place your brand new generator inside the following directories:
But I think that a plugin is the easiest way to do it, and it will be easier to deploy to your applications too.
Of course that a GEM would be a better way, because you can install only once per machine, and does not need it installed on the server, but I have never created a GEM before, so let’s stay with the plugin for a wile …
to start coding the generator, we’ll need to create a file named [generator_name]_generator.rb inside the generator’s directory, in my case, I’ve created the file: my_generator/generators/test_gen/test_gen_generator.rb
Now inside that file, create the class to define your generator, it must be named according to the file name, in my case TestGenGenerator, and this class must extend one of the base classes for Rails generators:
Rails::Generator::Base or Rails::Generator::NamedBase, I’ll use NamedBase (The base for the controller generator), and I’ll create a generator for a model and a migration, just to show how to do it …
The NamedBase is the perfect base class for generators that expect parameters in the form: Name [param1] [param2] …
For all the others, Base is a best start point …
The starting point for our code is:
1 2 3 4 5 6 7 | class TestGenGenerator < Rails::Generator::NamedBase def manifest record do |m| end end end |
In this class, all we need to do is to set up the generator’s manifest (more on this later) and to setup any local variables to be used by our templates (more on this later too).
with only this, we can already run: ruby script\generate test_gen asdas_dasda asd:ash (randon letters as parameters for now).
The NamedBase will automatically set up some variables for us, and the values for the given parameters will be:
of course this generator now will generate nothing, because our manifest is empty, so let’s build a simple example to see how it really works …
so, lets create inside the template directory, a directory called “dummy”, and a blank file called “log.log” inside of it and let’s change the manifest method to some thing like this:
1 2 3 4 5 | def manifest record do |m| m.file 'dummy/log.log', "log/#{file_path}.log" end end |
This code will tell the generator, to copy our newly created, blank file, to $APP_ROOT/log/asdas_dasda.log if we run the generator with the same parameters as before …
but just copying files from one place to another is not a very cool thing to be done, so let’s play a little with ERB, and let’s create a migration for our plugin, so we need to change again the manifest method as follow:
1 2 3 4 5 6 7 8 | def manifest @migration_name = "Create#{class_name}" @migration_action = "add" record do |m| m.file 'dummy/log.log', "log/#{file_path}.log" m.migration_template 'lib/mymigration.rb',"db/migrate", :migration_file_name => "create_#{file_path}" end end |
As you can see in the code, I’m telling the manifest that I have a template named mymigration.rb inside the directory lib in my templates directory, it will be processed and the result will be placed inside the directory “db/migrate” and will be called “000_create_asdas_dasda.rb” (the 000 will be replaced with the latest migration number plus one as the normal “generate migration” command do.
to be able to access the attributes migration_name and migration_action in the template, we need create the accessor methods for them, it is as easy as adding the following line to the TestGenGenerator class (outside the manifest method):
1 | attr_accessor :migration_name, :migration_action |
My migration template is the following:
1 2 3 4 5 6 7 8 9 10 11 | class <%= migration_name.underscore.camelize %> < ActiveRecord::Migration def self.up<% attributes.each do |attribute| %> <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%> <%- end %> end def self.down<% attributes.reverse.each do |attribute| %> <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end -%> <%- end %> end end |
It is an ERB template that will generate a ruby file.
Now we have already a very cool working plugin, but what else can we do?
to answer this question, I’ll quote a little the documentation of the Rails::Generator::Commands::Create class, that means, what can you do within the manifest method:
I think that is it, you can ask more questions if you want, I’ll try to answer all, if any
for more resources on Rails generators you can follow this links:
http://wiki.rubyonrails.org/rails/pages/UnderstandingGenerators
http://www.aidanf.net/node/33
http://api.rubyonrails.org/classes/Rails/Generator/Base.html
http://api.rubyonrails.org/classes/Rails/Generator/NamedBase.html
http://api.rubyonrails.org/classes/Rails/Generator/Commands/Create.html
I hope this little step by step help some one!
The next one will be about writing tests for your plugins! and the fourth I have not started to think about yet
If you enjoyed this post, make sure you subscribe to my RSS feed!
One of the beauty of Ruby on Rails are the Plugins …
Ruby On Rails itself is already a great framework, but the combination of Rails plugins with the Ruby Open Classes is a killer feature!
This little combination enables you to create you own “tags” to be used in your views and layouts.
And it is really easy to create this kind of helper in Rails.
The non plugin way is to create a method in one of the Helper classes (the ones in app/helpers directory), for example, if all the forms in your application are inside a table, with one column for the label and one column for the real field, you can create a helper to simplify the code in your views like this:
The standard very simple form for the “Example” model we created for this example, in the application standards would be:
1 2 3 4 5 6 7 | <% form_for(@example) do |f| %> <table> <tr><td><label for="example_name">Name</label></td><td><%= f.text_field :name %></td></tr> <tr><td><label for="example_name">Url</label></td><td><%= f.text_field :url %></td></tr> <tr><td colspan="2"><%= f.submit "Update" %></td></tr> </table> <% end %> |
but if we edit the file app/application_helper.rb and add the following method there:
1 2 3 | def textfield label, object, property, options = {} %Q{<tr><td><label for="#{object.to_s}_#{property.to_s}">#{label}</label></td><td>#{text_field object, property, options}</td></tr>} end |
we could simplify a lot the code only with the help of this “helper”, the new form code would be like this:
1 2 3 4 5 6 7 | <% form_for(@example) do |f| %> <table> <%= textfield "Name", :example, :name %> <%= textfield "Url", :example, :url %> <tr><td colspan="2"><%= f.submit "Update" %></td></tr> </table> <% end %> |
for this little form, this is too much work for some less characters in the view, but if you think about your entire application it would help you a lot!
Now think bigger, this ugly table/form standard I’ve created is the standard for the entire company, all applications in the company follow this standard.
So all developers, always write the forms using the first option (the ugly one, with lots of HTML).
and one day, a new designer tells that you need to add one new class in the “TR” tag in all forms for all applications, and you start crying ![]()
Or you choose to use the second option (with the helper method) and just need to change one line of code for each application in the company!
Now you are a hero, right?
But if you are smarter than this? what about creating a plugin, that all applications can use, and define this tag inside the plugin?
That way you change only one line of code, test only once, and all applications will be fixed at the same time!
And as always with Rails, this is a lot easy to do!
Just follow this steps:
1 2 3 4 5 6 | # LifeSaver module LifeSaver def textfield label, object, property, options = {} %Q{<tr><td><label for=#{object.to_s}_#{property.to_s}>#{label}</label></td><td>#{text_field object, property, options}</td></tr>} end end |
1 2 | # Include hook code here ActionView::Base.send :include, LifeSaver |
Now you have just created your first Ruby On Rails plugin!
And yes, the code is the same as the one used in the application_helper.rb, the only trick here is in the init.rb file of the plugin, that line of code is including all methods of the module “LifeSaver” in the base class for all Rails views, the ActionView::Base class …
Now, if you did this, instead of just adding a helper method to each application, tell your boss how much time you have saved for the company with this little story here, and ask for a rise
I hope this little step by step help some one!
The next one will be about writing tests for your plugins, and the third one will talk about plugins with generators, the forth you’ll have to come back here to discover
If you enjoyed this post, make sure you subscribe to my RSS feed!
Tags: ActionView::Base, howto, plugin, rails, ruby, step-by-step
Ruby On Rails 2.0 has just been officially released.
If you open your command prompt now and run: gem install -y rails
The rails 2.0.1 will be installed and you will be ready to play!
There are not that many new features, but there are some cool new ones
Take a look at DHH’s official post and start to play
If you enjoyed this post, make sure you subscribe to my RSS feed!
Versão em portugues aqui
Before we start with the chat, you need to have the requisites installed, juggernaut need the gems json and eventmachine installed, so, run the following command before reading the rest of this example …
Ok, now we are ready to go!
First of all, create a rails application, and install the Juggernaut plugin with this commands:
Juggernaut uses an external Flash XML Push Server to do the reverse ajax magic, and we’ll need to configure that server, so, let’s edit the file: config/juggernaut.yml
I’ve put in the snipet bellow just the lines I changed in the file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | PUSH_PORT: 8080 ... DEFAULT_CHANNELS: - "chat" ... PUSH_HELPER_HOST: "localhost" ... SECRET: "481516232342edededededed" ... LOGIN_GET_URL: "http://localhost:3000/session/login" LOGOUT_GET_URL: "http://localhost:3000/session/logout" ... SESSION_ID: "_chattest_session_id" ... BASE64: true |
I had to change the PUSH_PORT because I’m using a linux box and the application does not run as root, so I was not able to use the default 443 port, and I do not think that 443 is a good port choice because it is the default HTTPS port.
Make sure you change the PUSH_HELPER_HOST to the same host name as the one you are using to access the application, localhost will do the job in the development environment, but remember to change it when you publish your site in a production environment.
the LOGIN_GET_URL and LOGOUT_GET_URL are used to notify the application about clients arriving and leaving, we will really use only the leaving notification.
the SESSION_ID must be the same as the defined cookie name for your application, it is defined in the application controller for rails 1.2.x and will be moved to environment.rb for rails 2.0
and BASE64 must be set to true if we want to use the rails helpers to generate the javascript for us.
Now let’s start the layout for the application. Create a file named pubic/stylesheets/public.css with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | body { background-color: white; } #users { float: left; width: 200px; height: 400px; border-style: inset; overflow: auto; color: white; background-color: gray; } #dasd { height: 400px; margin-left: 5px; border-style: inset; overflow: auto; color: white; background-color: gray; } #controls { clear: both; padding: 0 0 0 0; height: 55px; vertical-align: top; border-style: inset; overflow: auto; color: white; background-color: gray; } |
and a file named: app/views/layouts/application.rhtml with the following content.
1 2 3 4 5 6 7 8 9 10 | <html> <head> <title>Chat Test</title> <%= stylesheet_link_tag 'public' %> <%= javascript_include_tag :defaults %> </head> <body> <%= yield %> </body> </html> |
in this file it is important to add the stylesheet and the default javascript includes.
With the layout ready (ok, I know it is pretty ugly, but I’m a developer not a webdesigner so, for production, ask a designer in your team for a new layout
) let’s generate the needed files and database tables with the following four commands.
Every thing ready, we just need to edit some files …
Open the OnlineUser model (app/model/online_user.rb) and change the content to something like the following
1 2 3 4 | class OnlineUser < ActiveRecord::Base validates_presence_of :username, :session_id, :last_seen validates_uniqueness_of :username, :if => Proc.new {|user| user.online } end |
It is just a few validations, not really needed, this was my first idea for the chat, I’ve changed it a little but still works.
Now let’s code the main view of the application in the file:app/views/chat/index.rhtml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <!-- Register with Juggernault --> <%= listen_to_juggernaut_channels [:generic],session.session_id %> <!-- The Users List --> <div id="users"> <ul id="users_list"></ul> </div> <!-- The messages pane --> <div id="dasd"></div> <!-- The controls pane (login and send messages) --> <div id="controls"><%= render :partial => 'login' %></div> <!-- An util javascript to scroll the messages window --> <script type="text/javascript"> function scrollMessages(){ $('dasd').scrollTop = $('dasd').scrollHeight; } </script> |
That is all, just tree DIVs, the tag to initialize juggernaut and a simple javascript to scroll the messages DIV to the last sent message.
the messages DIV is named dasd because I was testing some conflicts and forgot to change it back
As seen in the page above, we need a login partial, and we’ll need a controls partial too, so let’s code the controls partial (app/views/chat/_controls.rhtml) with the following code:
1 2 3 4 5 6 | <% form_remote_tag( :url => { :action => :say }, :complete => "$('message').value = '';$('message').focus();" ) do %> <%= text_field_tag( 'message', '', { :size => 90, :id => 'message'} ) %> <%= submit_tag "Send" %> <% end %> |
It has only a remote form tag and two fields, after the form is submited the message field is cleared and the focus os placed back in that field so the user can type another message.
And the login partial (app/views/chat/_login.rhtml):
1 2 3 4 5 6 7 | <%= "#{@message}<br/>" if @message %><% form_remote_tag(
:url => { :action => :login },
:complete => "$('username').value = ''",
:after => "$('login').disabled = true" ) do %>
<%= text_field_tag( 'username', '', { :size => 90, :id => 'username'} ) %>
<%= submit_tag "Join", :id => 'login' %>
<% end %> |
Very similar to the controls partial, but it shows a message to the user if the chosen nick name is already taken.
the views are all set, and now we need the application logic, as seen in the views, we need a chat controller with two methods: login and say
Let’s take a look at the chat controller (app/controllers/chat_controller.rb):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | class ChatController < ApplicationController #this method does not need to exist, but I like to see it here, it only needs to render the index.rhtml view def index end def login #creates a new OnlineUser record, this is used to store who are the users that are online now @user = OnlineUser.new @user.username = Juggernaut.html_and_string_escape params[:username] @user.session_id = session.session_id @user.online = true @user.last_seen = Time.now #if we can save, it means that there is no other user with the same nick online, so this user can join the chat if @user.save #let's save the username in the session for future reference session[:username] = @user.username #if there are online users, fill the users box for the new user know who is online @users = OnlineUser.find(:all, :conditions => ["online = true and id != ?", @user.id]) if @users.size >0 data = render_to_string(:update) do |page| @users.each {|u| page.insert_html :bottom, :users_list, %Q{<li id="user_#{u.username}">#{u.username}</li>} } end #send the javascript only to the new user Juggernaut.send_to(@user.session_id, data) end #create a javascript call to add the new user to the end of the online users list data = render_to_string(:update) do |page| page.insert_html :bottom, :users_list, %Q{<li id="user_#{@user.username}">#{@user.username}</li>} page.insert_html :bottom, :dasd, "<b>user #{@user.username} just joined the chat</b><br/>" end #add the new user to the chat channel Juggernaut.add_channel(@user.session_id, 'chat') #send the javascript to all users in the chat channel Juggernaut.send_data(data, 'chat') render(:update) do |page| page.replace_html 'controls', :partial => "controls" end else @message = 'This nick name is already in use, please choose another' render(:update) do |page| page.replace_html 'controls', :partial => "login" end end end def say #escape the message, that way the user can not harm others sending HTML ot JavaScript commands message = "#{session[:username]}: #{Juggernaut.html_and_string_escape(params[:message])}" #create a javascript to add the new message to the end of the messages screen and scroll the div to the bottom data = render_to_string(:update) do |page| page.insert_html :bottom, :dasd, "#{message}<br/>" page.call "scrollMessages" end #send the message to all users Juggernaut.send_data(data, 'chat') render :nothing => true end end |
The method say is really simple, so we’ll start explaining it:
It first build a new message appending the escaped original message to the user name, then it creates the javascript to add the message to the bottom of the messages div using the Rails JavaScriptBuilder and the render_to_string method, that returns a string instead of rendering the code directly to the client.
then it sends the message to all users subscribed to the “chat” channel, and all the users will execute that javascript.
Now a little about the login method:
This is almost all logic needed for this chat application, the only missing thing is removing the nick of users that are no more online from the users DIV from the other users, and we’ll do that in the session controller (app/controllers/session_controller.rb)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class SessionController < ApplicationController #Called when a user disconnect (a refresh in the browser causes this to be called too) def logout #search for the user record using the session_id @u = OnlineUser.find_by_session_id(session.session_id) reset_session #if a user was found if @u username = @u.username #remove it from the database @u.destroy #remove from the online users list from all users, and tell others this user left the chat data = render_to_string(:update) do |page| page.remove "user_#{username}" page.insert_html :bottom, :dasd, "<b>User #{username} left the chat</b><br/>" end Juggernaut.send_data(data,'chat') end render :nothing => true end def login render :nothing => true end end |
The login method does nothing, but we have some code in the logout method …
That is all folks, we just need to run the application
to run this application we need to start the rails application server as usual, and then start the push server.
to do this, just run the following two commands:
and access your newly build chat with the URL: http://localhost:3000/chat and play a little around.
I’ve built this example while studding the juggernaut lib, so it is possible that there is a easy way to do this, but I think this is a good start point ![]()
The start idea was not to use a database, but I could not find anything like the ServletContext in java for rails (an application context), I’ll try to use ENV to store the online user names, but I could not make it work until now.
Any tips for improving this example will be very welcome.
If you enjoyed this post, make sure you subscribe to my RSS feed!
Tags: ajax, chat, example, flash, howto, push, rails, reverse, ruby, simple, xml
Some time ago I’ve got a really strange error message when trying to access a new method in a controller I have created for a rails application …
In the log the error message was:
“wrong number of arguments (2 for 0)”
the step by step to reproduce this error is:
What really happened?
ActionController has a method named process, and my controller:
class TestController < ApplicationController def process end end
defined a action with the same name, and it messed up with ActionPack …
Hasan had a very similar problem but with a reserved attribute of ActiveRecord
So, if you are having strange problems like these ones, try looking for a “reserved” attribute or method …
They are not really reserved, but, you have to pay attention when you redefine a “rails core” method ![]()
Even if you do not know that you are redefining the method
If you enjoyed this post, make sure you subscribe to my RSS feed!
Versão em portugues aqui
I think that everyone agrees that continuous integrationis a need for any and every software project, but the asynchronous integration largely used has some problems, like:
I’m not telling that you should no have a build server, you will have a build server to enable every involved on the project to get the latest version of the application, and to generate reports about the source code, these reports usually take a long time to generate and you there is no need for the developer to wait this generation to start the next task, but I do not think that the integration of the developers work are the responsibility of this server.
In other words, you do no need an integration server, just a build server if you are using synchronous integration!
The ideal is that during the commit process, the developer update his source code to the latest version, then run all tests and only if all tests passes he can commit the code to the repository.
Of course you can improve this process adding some restrictions, for example a minimum test coverage, but in my case, I wrote this Rake taks for a project where I’m working alone, and I’m using GIT for version control (I’ll write a post about GIT this week if I have the time), I’m using synchronous integration in some Java projects too, in other opportunity I’ll write about this process in java projects.
I’m adopting this synchronous integration practice for all new projects in my company and in every client that likes the idea.
Back to this post subject, it is really easy to implement synchronous integration in a Rails project, you just need to follow this two steps:
1 – create a ‘git.rake’ file in the directory lib/tasks with the following content:
namespace :git do desc "Update every thing before the tests" task :update do puts "Lets update it all, but we are using GIT so we already have the latest source for this repo" end desc "Run all tests, if all are OK, then commit every thing to the git local repository" task :commit => [:update, :test] do puts "No test failures, now we can commit it all" exec 'git commit -a' end end
As you can see, this Rake task is very simple, and I’m sure it will improve a lot your projects quality.
2 – at the and of each task, instead of running “git commit -a”, run the command: “rake git:update”
That is it! using that you are now using synchronous integration
Of course this Rake task can be improved, you can configure it to pull from a central repository, or update your source code if you are using subversion for example, but I think this one is already a good start point.
It all tests are OK, GIT will open VIM for the developer to write the commit message, and after that, the code will be committed with every thing working, and good bye integration problems!
And you, what do you think about synchronous versus asynchronous continuous integration? what approach do you think is best? why?
If you enjoyed this post, make sure you subscribe to my RSS feed!
Tags: integration, productivity, rails, rake, synchronous
The Ruby language, started to grow inside the enterprises and to be “the topic” in many blogs after the Rails framework showed up, but Rails is not the only option for developing web applications with Ruby, there are other frameworks, and one of “the others” is called “Nitro Framework“, thsi one has almost the same age as Rails but it has a lot less documentation and AFAIK a lot less users too.
This post is about my first 30 minutes with Nitro.
One of the better things about rails, and one of the things that you can not do with Nitro is just to change a class while the server is running, and see the change after a refresh in the browser window, but I’ll talk about these problems later …
The Nitro framework, goes through a path different from Rails, while Rails tell you where to put your models, where to put your controllers, where to put your views, …
What I think is a great feature, since when you start working in a new project you already know where every thing is, Nitro let you put your stuff just where you want it to be.
What is a nice feature too, but if I want to put things where I want them to be, I’ll use Java as I’m doing for the last 5 years
Nitro is almost a two framework in one, with Nitro you can develop MVC style applications like Rails, but you can write page oriented applications like in PHP or ASP too, and the best thing is that you can mix the two styles too, of course it will increase the complexity of your application, but it can make it a lot easier to work too, you can write what you need using the easier way to accomplish that task.
To start playing with Nitro just run the following commands:
Every thing ready, you are running your first Nitro application!
Until now, I think that nitro is more complicated than Rails, probably the lack of documentation contributed a lot to this, the terrible examples have their share in this responsibility too, but Nitro has some cool things too.
If you want to create a page based application, you just need to create some “.xhtml” files and place your Ruby code between <?r and ?>, or if you just want to display some string, do it just like in any Ruby String, placing your code between #{ … }.
Since nitro does not have a fixed directory structure, you need to “require” some of your Ruby files from inside the “run.rb” code, I think that this is the cause of the lack of automatic reloading of your code.
Nitro uses Og for persistence, and Og does not need your persistent objects to extend any class, it will persist any object that uses any of it’s “property definition helpers”, itlooks for an automatic created method called “serializable_attributes”, I think it is more the “ruby way”, since it uses “Duck Typing” instead of hierarchy.
Nitro does not have any thing like “migrations”, at least I did not find it yet, and I really like Rails Migrations.
I found the source code for Nitro and Og easier to read and understand than the Rails code, but the code for the Rails applications is a lot cleaner and easier to read.
One thing that I found cool about nitro is that they use the explicit parameters for almost all methods, when rails use hashes for almost every thing, both approaches have advantages …
The example I wanted to write for this post will be published another time, because I need to study a little more about Nitro and Og to develop any thing that I’m not ashamed of publishing.
They do not follow a standard directory structure even in the sample applications, in some of them the code is in the source directory, in others it is in the app directory, the only standard is that the public things (style sheets, images, …) are in a directory called public.
For now, they (Nitro and Og) looks like they need more work, they stopped in time for a while, and the development restarted now with the “Rails Boom“.
Nitro has some cool things, but at least for now, I’ll use Rails for my projects (at least the ones that aren’t in Java)
If you enjoyed this post, make sure you subscribe to my RSS feed!