RQuery: refactored and simplified

I’ve gone back and knocked off a couple of big todos in rquery. Namely, removing the Symbol capability, and using instances of the new OperationCollector in place of the global class variable + mutex required by the Symbol alterations. Also, as a consequence, I was able to remove the Declarations methods ( .is, .is_not ) previously required for use with overloaded operators like ==.

No More Symbols

Previously there were two DSLs coexisting in the library. One opened up the Symbol class and added the declaration methods.

User.where { :age.is == 12 }

This, in my opinion, was an interesting hack and fun to implement but required both global storage for the results of the executing statements and the pollution of the Symbol class. The library’s better half used a block parameter that represented a collection of the attributes provided by ActiveRecord.

User.where { |user| user.age.is == 12 }

In removing support for the Symbol based syntax, the block based version became much cleaner both externally and internally. Externally there’s no longer a need for the .is and .is_not declarations while the from, between, in and contains methods remain the same.

User.where do |user|
  user.age == 12
  user.name.contains "George"
  user.weight.between 160..180
end

Though, because of Ruby’s restriction on overloading the != operator it now looks like:

User.where do |user|
  user.age.not = 12
end

Simple Internals

The internals have been simplified as well. At a glance, there is now a single class that handles the organization of the calls to the adapter for serialization, OperationCollector. In addition every time the where method is called, a new instance of this class is created so there’s no need to have the mutex to prevent race conditions. Please take a look at the newness for more details.

Unraverl: Partial Function Application

What is it?

Last weekend I hacked partial application into my parse transform for erlang, unraverl. The rules are very simple but the implications are interesting if nothing else.

In other functional languages, haskell as an example, there’s nothing inherently wrong with defining a function that requires two arguments but giving it one.

add x y = x + y
example = add 1
{- => \y -> 1 + y -}

The result is a lambda where the supplied argument is applied across the code in the function.

Unraverl’s implementation?

Using the initial example in Erlang

add(x, y) -> x + y.

example() -> add(1).

the parse transform sees that there are no local (only) functions named ‘add’ with an arity of 1. It then finds the next highest arity definition and changes the call to accommodate

%% roughly
example() -> fun(y) -> add(1, y) end.

So while haskell applies whatever arguments are available to the actual function (internally I’m not sure how its implemented), unraverl simply wraps a call to the actual function in a new fun requiring the remaining arguments.

To make use of this feature in your own applications simply add the compiler directive

-compile([{parse_transform, unraverl}]). 

and make sure the library is in the code path.

As yet, it doesn’t have a makefile or unit tests so use at your own peril.

Where to from here…

Been mulling over my next project after I finish a small Rails app for my friend. I’m not sure I’ll have the time in the near future but here are the ideas that are competing in my head for… well literally… mind share.

Both use Erlang. Unraverl is the beginnings of a meta programming/parse transform library for Erlang. At this point it only implements before/after filters with method chains. Grove is a JSON Query to set comprehension library for use with Mnesia.

  • Unraverl
    1. add partial application
    2. runtime function definition from funs
    3. add pure/impure designation attribute/checking
  • Grove
    1. Make use of MochiWeb
    2. Rework the JSON query structure
    3. Move JSON Query to Set Comprehension logic out into separate module/project

Clearly Unraverl is the most flashy, and likely the most interesting bit of hacking, but whether it becomes useful later or not remains to be seen. I still think that Being able to use Mnesia from Rails or from rich clients would be really very awesome.

On top of those things I need to take the time to learn org-mode, and build at least one gen_server or gen_fsm based app.

Either way code will result!

BootStraps: now with less crapyness

I’ve been getting rid of my TODO’s in my mini bootstrapping library whilst focusing on my Sinatra app and the config file looks a lot less crappy now. It even looks a tiny bit awesome. Its doesn’t do everything (still need to put my app/* directories in here) but it serves my purposes and remains flexible:

BootStraps::Initializer.configure do |config|

  #Use the vendor directory
  config.vendored = true
  config.default_env = 'production'

  #require each gem
  config.gem 'activerecord'
  config.gem 'sinatra'
  config.gem 'json'
  config.gem 'haml'
  config.gem 'twitter'
  config.gem 'nakajima-rack-flash'

  #action taken when config.db.connect is called
  config.db.connect_action do
    ActiveRecord::Base.establish_connection(
        :adapter => 'sqlite3',
        :dbfile =>  File.join(config.root, 'db', "#{config.env}.sqlite3" ))

  end

  #Sinatra settings
  config.framework.set :environment, config.env
  config.framework.set :views, File.join('app','views')
  config.framework.set :server, 'mongrel'
  config.framework.use Rack::Session::Cookie, :domain => 'localhost'
  config.framework.use Rack::Flash, :sweep => true
end

No more string literal library names (holy cow that was bad), a nice flexible block for defining the db connection code, and little method missing spice to run whatever methods on whatever framework I’m using.

Using the config has been simplified as well:

require 'config/bootstraps'

module MyMod
  class MyApp < Sinatra::Base

    configure do
      Straps.db.connect
      Straps.framework.apply_settings!(self)
    end

  end
end

Thinking about rolling it into a gem just to save myself time down the road.

BootStrapping a Sinatra app

In an effort to simplify my Sinatra app startup and configuration process I’ve built out a very simple bootstrapping library. It’s robust enough to cover the needs for a small to mid size app, but simple enough that it’s not overkill for use with Sinatra.

boostraps.rb

In my config directory I have two files: bootstraps.rb and environment.rb.

The former contains three classes DataStore, Configuration, and Initializer. When you invoke the class method boot! on Initializer it pulls in the environment.rb, loads up the configured gems and then loads up all the files in whatever library directories have been specified.

Use within your Sinatra::Base subclass takes the form:

require 'config/bootstraps'

module MyModule
  class App < Sinatra::Base

    configure do
      Config = BootStraps::Initializer.config
      Config.db.connect
      bar = Config.global[:foo]
    end
  end
end

Environment.rb

The above is nice and quiet as it should be. Behind the scenes we’ve told ol’ BootStraps a bunch of stuff about what our app looks like:

BootStraps::Initializer.configure do |config|

  #Configure the db library and settings
  config.db.lib = 'ActiveRecord::Base'
  config.db.init_method = :establish_connection
  config.db.init_args = {
    :adapter => 'sqlite3',
    :dbfile =>  "#{config.root}db/#{config.env}.sqlite3"}

  #default our environment to production
  config.default_env = 'production'

  #require a ActiveRecord on boot
  config.gems['activerecord'] = '>=2.2'

  #arbitrary setting
  config.global[:foo] = 'bar'

end

Other stuff

The best part that you don’t see here is the ability to add libraries to app/[models,ext]/ or lib/ and have them required for you. Again, the point is to limit the knowledge/effort needed to contribute new code to the app.

kinks

It’s quirky in some places (you have to specify something for a gem version even if its nil) but its a relatively clean app boostrapping solution. You can check out my current use case in more detail here so long as you ignore the rest of the app :D.

OS XI

Things I’d like to see in OS 11:

  • True micro-kernel architecture
  • 64 bit all the way down
  • MacRuby support for desktop apps (Apple should adopt this project immediately)
  • Automatic window arrangement like XMonad/Ion

That is all.

RQuery: now with | and & operators

This weekend I wanted to tackle some of rquery’s shortcomings so I added the ability to |/& your operations and also the ability to use an object in the block instead of the symbol class for the column names. I agree with the assertion that it’s bad to pollute other classes, but I still think the symbol.declaration syntax is pretty clean and there’s isn’t much likely hood of those methods ever being implemented elsewhere ( says the guy who decided to implement them :P )

Examples

The key bit is the ability to add multiple operations to the same line connected with | or & operators. In this way you can form extremely complex queries, where as before you were limited to whatever you could get out a series of anded logic. The only stipulation is that you you have to wrap the operations with parenthesis if they are on the same line, or ruby will complain about a syntax error.

#All operations need to be on the same line and in parens
#either the | operator or the & operator can be used on a singel line
User.where do |user|
  (user.age.is > 20) | (user.age.in 16,18)
end

The above example should be easy to follow but there is one important gotcha with including &’s and |’s on the same line. Because the & operator has precedence in a string of operations if you want both | and & on the same line you need to force precedence for the | where it needs to be evaluated before the & in the resulting query.

#the & takes precedence here and will be grouped with the contains "Alice" which will be
#or'd with the contains "George"
#=> name contains "George" or (name contains "Alice and age from 20 to 30)
User.where do |user|
  (user.name.contains "George") | (user.name.contains "Alice") & (user.age.from 20..30)
end

In this case the query writer wanted to get the users who’s names contained either George or Alice and then filter with an age range. What it’ll actually do is find the users who’s name contains Alice and have an age between 20 and 30, and add anyone with a name like George. To fix this you can add more parens :(

#to correct the above to the more intuitive version add parens to force precedence of the
#contains operations
#=> (name contains "George" or name contains "Alice) and age from 20 to 30
User.where do |user|
 ((user.name.contains "George") | (user.name.contains "Alice")) & (user.age.from 20..30)
end

Or you can simply move the and’d operation to the next line as the operations are evaluated top down as the ruby block is executed and they are grouped using and.

#in this sutation it would be cleaner and easier to just move the and'd statement down
#a line as all seperate lines are and'd and lines have precedence from top to bottom
#additionaly operations on seperate lines don't need parens
User.where do |user|
 (user.name.contains "George") | (user.name.contains "Alice")
 user.age.from 20..30
end

The biggest change here is simply being able to use the | instead of the default for each line (and). And finally, if you checked out RQuery before, you’ll have noticed I’m passing in an object that represents the model being queried. The advantage is there’s no class polution, and the not so obvious advantage is that it will throw and nice verbose and descriptive exception if you try and use an attribute that doesn’t exist for a given model.


#should you attempt to use and attribute that doesn't exist for a given model
#rquery will tell you before it's sent to the db
User.where do |user|
  user.ssn.is == "123-45-6789"
end
# RQuery::AttributeNotFoundError: The field 'ssn' doesn't exist for this object
#       from /Users/johnbender/Projects/rquery/lib/rquery/attrib...
#       from (irb):24
#       from /Users/johnbender/Projects/rquery/lib/rquery/active...
#       from /Users/johnbender/Projects/rquery/lib/rquery/active...
#       from /Users/johnbender/Projects/rquery/lib/rquery/active...
#       from (irb):23


My specs cover most of the code and functionality for the activerecord extension (still the only piece I’ve worked on) and you can still use the symbol syntax if you want.

#environment config
RQuery.use_symbols

#example of using symbols, you can see more at the RQuery page on my site.
User.where do
 (:name.contains "George") | (:name.contains "Alice")
 :age.from 20..30
end

You can get the gem from github as johnbender-rquery, and I won’t be updating the rubyforge one unless github fails to build my gem again ( /cross_fingers ).

« Previous PageNext Page »