Archive for category Tutorial

MacRuby tips: browse for folder or file dialog

This is yet another pretty simple tip.
Use case: let say you want your applications users to choose one or multiple files or folder on their file system. A good example would be that you want the user to choose a file to process or a folder where to save some data.

In the example above, I added a browse button and a text field.

I would like my users to click on the browse button, locate a folder and display it in the text field.

In your MacRuby controller, use a simple action method as well as an accessor to the text field:

1
2
3
4
attr_accessor :destination_path
 
def browse(sender)
end

Now, in Interface builder bind the destination_path outlet to the text field you want to use to display the path and bind the button to the browse action.

Let’s go back to our action method and let’s create a dialog panel, set some options and handle the user selection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def browse(sender)
  # Create the File Open Dialog class.
  dialog = NSOpenPanel.openPanel
  # Disable the selection of files in the dialog.
  dialog.canChooseFiles = false
  # Enable the selection of directories in the dialog.
  dialog.canChooseDirectories = true
  # Disable the selection of multiple items in the dialog.
  dialog.allowsMultipleSelection = false
 
  # Display the dialog and process the selected folder
  if dialog.runModalForDirectory(nil, file:nil) == NSOKButton
  # if we had a allowed for the selection of multiple items
  # we would have want to loop through the selection
    destination_path.stringValue = dialog.filenames.first
  end
end

That’s it, your user can now browse for a folder and the selection will be displayed in the text field. Look at the NSOpenPanel documentation for more details on the Cocoa API.

, ,

1 Comment

MacRuby tips: capturing keyboard events

If you are writing any type of games you might want your users to interact with your application using their keyboards.

This is actually not that hard. The approach is simple and fast forward if you are used to Cocoa.

Everything starts in Interface Builder, add a custom view instance to your window.

Now switch to your project and a new file with a class called KeyboardControlView and make in inherit from NSView. We are creating a subview of NSView so we will be able to make our top view “layer” use this subclass.

class KeyboardControlView < NSView
  attr_accessor :game_controller
 
  def acceptsFirstResponder
    true
  end
 
end

As you can see in the example above, I added an attribute accessor. attr_accessor class method creates getters and setters. It’s basically the same as writing:

 def game_controller=(value)
  @game_controller = value
end
 
def game_controller
  @game_controller
end

MacRuby is keeping an eye on these accessors and let bind outlets to them.
But let’s not get ahead of ourselves, we’ll keep that for another time.

Let’s go back to our newly created class. Notice, we also added a method called `
acceptsFirstResponder` and returns true. acceptsFirstResponder returns false by default.
But in this case we want it to return true so our new class instance can be first in the responder chain.

Now that our class is ready, let’s go back to Interface Builder, select our new custom view and click on the inspector button.


Click on the (i) icon and in the Class field choose our new KeyboardControlView.
Yep, our new class just shows up by magic, it’s also called the lrz effect, just don’t ask ;)
So now when our application starts, a new instance of our NSView class is created and Cocoa will call different methods based on events triggered.

The two methods we are interested in reimplementing are keyDown and keyUp. They get called when a key gets pressed or released.

def keyDown(event)
  characters = event.characters
  if characters.length == 1 && !event.isARepeat
    character = characters.characterAtIndex(0)
    if character == NSLeftArrowFunctionKey
      puts "LEFT pressed"
    elsif character == NSRightArrowFunctionKey
      puts "RIGHT pressed"
    elsif character == NSUpArrowFunctionKey
      puts "UP pressed"
    elsif character == NSDownArrowFunctionKey
      puts "DOWN pressed"
    end
  end
 super
end

I don’t think the code above needs much explanation. The only things that you might not understand are ‘event.isARepeat’. This method returns true if the user left his/her finger on the key. The other thing is the use of the ‘super’ call at the end of the method. Basically, we reopened a method that was already defined and we don’t want to just overwrite it, we just want to inject out code within the existing method, so once we are done handling the event, we just pass it back to original method.

Final result:

class KeyboardControlView < NSView
  attr_accessor :game_controller
 
  def acceptsFirstResponder
    true
  end
 
  def keyDown(event)
    characters = event.characters
    if characters.length == 1 && !event.isARepeat
      character = characters.characterAtIndex(0)
      if character == NSLeftArrowFunctionKey
        puts "LEFT pressed"
      elsif character == NSRightArrowFunctionKey
        puts "RIGHT pressed"
      elsif character == NSUpArrowFunctionKey
        puts "UP pressed"
      elsif character == NSDownArrowFunctionKey
  	puts "DOWN pressed"
      end
    end
    super
  end
 
  # Deals with keyboard keys being released
  def keyUp(event)
    characters = event.characters
    if characters.length == 1
      character = characters.characterAtIndex(0)
      if character == NSLeftArrowFunctionKey
       puts "LEFT released"
      elsif character == NSRightArrowFunctionKey
        puts "RIGHT released"
      elsif character == NSUpArrowFunctionKey
       puts "UP released"
      elsif character == NSDownArrowFunctionKey
        puts "DOWN released"
      end
    end
    super
  end
 
end

Now it’s up to you to handle the other keystrokes and do whatever you want. That’s it for this tip, I hope it helps.

No Comments

Ruby, Rack and CouchDB = lots of awesomeness

Over the weekend, I spent some time working on a Ruby + Rack +CouchDB project. Three technologies that I know quite well but that I never put to work together at the same time, at least not directly.  Let’s call this Part I.

Before we get started, let me introduce each component:

  • Ruby : if you are reading this blog, you more than likely know at least a little bit about, what I consider, one of the most enjoyable programming language out there. It’s also a very flexible language that lets us do some interesting things. I could have chosen Python to do the same project but that’s a whole different topic. For this project we will do something Ruby excels at: reopening existing classes and injecting more code.
  • Rack: a webserver interface written in Ruby and inspired by Python’s WSGI. Basically, it’s a defined API to interact between webservers and web frameworks. It’s used by most common Ruby web frameworks, from Sinatra to Rails (btw, Rails3 is going to be even more Rack-focused than it already is). So, very simply put, the webserver receives a request, passes it to Rack, that converts it, passes it to your web framework and the web framework sends a response in the expected format (more on Rack later).
  • CouchDB: Apache’s document-oriented database. RESTful API, schema-less, written in Erlang with built-in support for map/reduce. For this project, I’m using CouchRest, a Ruby wrapper for Couch.

Goal: Log Couch requests and analyze data

Let’s say we have a Rails, Sinatra or Merb application and we are using CouchRest (maybe we are using CouchRest and ActiveRecord, but let’s ignore that for now).

Everything works fine but we would like to profile our app a little and maybe optimize the DB usage. The default framework loggers don’t support Couch. The easy way would be to tail the Couch logs or look at the logs in CouchDBX. Now, while that works, we can’t really see what DB calls are made per action, so it makes any optimization work a bit tedious. (Note that Rails3 will have some better conventions for logging, making things even easier)

So, let’s see how to fix that. Let’s start by looking at Rack.

Rack Middleware

Instead of hacking a web framework specific solution, let’s use Rack. Rack is dead simple, you just need to write a class that has a call method.
In our case, we don’t care about modifying the response, we just want to instrument our app. We just want our middleware to be transparent and let our webserver deal with it normally.

Here we go … that wasn’t hard, was it? We keep the application reference in the @app variable when a new instance of the middleware is created. Then when the middleware is called, we just call the rest of the chain and pretend nothing happened.

As you can see, we just added some logging info around the request. Let’s do one better and save the logs in CouchDB:

Again, nothing complicated. In our rackup file we defined which Couch database to use and we passed it to our middleware (we change our initialize method signature to take the DB).
Finally, instead of printing out the logs, we are saving them to the database.

W00t! At this point all our requests have been saved in the DB with all the data there, ready to be manipulated by some map/reduce views we will write. For the record, you might want to use the bulk_save approach in CouchDB which will wait for X amount of records to save them in the DB all at once. Couch also let’s you send new documents, but only save it to the DB every X documents or X seconds.

As you can see, our document contains the timestamps and the full environment as a hash.

All of that is nice, but even though we get a lot of information, we could not actually see any of the DB calls made in each request. Let’s fix that and inject our logger in CouchRest (you could apply the same approach to any adapter).

Let’s reopen the HTTP Abstraction layer class used by CouchRest and inject some instrumentation:

Again, nothing fancy, we are just opening the module, reopening the methods and wrapping our code around the super call (for those who don’t know, super calls the original method).

This is all for Part I. In Part II, we’ll see how to process the logs and make all that data useful.

By the way, if you make it to RailsSummit, I will be giving a talk on Rails3 and the new exciting stuff you will be able to do including Rack based stuff, CouchDB, MongoDB, new DataMapper etc..

, , , , ,

3 Comments

Get on Merb Edge pre 1.0

Merb 1.0 is almost ready to be pushed out and you might be impatient to start playing with some of the goodies not yet available in the latest stable release. Before getting started, you should know that not everything has been ironed out yet so don’t expect to have a fully stable Edge.

The easiest way to get started requires that you have git installed as well as a gem called thor.

I let you take care of installing git on your machine, Ruby dev without git became quite challenging since GitHub started ruling the Ruby OSS world.

sudo gem install wycats-thor -s http://gems.github.com
Hops, used primarily as a flavoring and stability agent in beer, and also in other beverages and in herbal medicine.

Hops, used primarily as a flavoring and stability agent in beer, and also in other beverages and in herbal medicine.

Thor is a sort if mix between rake, sake with a better argument parser and based on Ruby classes.

Thor on its own won’t be very helpful, we need some thor tasks.

Create a folder where you want to store Merb’s source code and cd in it.

Once there download the latest merb thor tasks:

curl -L http://merbivore.com/merb.thor > merb.thor

You can now look at the available task by doing

thor -T

Read the rest of this entry »

, , ,

6 Comments

problems with urls in Merb HEAD?

I actually run into a small problem when updated an older Merb app. Here was how my router looked like:

Merb::Router.prepare do |r|
  r.resources :channels do |channels|
    channels.resources :shows do |shows|
      shows.resources :episodes
    end
  end
end

But after updating to the latest version of Merb, I got links looking like:

http://localhost:4000/channels/#<Channel:0x27b7300>/shows

The first thing to do is to read Carl’s wiki about the latest Router changes.

Carl explains that things got cleaned up in the router code and my routes should now look like:

Merb::Router.prepare do |r|
  r.resources :channels do
    resources :shows do |shows|
      resources :episodes
    end
  end
end

However that won’t be enough.. You see my url used to look like that:

url(:channel_shows, :channel_id =&gt; channel)

Now I can simplify it to:

url(:channel_shows, channel)

That still won’t fix the problem, since the real problem comes from the fact that I was on Merb HEAD but not DataMapper HEAD. Updating DM clears things up. That’s the price to pay to be on HEAD ;)

FYI the problem comes from the fact that DM doesn’t add a to_params method to its objects. Rails users might recognize that method used to convert an object into a string to create a route, something not really ORM agnostic and frowned upon by the DM/Merb teams.

Merb lets you specify the param to use for your routes using the identify method. Read Carl’s wiki page for more cool stuff and see how to create some cool stuff like url slugs etc..

Note that even if you are using ActiveRecord, you’ll need to update merb_activerecord as the new identify rules were updated in the ORM plugins.

, ,

No Comments

Write your own custom DataMapper adapter

If you read this blog, you probably know that Merb‘s best ORM friend is DataMapper.

compounds in basil oil have potent antioxidant and is used for supplementary treatment of stress

compounds in basil oil have potent antioxidant and is used for supplementary treatment of stress

Merb works very well with ActiveRecord and Sequel but most of the Merbivores get excited about DataMapper.

DataMapper has a lot of cool stuff going for it. I’m planning on writingi few articles about what I particularily like with DM and some of the misconceptions.

I’m going to give a talk about DataMapper during MerbCamp and something I want to cover is the fact that you can write DM adapters for virtually anything. From an adapter for couchdb (available in dm-more) to an adapter for SalesForce API. That’s the kind of stuff that gets me excited, a bit like what Ambition does but built-in in DM.

So, I decided to take some advise from Yehuda and dkubb and wrote my own adapter for Google Video. I had just finished a gem to retrieve google videos for a given google user and thought it would be a perfect exercise to mix a http-scraper with a DM adapter.

Read the rest of this entry »

, , ,

6 Comments

Deploying a bundled merb app (merb 0.9.7+)

Since Merb 0.9.7 the Merb team decided to change the way you can bundle an app. Until 0.9.7 you would use the merb-freezer plugin which was supporting git submodules and gems. The only problem was that you still had to install merb-freezer on your server and it had to stay in sync with your app… kinda lame :(

Ginger

Ginger roots, great for deployment issues

Instead, after a lot of discussions, we decided to add this feature to merb-core and let you bundle all your dependencies in a bundled gem folder. No more support for git submodules as they are hard to keep track of and don’t handle dependencies very well (not at all).

The new freezing strategy is very well described in this merbunity article. However it doesn’t really explain how to deploy a bundled app.

So let’s imagine for a second that we bundle our app, generated the scripts needed to start merb/rake etc…

Read the rest of this entry »

, , , , ,

8 Comments