msgbartop
Just a little about development! Think before you write!
msgbarbottom

09 Jun 08 GEdit plugin formatting Ruby and ERB code now!

I have published some time ago a GEdit plugin for formatting Ruby code.

Now I just updated the plugin and added support for formatting html.erb and .xml.erb files too.

The main idea  is to make this plugin a generic language code formatter for GEdit, contributions are welcome.

As told before, to install it, grab the source from GitHub and copy all files to ~/.gnome2/gedit/plugins.

To download the files using Git use the following command line:

git clone git://github.com/urubatan/gedit_formatter.git

If you want to contribute just send a pull request in GitHub or send me a patch by e-mail.

To use the plugin, just press CTRL+ALT+F after installing and enabling it.

If you do not have git installed, you can download a tarbal with the latest sources from this link.

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , ,

04 May 08 GEdit plugin for formatting ruby code


I work with Java for a long time, and I’m used to Eclipse facilities, but to work with ruby on rails I think an IDE is not needed, I work better with GEdit (or even VIM, depends on my humor) than ith NetBeans or Aptana.
But there are some features of an IDE that I miss when working with GEdit, for example code formatting.
As a heavy eclipse user, I prefer to let eclipse take care of the indentation of my source files …
So I have created this GEdit plugin to format ruby files, if you want to use it, just checkout the source code from GitHub and copy all files to ~/.gnome2/gedit/plugins, restart gedit and you are ready to go …
After reopening GEdit, click Edit\Preferences\plugins and enable the “Code Formatter” plugin.
then, when you are editing a ruby file, just click CTRL+ALT+F and the code will be formatted for you.
if you are more a mouse gui, just click Tools\Format Code.

I hope this plugin can be useful for others too.
I plan to add support for more languages, for example XHTML and RHTML (ERB + XHTML), I know tidy plugin can do this, but I do not like the default tidy configuration, but will probably use tidy as a backend to this feature :D

to checkout the code, you can download the zip file available in the project page, or use git:

git clone git://github.com/urubatan/gedit_formatter.git

Any doubts, problems or tips for improvements just leave a comment, here or in github.

PS.: this was my first GEdit plugin, and my first experience with python, the code can be improved a lot, any comments about that are very welcome.

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , ,

01 May 08 Dirty models and partial SQL updates for Rails 2.0.2

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 :D
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 :D

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , , , ,

28 Apr 08 Lazy men’s rails plugin – mydry

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 =&gt; 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 =&gt; true
  validates_length_of :name, :maximum =&gt; 255, :allow_nil =&gt; true
  validates_associated :example, :allow_nil =&gt; 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 :D

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , , , , ,

05 Mar 08 A better “try()” for Ruby, why not do the Groovy way?

The new method in Ruby 1.9 is making some people happy and creative too.
The only problem with that in my opinion is that every one is fixed about using method chaining with the name “try” with a symbol as a parameter to escape from the:

@person.name unless @person.nil?

or

@person ? @person.name : nil

and for some strange reason they think it is natural to write:

@person.try(:name)

when I see the sentence above, the first thing I want to “try” is:

@person.try(:to_swim)

Ok, the last comment was not cool, but I thought it would be funny :D

So, what is this post about?

In Groovy there is a built’in language construct to solve this situation in a very cool way:

person?.name

The method call after the “?” is only called if the previous expression was not nil.

I could not think in a way to implement any thing like that in ruby, but I think I came out with a satisfactory solution.
What do you think about flagging your method calls with “I know this can be null, but ignore it, it is a normal situation”?
No, I’m not telling you to write all this sentence in your code :D
the flag I’m using now is the “_” character.

I’m monkey patching ruby like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class NilClass
   alias :old_method_missing :method_missing
   def method_missing(methodname,*args)
	  old_method_missing methodname, *args unless methodname.to_s =~ /.*_$/
   end
end
class Object
   alias :old_method_missing :method_missing
   def method_missing(methodname,*args)
      methodname = (methodname.to_s.gsub(/(.*)_$/,'\1')).to_sym
	  if respond_to? methodname
	     send methodname,*args
	  else
	     old_method_missing methodname, *args 
	  end
   end
end

and you can use it like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#test classes
class Named
	attr_accessor :name
	def initialize(name)
		@name = name
	end
end
class Person < Named
	attr_accessor :name, :company
	def initialize(name,company=nil)
		super(name)
		@company = company
	end
end
#Testing starts here
def run_test
	puts @person.name_
	puts @person.company_.name_
end
@person = Person.new('Urubatan',Named.new('The Best'))
run_test
@person = nil
run_test


With this little method missing hack, we just put an “_” in the end of the method name we want to call and if the target object is nil the method will not be called and we will not get a NoMethodError.
Better yet, we can even pass arguments to the method we want to call :D

This way, we can use this solution only in situations we know it is safe.
What do you think about this?
I prefer the groovy solution, but I kine this one better than the “try” method :D

<updated>
Added one more link to the list in the beggining of the post (thanks coderr)
</updated>

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , , ,

24 Jan 08 Writing rails plugins – tips and tricks – plugins with generators

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[bb] in two minutes …

Generators are a way to, generate code :D

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[bb] 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 :D )

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:

  • my_generator
    • lib
    • tasks
    • test
    • generators
      • test_gen
        • templates

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:

  • RAILS_ROOT/lib/generators
  • RAILS_ROOT/vendor/generators
  • RAILS_ROOT/vendor/plugins/plugin_name/generators
  • USER_HOME/.rails/generators
  • gems ending in _generator

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:

  • class_name -> AsdasDasda
  • class_nesting ->
  • class_nesting_depth -> 0
  • class_path ->
  • file_path -> asdas_dasda
  • name -> asdas_dasda
  • plural_name -> asdas_dasdas
  • singular_name -> asdas_dasda
  • table_name -> asdas_dasdas
  • attributes -> #<Rails::Generator::GeneratedAttribute:0×3716418>
  • args -> asd:ash

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:

  • class_collisions – Check whether the given class names are already taken by Ruby or Rails. In the future, expand to check other namespaces such as the rest of the user‘s app.
  • directory – Create a directory including any missing parent directories. Always directories which exist.
  • file – Copy a file from source to destination with collision checking.
  • identical? – Checks if the source and the destination file are identical. If passed a block then the source file is a template that needs to first be evaluated before being compared to the destination.
  • migration_template – When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template.
  • readme – Display a README.
  • route_resources – add a route to the routes.rb
  • template – Generate a file for a Rails application using an ERuby template. Looks up and evaluates a template by name and writes the result.

I think that is it, you can ask more questions if you want, I’ll try to answer all, if any :D

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 :D

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , , , ,

15 Jan 08 Writing rails plugins – tips and tricks – plugins with view helpers

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:

  1. rails plugins101
  2. cd plugins101
  3. ./script/generate scaffold example name:string url:string

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 :D
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. ./script/generate plugin life_saver
  2. Edit the file vendor/plugins/life_saver/lib/life_saver.rb and put the following content in it:
    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
  3. Edit the file vendor/plugins/life_saver/init.rb
    1
    2
    
    # Include hook code here
    ActionView::Base.send :include, LifeSaver
  4. Remove the method from the application_helper.rb

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 :D

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 :D

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , , , ,

07 Dec 07 Ruby on Rails 2.0 is out!

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 :D

  • Beter named routes for REST
  • REST is the standard way for working from now on.
  • Rails has support for multiple rendering engines now (for 1.x there was only ERB)
  • Better exception handling
  • Sessions can now be stored in cookies
  • sexy_migrations was integrated into rails core (most of it at least)

Take a look at DHH’s official post and start to play :D

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , ,

24 Oct 07 A cool chat example created with Ruby On Rails and Juggernaut

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 …

  • gem install -y json eventmachine

Ok, now we are ready to go!
First of all, create a rails application, and install the Juggernaut plugin with this commands:

  • rails -d sqlite3 chattest
  • cd chattest
  • script/plugin install svn://rubyforge.org//var/svn/juggernaut/trunk/juggernaut

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 :D ) let’s generate the needed files and database tables with the following four commands.

  • script/generate model online_user username:string session_id:string last_seen:date online:boolean
  • script/generate controller session
  • script/generate controller chat index
  • rake db:migrate

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 :D

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:

  • from the line 7 to 11 we just configure the new OnlineUser model.
  • if the user is saved to the database it means that there is no other user with the same nick name, so we can proceed with the logic
  • from line 17 to 23 we just create a javascript to populate the users DIV for the new user with the users already online
  • and in the line 25 we send this javascript just for the new user.
  • from line 28 to 31 we create a javascript adding the new user to the users DIV and add a message to the messages window telling all users that a new user joined the chat.
  • in the line 33 we add the new user to the “chat” channel
  • in the line 35 the generated javascript is sent to all users (now including the new arriver)
  • and in from the line 36 to 38 the traditional rails ajax helper is used to change the content of the controls DIV to the “controls” partial (the one that enables the user to send messages to other users.

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 …

  • in the line 5 we lookup the user record from the database
  • if a user is found
  • we destroy it in the line 8
  • from line 13 to 17 we create a javascript to remove the user from the users DIV and send a message to all users telling that the user has left the chat

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:

  • script/server
  • script/push_server

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 :D
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: , , , , , , , , , ,

22 Oct 07 Rails gotchas (reserved attribute or method name)

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:

  • rails test
  • cd test
  • script/generate controller test process
  • script/server
  • point your browser to http://localhost:3000/test/process

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 :D
Even if you do not know that you are redefining the method :D

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags: , , ,