One of the many possible ways of iterating over an array in Flex is:
1 2 3 4 5 | private function iterateOverArray(arr : Array) : void { for(var i : int = 0; i < arr.length; i++){ doSomethingWith(arr[i]); } } |
But this code will cause problems if the Array is null, to avoid this kind of problems you can use the following code instead:
1 2 3 4 5 | private function iterateOverArray(arr : Array) : void { for each(var o : Object in arr){ doSomethingWith(o); } } |
This way we do not need two variable declarations, and we do not try to read the length of a possible null variable.
If you enjoyed this post, make sure you subscribe to my RSS feed!
Many of you will probably not agree with me, but if you read all this post, you will probably agree that commenting your source code is a sign that your code sucks. Real men write code that other people can read!
But keep in mind that I’m talking about comments that tell what the code do, and not documentation like “Javadoc”.
For example, if you ware not a Java programmer, or even being a java programmer, can you quickly tell what the code bellow do?
1 2 3 4 5 6 7 8 9 10 | public String write(StringBuilder fle, StringBuffer con) { File f = new File(fle.toString()); FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); String lin; while((lin=br.readLine())!=null){ con.append(lin).append("\n"); } return con.toString(); } |
Hard one? And this is a very simple code, but we can make it a little better …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public String read(StringBuilder fle, StringBuffer con) { //Opens the file with the name container in the fle parameter File f = new File(fle.toString()); //Create a file reader, then a buffered reader to make our work easier FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); String lin; //Read each line of the file until it is null while((lin=br.readLine())!=null){ //Put the content read into the buffer pointed by the parameter "con" con.append(lin).append("\n"); } //The caller already have the content, because he created the buffer, but I'll return the string anyway return con.toString(); } |
Easier, right? Now you can read the comments in the code, and understand it, but the code still crappy.
This is the kind of comment I’m talking about. Commenting your code is a sign that you are a very bad programmer (with little exceptions, in very complex algorithms that you can not break in smaller pieces of code).
When you do this, you are doing a “hack” to hide from others your own incompetence. This mean that you cannot write a clearer code. One that other programmers can read.
Here we have a little solution for the code above, a refactoring of that crappy method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public String readFileContents(File fileToRead) { boolean canReadFile = fileToRead.exists(); if(!canReadFile) return ""; StringBuilder buffer = new StringBuilder(); BufferedReader readerForFile = openBufferedReaderForFile(fileToRead); readFileContetIntoBuffer(buffer,readerForFile); closeFileReader(readerForFile); return buffer.toString(); } private BufferedReader openBufferedReaderForFile(File fileToRead){ return new BufferedReader(new FileReader(fileToRead)); } private void readFileContetIntoBuffer(buffer,readerForFile){ String line; while((line=readerForFile.readLine())!=null){ buffer.append(line).append("\n"); } } private void closeFileReader(readerForFile){ readerForFile.close(); } |
Now if you pay attention to the method name “readFileContents”, you can already know what it does, besides that, the method code is almost legible, as you can see bellow:
if not can read file, return null
open Buffered Reader For File: fileToRead
read File Contet Into Buffer: buffer, readerForFile
close File Reader: readerForFile
return buffer.toString();
What I’m telling is that if you can read english, you can read the code, and almost every programmer in the planet knows how to read english, right?
I have even seen people doing worst things, like the snippet bellow:
1 2 3 4 5 | public String write(StringBuilder fle, StringBuffer con) { File f = new File(fle.toString()); FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); String lin; while((lin=br.readLine())!=null){ con.append(lin).append("\n"); } return con.toString(); } |
It has less lines of code than my version, but you will have a hard time trying to read a program wrote by the monster that wrote the snippet above.
Of course the example I used was a simple one, and I know that writing legible code needs practice …
But I’ll let you a proposal, not really a proposal, an exercise …
I’ll let a code example bellow, and you will try to make it more readable. And I’ll post my solution in one or two days.
If you want to post the refactored code in the comments, just use the tags <pre lang=”java” line=”1″> … </pre>
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 | package blog; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class VeryBadlyNamedFile { private static final char[] asdfg = new char[] {'I', ' ', 'c', 'a', 'n', ' ', 'd', 'o', ' ', 'v', 'e', 'r', 'y', ' ', 'u', 'g', 'l', 'y', ' ', 'c', 'o', 'd', 'e'}; private String an; private BufferedReader rfsdw; private FileReader temp; public VeryBadlyNamedFile(String an, BufferedReader rfsdw, FileReader temp) { super(); this.an = an; this.rfsdw = rfsdw; this.temp = temp; } public void doIt() throws IOException { ctfiidne(); startDoing(); try { canIDoAnyThing(); } catch (RuntimeException yicdet) { nowReallyDoIt(); } } private void nowReallyDoIt() { firstDoTheOtherThing(); reallyDoItInternal(); } private void firstDoTheOtherThing() { rfsdw = new BufferedReader(temp); } private void reallyDoItInternal() { while (true) { try { imDoingIt(); } catch (Exception e) { break; } } } private void imDoingIt() throws Exception { String s = rfsdw.readLine(); if (s == null) throw new Exception("hahaha, I bet you did not understood the code"); System.out.println(s); } private void ctfiidne() throws IOException { File a = new File(an); if (!a.exists()) { FileWriter wrfedsd = new FileWriter(a); wrfedsd.write(asdfg); wrfedsd.close(); } } private void canIDoAnyThing() { if (new File(an).exists() && new File(an).canRead() && new File(an).canWrite()) throw new RuntimeException(); } private void startDoing() throws FileNotFoundException { File f = new File(an); temp = new FileReader(f); } } |
And now a “main” file to exebute the trash above.
1 2 3 4 5 6 7 8 9 | package blog; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { new VeryBadlyNamedFile("c:\\anyFile.any",null,null).doIt(); } } |
The examples in this post are using Java, but the concept apply to any language, if you are programming and using comments to explain your code, you are a terrible programmer. Just pray for god’s help, and beg him that the next one to read the crap you are creating is not a serial killer that knows where you live.
PS.: I really do not think that you should not comment your code, but if your code is not understandable without the comments, you do not know how to write programs yet.
PS2.: I think I need better examples.
PS3.: the secret to write good code is to write it as if the next one that will read your code is a serial killer that knows where you live. (I do not remember who wrote this sentence in the first place, I’ll research and post it here)
If you enjoyed this post, make sure you subscribe to my RSS feed!
As you can see here, Adobe created a big problem (at least for me) after solving a memory leak problem.
But thanks to Scott Melby at this post, now I have a medicine to the Alzheimer of the PopUpButtons in mu Applications, and I called this medicine “PopUpMenuGuardian”.
The problem is that the PupUpButton stops poping up after a screen resize for example.
The exact proposed solution was not good enough for me because I have many PopUpButtons, so, I created this helper class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import flash.events.Event; import mx.controls.Menu; import mx.controls.PopUpButton; public class PopUpMenuGuardian { private var rbtn : PopUpButton; private var mnu : Menu; public function PopUpMenuGuardian(rbtn : PopUpButton) { this.mnu = rbtn.popUp as Menu; this.rbtn = rbtn; rbtn.addEventListener(Event.ADDED_TO_STAGE,restoreMenu); } public function restoreMenu(evt:Event):void{ rbtn.popUp = mnu; } } |
And now I just need to create one of this objects after I create each popupbutton
I think Adobe should fix this ASAP, but at least I have a workaround now.
PS.: I know I’m a looooong time without posting, but I just finished writing my RoR book, I’ll probably post more often now.
If you enjoyed this post, make sure you subscribe to my RSS feed!
Tags: bug, flex, workaround
I have worked with C++ in my early days of programming (between 1997 and 2000), but I used to work with Borland C++ Builder and Microsoft Visual C++, in that time I have not heard about unit tests yet, after that I worked with Delphi, PHP, ASP, ColdFusion, …
Since 2002 I worked most of the time with Java, and learned a lot since then, lots of good practices, a lot more about object orientation and I learned to love unit tests.
Little time ago I starter working with C++ again, but I’m already in love with unit tests, and want to do them for my C++ code too, and this post is a very short example of how a Java programmer can work with C++ and do unit tests.
A C++ project starts with a Makefile, I’m used with ANT and do not like the idea of enumerating all my source files in a build configuration file like most examples of Makefiles do, so I created a simple but very flexible Makefile for my project.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | TESTDIRECTORIES := test DIRECTORIES := src SOURCES := $(foreach dir,$(DIRECTORIES),$(wildcard $(dir)/*.cpp)) TESTSOURCES := $(foreach dir,$(TESTDIRECTORIES),$(wildcard $(dir)/*.cpp)) OBJECTS := $(patsubst %.cpp,%.obj,$(SOURCES)) TESTOBJECTS := $(patsubst %.cpp,%.obj,$(TESTSOURCES)) TESTOBJECTS += $(filter-out src/main.obj,$(OBJECTS)) TARGET := example LINK := g++ CC := g++ CFLAGS := -c LFLAGS := all: $(OBJECTS) $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) test: $(TESTOBJECTS) $(LINK) $(LFLAGS) -lcppunit -o $(TARGET)_unit $(TESTOBJECTS) ./$(TARGET)_unit %.obj:%.cpp $(CC) $(CFLAGS) -o $*.obj $*.cpp |
With this make file, all cpp files in the src directory will be part of the target executable, more directories can be added just enumerating the source directories in the “DIRECTORIES” variable, the same for the test code, under the test directory and the “TESTDIRECTORIES” variable.
The trick here is the combination of the functions foreach and wildcard, the function pathsubst is used to change the extensions from .cpp to .obj and the filter-out function is used to remove the main.cpp (the entry point for the main application) from the test objects.
This Makefile is the nearest I could get from ANT functionality for C++ programming, but of course it can be improved a lot.
But the Makefile is not the target of this post, I’m writing this post to tell you about CppUnit, a great unit test framework for C++.
I started writing this unit test runner using CppUnit:
testRunner.hpp
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 | #include <cppunit/CompilerOutputter.h> #include <cppunit/extensions/TestFactoryRegistry.h> #include <cppunit/TestResult.h> #include <cppunit/TestResultCollector.h> #include <cppunit/TestRunner.h> #include <cppunit/BriefTestProgressListener.h> int main (int argc, char* argv[]) { // informs test-listener about testresults CPPUNIT_NS :: TestResult testresult; // register listener for collecting the test-results CPPUNIT_NS :: TestResultCollector collectedresults; testresult.addListener (&collectedresults); // register listener for per-test progress output CPPUNIT_NS :: BriefTestProgressListener progress; testresult.addListener (&progress); // insert test-suite at test-runner by registry CPPUNIT_NS :: TestRunner testrunner; testrunner.addTest (CPPUNIT_NS :: TestFactoryRegistry :: getRegistry ().makeTest ()); testrunner.run (testresult); // output results in compiler-format CPPUNIT_NS :: CompilerOutputter compileroutputter (&collectedresults, std::cerr); compileroutputter.write (); // return 0 if tests were successful return collectedresults.wasSuccessful () ? 0 : 1; } |
CppUnit is very flexible, allowing lots of outputs for the tests, I’ll write another post about this soon, but the idea of this runner is to use the CppUnit test registry, what makes a lot easier to work, because you can forget about the runner, just write your tests and register it.
It is almost like the fileset you pass to the junit task of ANT but written in C++.
After the test runner is ready, you can start writing your tests.
C++ different from Java, you use 2 files for each class, one header and one implementation file.
Let’s start with the header …
mainTest.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #ifndef MAINTEST_H #define MAINTEST_H #include <cppunit/TestFixture.h> #include <cppunit/extensions/HelperMacros.h> #include "../src/HelloWorld.hpp" using namespace std; class MainTest : public CPPUNIT_NS :: TestFixture { CPPUNIT_TEST_SUITE (MainTest); CPPUNIT_TEST (testHello); CPPUNIT_TEST_SUITE_END (); public: void setUp (void); void tearDown (void); void testHello (void); private: HelloWorld *hello; }; CPPUNIT_TEST_SUITE_REGISTRATION (MainTest); #endif |
In this header we have a simple class declaration, extending TestFixture from the cpp unit namespace.
C++ is a static language without reflection, because of that CppUnit has the macros you can see in the top of the class declaration, you need one CPPUNIT_TEST line for each test method you write.
Ant the line: CPPUNIT_TEST_SUITE_REGISTRATION (MainTest);
Does the magic of test auto registration.
After this you can write your test as you do in Java:
mainTest.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include "mainTest.hpp" void MainTest::setUp(){ hello = new HelloWorld("Test"); } void MainTest::tearDown(){ delete hello; } void MainTest::testHello(){ string expected("Hello Test\n"); CPPUNIT_ASSERT_EQUAL(expected,hello->sayHello()); } |
As in JUnit we have a setUp and a tearDown method, this methods does exactly the same thing, they initialize/uninitialize the variables you will use in your test code, and the “testHello” method is our test.
the assertions in CppUnit are done using macros.
These are the available assertions:
A lot less assertion types than junit, but enough for working.
After the test done, now we just need to write our code!
HelloWord.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <stdio.h> #include <iostream> #ifndef MAIN_HPP #define MAINHPP class HelloWorld{ private: std::string name; public: HelloWorld(char* name); std::string sayHello(); }; #endif |
and the implementation:
HelloWord.cpp
1 2 3 4 5 6 7 8 9 10 11 | #include "HelloWorld.hpp" HelloWorld::HelloWorld(char* name){ this->name = name; } std::string HelloWorld::sayHello(){ std::string result("Hello "); result = result + name + "\n"; return result; } |
To run the tests, you just need to type in the console:
make test
With all tests written and passing, the last step is the application runner (like the test runner).
main.cpp
1 2 3 4 5 6 7 8 | #include "HelloWorld.hpp" int main(int argc, char** argv){ if (argc >= 2) { HelloWorld* hello = new HelloWorld(argv[1]); std::cout << hello->sayHello(); delete hello; } } |
And you have your first test driven C++ application up and running!
PS.: if you are using my makefile, remember that application code is placed into src folder and test code is placed into test folder.
PS2.: the example was tested on linux with cpp unit installed using the package manager, if you want to install cpp unit from source, remember to update CFLAGS with the include path and LFLAGS with the library path. and if you are not using g++ as the compiler and linker, change the CC and LINKER variables with appropriated values.
If you enjoyed this post, make sure you subscribe to my RSS feed!
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!
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!

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
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!
Have you ever read about easyb? and about Grails? May be you answer yes to one or both previous questions, but this is the first time you will find them together I guarantee, at least in a plugin form.
This is the first post I’m writing about my latest toy project (at least for now it is only a toy project).
The project is hosted at GitHub, and any help will be very welcome since I do not work every day with Grails, but I did a presentation about grails in the last weekend and wanted to use BDD instead of the traditional GUnit based unit tests, I already use Easyb for some of my Java projects, then I wanted to make Easyb and Grails to work together.
It was not an easy task, because of some craziness of Grails class loader, but it is working fine now.
You can write a test with the easyb sintaxe, and the test will look line this:
scenario "Project creation", { given "A new project", { project = new Project() } when "the project name is supplied", { project.name = 'My test project' } and "there is no other project with that name", { validateproject = { project.validate().shouldBe true } } then "a new project must be created", { validateproject() project.save(flush:true).shouldNotBe null } } scenario "User association with a project", { given "Any existing project", { project = Project.findAll()[0] } when "a list of users is supplied", { users = [new User(login:'testUser1',password:'testUser1',email:'test1@test.com').save(),new User(login:'testUser2',password:'testUser2',email:'test2@test.com').save()] } then "the users must be associated with that project", { users.each{ ProjectMembership.link(it,project) } } }
You can even show it to your boss, because the output of this test is some thing like this:
Story: project management
scenario Project creation
given A new project
when the project name is supplied
when there is no other project with that name
then a new project must be createdscenario User association with a project
given Any existing project
when a list of users is supplied
then the users must be associated with that project
And the best part of it, is that you can use your tests as the specifications of your next grails project!
If you want to know more about the easyb sintax, take a look at the project site.
If you want to know more about grails, take a look at the project site.
If you want a new functionality in this plugin, I’ll probably accept all patches and push requests you send-me ![]()
And finally, if you want to install the plugin, download it from here and run:
grails install-plugin grails-easybtest-latest.zip
You will need to place the tests you write in the folder tests/behavior
And to run the tests, just type: grails easyb-test
Any doubts or suggestions leave a comment here ![]()
I hope it can be useful to others.
If you enjoyed this post, make sure you subscribe to my RSS feed!
Tags: easyb, easyb-test, grails, petproject, plugin, test
Google has launched a very cool application server, it is called Google App Engine.
Kudos to google again
But this application server started a big war on the internet, lots and lots of developers around the world, are very sad because the only supported language in the application server is Python.
Google App Engine has free accounts with limitations, like heroku for example, which is a great application server that supports only Ruby On Rails.
So, what is the problem?
The big problem is that because App Engine is from Google, most of the developers that haven’t not learned Python yet are yelling around because Google App Engine does not support their language.
But the real problem here is that these developers do not understand that a programming language is only a tool you need for work.
Most of the money I get every month is from Java programming, but I have to use many other languages, like Ruby, Bash, ActionScript/Flex, Java, C++, Object Pascal/Delphi, …
<opinion>
If you think yourself as a developer and you can write programs using only one language, you is probably a very bad developer.
</opinion>
I think this bunch of “why did you choose python instead of my beloved Java/Ruby/Perl” is just because it is from google, and everyone wants some google love.
Why no one yells about Heroku supporting only Ruby On Rails? Not even the Merb programmers …
But if google created Heroku (almost it did now), every one would complain about the chosen language.
I liked the idea of Google App Engine, probably this was the push I needed to learn Python (I never liked it, and never had any good reason for that, I just do not want the idea of spaces defining code blocks
), the only boring thing is all this yelling about the supported language.
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!