Major Ruby OData Update v0.1.0

December 7, 2011 • Damien White

OData_logo_MS_smallIt’s here! I’m happy to announce a major release of ruby_odata, v0.1.0. There are many enhancements found in this release so let’s dig in and see what’s new.

There is one thing that we need to get out of the way first, a breaking change. In v0.0.10 and below, queries and some instances of calls to save_changes would return a single entity if there was only one returned. This was confusing and could lead to errors. As a consumer of the gem, you wouldn’t know if a call to the Service’s execute method was a single object or an enumerable. Instead of you having to guess, those calls now return enumerables all the time. This applies to calls to execute and save_change additions. Updates, deletes, and add_link calls (we’ll get to those in a minute), as well as batch saves all return Booleans, but single save_change add calls will return an enumerable because those results are parsed similar to execute calls.

Now that that’s out of the way, let’s look at the new features.

The Features

Partial Results

The first change is thanks to arienmalec. OData allows services to do server-side paging in Atom by defining a next link. The default behavior is to repeatedly consume partial feeds until the result set is complete, for example:

svc.Partials
results = svc.execute # => retrieves all results in the Partials collection

If desired (e.g., because the result set is too large to fit in memory), explicit traversal of partial results can be requested via options:

svc = OData::Service.new "http://example.com/Example.svc", { :eager_partial => false }

svc.Partials
results = svc.execute # => retrieves the first set of results returned by the server

if svc.partial? # => true if the last result set was a partial result set (i.e., had a next link)
  results.concat svc.next # => retrieves the next set of results
end

while svc.partial? # => to retrieve all partial result sets
  results.concat svc.next
end

Single Layer Inheritance

This feature was long overdue! Back in July 2010, Scott Allen wrote a post about ruby_odata where he mentioned a change to the Service class that would parse models with single layer inheritance. His code has finally been integrated into ruby_odata. Note, that the addition currently only support single layer inheritance, but that should handle most of the cases you will encounter.

There are times when you don’t necessarily want to bring down full navigation property entities. In these cases the OData protocol has support for Addressing Links Between Entities. To support this, there is a new method that can be tacked on to a single entity query (e.g. finding an entity by id) called links where you pass the name of the navigation property of the links that you want to retrieve. For example:

svc.Categories(1).links("Products")
product_links = svc.execute # => returns URIs for the products

There are times when you need to create a linkage between a parent and a child, and you don’t want be required to perform an add or update. You can now simply call the add_link method on the OData::Service with the parent object, the name of the navigation property, and the child object. Finally, call save_changes. For example:

svc.add_link(<Parent>, <Navigation Property Name>, <Child>)
svc.save_changes

Lazy Loading

In previous versions of ruby_odata, if you wanted to perform lazy loading of navigation properties you would need to do it manually, which was extremely ugly. I’m happy to say that there is now a way to perform lazy loading in a graceful way. All you need to do is to call the load_property method on the OData::Service with the entity to fill and the name of the navigation property. For example:

# Without expanding the query
svc.Products(1)
prod1 = svc.execute.first

# Use load_property for lazy loading
svc.load_property(prod1, "Category")

Reflection / Introspection

Paul Gallagher (tardate) had a great suggestion: Why should you have to look at the EDMX metadata file directly when ruby_odata parses all that data? You can now use the OData::Service to access a list of the collections that the service exposes. There is also a classes method, which gives you a list of the classes generated by the OData::Service. A function_imports method, which will list function imports exposed by the service (we’ll get to those in a minute). On a class, you can call the class method, properties, which as you guessed will give you information about each property. Each of these methods return hashes where the keys are the name, and the values are hashes of the metadata for the member that you are introspecting.

Function Imports

Quite a while ago, I received an message from a user via this blog asking about the ability to call custom service methods. Within a WCF Data Service, you can expose your own custom methods, which get translated into the EDMX as FunctionImports. These aren’t to be confused with Entity Framework’s FunctionImports. These will be parsed and dynamically added as methods to the OData::Service. When you call one of the methods, they will return a result without you needing to make a call to execute or save_changes. There are different result types. You may get back a true if the FunctionImport doesn’t return any content (for example, a delete). You may get back a single entity, or a collection of entities. Finally, you may get back a single primitive value (e.g. Integer, String, etc), or a collection of primitive values. You can use the function_imports method on the Service to determine the type that will be returned.

Namespaces

In the same message as the request for the custom service methods (ok, it was actually a reply), the user mentioned that he received type conflicts with the generated classes if one by the same name already existed. If this is a problem for you, you now have the ability to tell the OData::Service to generate the models in a specific namespace. There is a new option that can be passed in to the constructor’s option hash, :namespace. If you use this option, there shouldn’t be any collisions. You can pass a namespace in either Ruby (with double colons, e.g. RubyOData::Rocks) or .NET style (with periods, e.g. RubyOData.Rocks) if there is more than one part to the namespace that you want to use. If you don’t provide a namespace, then ruby_odata will generate the classes without any namespace just as it has before.

The Beginnings of Something Cool?

As developers, the majority of the time we either retrieve collections or a single entity, usually by an ID. I didn’t really like the current steps to retrieve a single entity by ID:

svc.Categories(1)
category = svc.execute.first

Sort of ugly, right? So inspired by Ruby on Rails, I added a class method for each created class so that you could just do this:

category = Category.first(1)

Great, right? It’s a little nicer, but sadly it’s a one trick pony right now. There’s no filtering, eager loading, etc. My idea was to hopefully expand on the concept like Rails and ActiveRecord, but for now it’s just an easier, cleaner way to get an entity.

Testing

In addition to the new features, I’ve been busy working on the testing suite.

No More Hardcoded URIs

Having to rely on Windows for testing has made me stick to Windows and e-TextEditor for ruby_odata development. The key word there is actually testing. All that Windows is required for is hosting the WCF Data Service for the Cucumber integration tests, but Cucumber is just one part of it, RSpec is the other. It’s no denying that Ruby on Windows is slower than its *nix counterparts (not to knock the RubyInstaller.com team, if it wasn’t for them, Ruby on Windows would be non-existent). Where you really see a slowdown is when you are doing TDD/BDD testing. You need tests to run fast, and if you ran the command time rake spec, my Windows box is more than 4 times slower than my old MacBook (note, not a Pro), which has similar hardware. Now, multiply that by every spec I write and I want to throw my machine out a window. So, in an effort to migrate development, the URLs and ports required for the sample WCF service are now dynamic. Take a look at features/support/constants.rb for more information. Thanks to Sean Carpenter (scarpenter) for his fork where he changed the static values in that file to be optionally set by environment variables.

Hello New Technology

I decided to change up the technologies used within the test service. First, no more SQL Express, hello SQL Compact 4. Why the change? SQL Compact 4 is simply DLL deployed, you don’t need to install anything, cool. Also, since SQL Compact 4 databases are file based, instead of having to truncate to clean the database for testing, the file could just be blown away and rebuilt easily.

So, a new database, how about a new ORM? Entity Framework 4.1, also known as “magic unicorn edition” is now being used. EF 4.1 “Code First” as it is called, is just awesome. Ditching Entity Framework’s EDMX for pure code, now that is right up my alley.

Finally, since I switch to EF 4.1, the current released version of WCF Data Services, well, didn’t work right. You know what did? The WCF Data Services October 2011 CTP, w00t.

Everything you need should be in the new test project (now called RubyODataService, changed from SampleService, though that’s still in the test URLs) thanks to NuGet and my copying of the WCF DS CTP DLLs to the packages directory since there wasn’t a NuGet package for it).

Pickle

I love the pickle gem. Aside from being authored by a guy with my last name (no relation), pickle makes Cucumber features so much nicer to read. Throughout development of ruby_odata, I’ve written my own step definitions to do things, essentially cloning what pickle does out-of-the-box, e.g. creating models, asserting against models, etc. I had toyed with getting pickle to work around June of last year, but was having issues. As time passed and experience grew, I finally got it all working. I used it in a few places in new tests, but I hope to do a complete overhaul. My pickle adapter also needs a bit of work since I didn’t put it through all of its paces, and some things need to be added to ruby_odata before it will function as expected. All in all though, it is great to finally have pickle working.

Conclusion

I hope that you find the new version of ruby_odata to be useful. In the next version, I hope to further enhance the gem with more of the different operations that OData can perform. I’m sure I’ll perform some more housekeeping in the test suite removing my ugly Cucumber steps for nice, clean Pickle steps.

What would you like to see in the new version? Add your suggestions to the issues section on GitHub. I’d also like to know what you think of the API as it stands right now. I’ve essentially been following the OData Silverlight client API syntax (e.g. AddTo methods, add_link, etc). With the addition of the `:first` class method, it got me thinking of a bit of a different approach. Not necessarily giving up one for the other, but I think another more Ruby-esq style could co-exist. Anyway, that’s all for now, go forth and do cool things with ruby_odata. I’d love to know how you are using the gem. I was excited to find out that ruby_odata was being utilized by the rgovdatablog gem.

Posted in odata, ruby, ruby_odata, wcf data services and tagged with OData, Ruby, WCF

Damien White

I am a software architect with over 16 years of experience. I simply love coding! I have a driving passion for computers and software development, and a thirst for knowledge that just cannot be quenched. I'm happy to share what I know in my quest to learn as much as possible. I focus most of my time on web development using Ruby on Rails, Ember.js, and ASP.NET MVC.

comments powered by Disqus