Tuesday, March 1, 2011

Instantiating a Ruby Class that is a Member of a Module from C# (a work around in 2 parts)

Recently I was experimenting with calling my generator ruby scripts from within an ASP.NET MVC 3 project and  I ran into a problem when I attempted to instantiate a class that is a member of a module.  I was sure others had ran into this issue so I hit the internet to see what others have done to get around this issue.  In this blog post I’ll lay out what my issue was and how I found the work around for it.

The Code

Here’s a scaled down version of the ruby class layout that I’m attempting to instantiate:

I took the guts out of the methods since they aren't important. The key here is that the class is in a module. The C# code that I’m trying to instantiate the ruby class in looks like this:

I’m not going to get into the nuts and bolts of how to call into ruby from C# but I am going to give you a quick overview of what the code is doing here.  The first line creates the object we need to call into ruby. I then set up the search path so all the required files can be found.  The next step is to execute the script that has the class I want to instantiate.  Once I have the object I want to execute the run method on the object and return the results.

Line #9 in the C# snippet is where the error was thrown.  It would complain about not being able to find the object.  I tried to use the full name, Generator::CmdLine, and just the class name CmdLine with the same result. After that I went to Google. Everything I found said that a class within a module couldn’t be instantiated from within C#. I found that a bit odd since modules are all over ruby so I turned to StackOverflow.com. 

The Workaround

I posted the question on StackOverflow,   Instantiating a ruby class that is in a module from within C#.  I figured if I’ve run across this problem someone else probably has as well. Within less than 24 hours Shay Friedman author of IronRuby Unleashed had answered my question. Here is what he suggested:

This method is to be placed at the bottom of the cmd_line.rb file outside of the module so it is visible from within the C# code. In the C# code I change the line #9 from the C# code snippet above to this:

Now I call the hack method from C# to get access to the Generator::CmdLine object. The work around got me past the issue I was having and I was able to continue on with my research.

Summary

Having the ability to create objects from ruby classes in C# code is nice but it is not fully functional at this point in time.  However, using resources such as StackOverflow can help you find ways around some of the limitations that currently exist.  I’m also thankful for people like Shay Friedman who take time out of their busy day to help the likes of me. 

This little adventure also has me wondering how many people out there are instantiating objects from Python or Ruby in production or in their day to day work environments.  If you are leave a comment with a brief description of how you are using it and what you think about it.

Wednesday, February 23, 2011

My Generator in the ‘Real World’–Generating Code from a JSON source

A while back I wrote a blog about using IronRuby to generate assemblies.  Back then the project had a pretty narrow goal which was to read information from a group of database tables and generate C# classes to model the data describe in the three tables.  It works nicely for what I needed for my day job.  While I was working on the original project I thought it would be nice to add more input sources like text files, URLs, etc.  In addition to multiple data sources I wanted to add the ability to produce code for other languages such as Ruby.  Given the time constraints I was working in I put those additions off.  Recently I had some time free up (yeah, right!) so I created a second project that is aimed at a wider field of use, Linux, Mac as well as windows.  I have added Ruby as an output language plus a new URL source.  .

In this blog will illustrate how a URL source that returns JSON can be used to create C# code.  The generated code will be responsible for retrieving the data and converting it from JSON to C# classes.  Next, I will be incorporated into a MVC project that I will use to display the standings from the given season.  The goal here is to show how quick you can use the code to display on the page.

Step 0.  Create new ASP.NET MVC 3 Project

Go through the usual steps of creating a new project in Visual Studio. Once you have created the project fire up the Nuget console and run:

Install-Package Newtonsoft.Json

The Newtonsoft.Json package is used by the service class we will generate in the next step.  The library will convert the JSON data and loads the C# class.

If you don’t have Nuget you can go the nuget.org site or install it through the Visual Studio Extension Manager.  It is an easy way to install third-party libraries to your project.  It feels very much like installing a gem in ruby. 

Ok, we have the project the way we need it now its time to generate the classes.

Step 1. Generating the Classes

I have created a batch file to run the code generation script so I don’t have to type out all the options.  The more I type the more mistakes I make.  Anyway here is the batch file.

The -i and -url parameters are used together to tell the generate that we are using URL as an input source and the –url passes the actual URL to use. The -l c_sharp and -mc switches are used to pass the desired output language and the name of the primary model class. The -mod and -sod options are used to tell the generator where to write the model and service files respectively. 

Next step is to run the script which takes all of a second and now I have my classes. 

imageA total of seven classes were generated to model the JSON (View the JSON here) that was returned.  The Standings.cs file is the primary model, meaning it is the topmost class in the source.  All other classes that end with Model were discovered in the input parsing process.  The all represent a property of the primary model or one of its supporting models.

The StandingsService.cs class will be used in our MVC project to retrieve the data and populate the Standings class.  We will use that class to display the data in a view.

Now that I have my JSON based classes it is time to start creating the web front end.

Step 2.  Incorporating the StandingsService Class

Since I chose to create an empty MVC project the first thing I’m going to do is create a StandingsController class without the CRUD methods.  I also created an index view that we will use to display standings.  Here is the code that I’ve added to the controller’s index method to retrieve the standings.

As you can see there is much to this method.  I am instantiating the StandingsService class and calling the Get method which returns an IEnumerable.  Since I know there will only be one object in the list I’m grabbing that and sending it to the view.  If this was actual production code I’d have a few more lines here to validate the year parameter, have the URL in the web.config file and check for nulls before I sent the data off to the view. Since its demo code I’m keeping it simple.

The same goes with the view, demo simple. I sort the values by league, division and ranking.

image

image

Nothing fancy here but going from nothing to displaying data in less than 10 minutes is a good way to start any project.  The generator takes away the tedious part of data access, creating the models and service classes and lets me get down to the business layer of the application. 

 

 

Summary

I started this project off with a URL to a web site that returns MLB standings in JSON format and I wanted to be able to display the results in my web site.  Using my generator project I was able to create the model classes and a service class needed to manipulate the data in C# in less than five seconds.  After the classes were generated it took me somewhere between 5 to 8 minutes to get a web page up and running that took the data from the URL and displayed it in my MVC view.

The generator project very young and hasn’t had many real world tests.  In the near future I will be running it through more rigorous testing.  Look for more blog posts on and around the generator project.

The MVC project can be downloaded from here

The Generator code  (Remember, this is very young/green code)

Tuesday, January 25, 2011

Using SpecFlow to test my F# Baseball Stats Library

As part of my Ruby indoctrination I picked up a copy of The RSpec Book: Behaviour Driven Development with Rspec, Cucumber, and Friends (The Facets of Ruby Series) .  I’m about half way through the book and I find the Behavior Driven Development (BDD) process very comfortable.  Writing tests this way just ‘seems right’ and the process has already improved my Ruby code.  However, during my day job I write C# code so I started looking around to see what BDD options are available for .NET.  That’s when I found SpecFlow, which plays the role of Cucumber in the Ruby world.  So to get up to speed with SpecFlow I’ve decided to use it to help me test my obp function which I wrote in Project Chadwick #2–Top 5 SF Giants OBP (F# Version) as I build out my baseball stats library.  Before I get started I will describe the setup.

The Setup

Step 0. Create F# Library Project

On the File Menu in Visual Studio Select New Project.  In the New Project dialog, open the Other Language option and click on Visual F# then Select F# Library.

FSharpProject

I named this project BaseballStats.  Remove the Module.fs and Script.fsx files, I’ll add my own *.fs file when the time comes and we won’t need the Script.fsx file. That’s it for the F# project until it is time to create test test project. 

Step 1. Create the Test Project

Since I am using MSTest for the testing I am using a normal C# test project.  I named mine BaseballStats.AccetpanceTests.  Once the project is created I need to add the following references:

  1. FSharp.Core assembly
  2. F# library project you created in Step 0, in my case the BaseballStats project.

Delete the test class that was automatically added to the test project, I wont be using it.  In real life I’d create a unit test project also lets just pretend we did that here.  

Now that we have the test project created its time to install SpecFlow

Step 3. Install SpecFlow

If you already have SpecFlow installed then you can jump down to the NuGet section, otherwise go ahead and grab SpecFlow installer from here.  I downloaded and ran the installer so that I had the file templates available in the ‘Add New Item’ dialog.  After the install I ran the command below from the NuGet Console to add the necessary DLLS to the test project.

install-package –Id SpecFlow –Project BaseballStats.AcceptanceTests 

SpecFlow uses NUnit as its test runner by default but it can be configured to use MSTest.  Since I’m using MSTest I need to update the app.config file so it looks like this:

<specFlow>
    <!-- Possible values include NUnit (default), MsTest, xUnit -->
    <unitTestProvider name="MsTest" />
  </specFlow>

The setup is complete.  Its time to get on with the testing!

The Testing

Step 4.  Create a Feature

To create a SpecFlow feature file right click on the BaseballStats.AcceptanceTests project and select ‘Add…’ > ‘New Item…’ And Select SpecFlow feature file.  Name it CalculatingObp.feature. 

image

Any file with the .feature suffix will also have a code behind file that SpecFlow will use to call the step definition methods.  The step definitions are what is run to perform the tests. I will define the steps after I have finished describing the feature .  When a new feature file is created you will see the following:

Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers

@mytag
Scenario: Add two numbers
Given I have entered 50 into the calculator
And I have entered 70 into the calculator
When I press add
Then the result should be 120 on the screen

Obviously this isn’t the feature I want to describe but lets take a minute to discuss it.  What the file does is describe the feature we are working in an almost plain English style.  The text under the Feature line is a narrative to help remind me what I want the the feature to do.  It has no real bearing on the code that we will use to test the feature. 

The Scenario section does have an impact on the test’s code. The Given, And, When and Then statements will be used in the step definition file and they will drive the test. 

Here’s what our feature for calculating the OBP looks like:

Feature: Calculating OBP
In order to determine the effectiveness of a batter
As a baseball fan
I want to be able to calculate a player's On Base Percentage

Scenario: Calculate a Season's On Base Percentage (OBP)
Given A batter had "389" ABs, "104" Hits, "56" BBs, "0" HBPs,
and "4" SFs
When I run the calculation 
Then I should see the result "0.356"

The feature is used to describe how I am going to calculate a batter’s seasonal OBP.  When I save the feature file a code behind file is created that contains code that SpecFlow will use to find the step definition. 

Step 5. Create the Steps

Now that I have the feature description in place I’m ready to test.  Lets run the SpecFlow test and see what happens.  To run the test make sure the feature file is the selected tab in VS and click the ‘Run Test in Current Context’ button.  When you run the test the results will say ‘Inconclusive’.  View the test run details and you will see a statement that says there were no matching steps found for …. which maps to the first line of the scenario.  A little further down in the ‘Standard Console Output’ section you will see that SpecFlow has provided us with boiler plate code for the step definitions that looks like:

Given A batter had "389" ABs, "104" Hits, "56" BBs, "0" HBPs, and "4" SFs
-> No matching step definition found for the step. Use the following code to create one:

[Binding]
public class StepDefinitions
{
[Given(@"A batter had ""389"" ABs, ""104"" Hits,   
""56"" BBs, ""0"" HBPs, and ""4"" 
SFs")]
public void GivenABatterHad389ABs104Hits56BBs0HBPsAnd4SFs()
{
ScenarioContext.Current.Pending();
}
}

When I run the calculation I should see
-> No matching step definition found for the step. Use the following code to create one:
[Binding]
public class StepDefinitions
{
[When(@"I run the calculation I should see")]
public void WhenIRunTheCalculationIShouldSee()
{
ScenarioContext.Current.Pending();
}
}

Then I should see the result "0.356"
-> No matching step definition found for the step. Use the following code to create one:
[Binding]
public class StepDefinitions
{
[Then(@"I should see the result ""0\.356""")]
public void ThenIShouldSeeTheResult0_356()
{
ScenarioContext.Current.Pending();
}
}

It is time to add a step definition file to our testing project.  Right click on the AcceptanceTests project and select Add New Item Select the SpecFlow Step Definition option and give it the name ObpStepDefinitions.

image

Remove the template code that is inserted into the ObpStepDefinitions test and replace it with the boiler plate methods, from the ‘Standard Output Console’ area of the test results, that were generated when when ran the SpecFlow test. My step definitions should now look like this:


Run the test again and this time the test should report:

Assert.Inconclusive failed. One or more step definitions are not implemented yet.
ObpSteps.GivenABatterHad389ABs104Hits56BBs0HBPsAnd4SFs()

This is a good sign!  What it means is that SpecFlow now sees the steps and attempts to execute them but since the only code in the first method is the ScenarioContext.Current.Pending method call and it halts execution there since this is the first step.  Now its time I put some actual code into the method bodies.  I am going to start with the Given step. I’m going to use the numeric values in the Given statement as inputs for my test.  How can I do that?  With SpecFlow I can use regexes to grab the numeric values from the Given attribute so we can use them to run the OBP calculation.   The values grabbed by the regexes are then passed to the step method via parameters.  The values will be converted to the specified data type in the method signature by SpecFlow.  My updated step looks like this:


This step is responsible for retrieving and storing the input values that will be used to calculate the OBP. Now when I run the test I see:

Assert.Inconclusive failed. One or more step definitions are not implemented yet.
ObpSteps.WhenIRunTheCalculationIShouldSee()

Again, it is a good sign.  It actually executed the first step and is now trying to run the When step, but it encounters the Pending method again.  This is the step where will do the OBP calculation. Since we do not have the Baseball.obp function in the F# code, we are adding code to the step that 'We wish we had', a phrase the author users repeatedly in the RSpec book. Here is the update step definition.


Since the Baseball.obp function doesn't exist yet we will not be able to build the project. So I am going to switch to the F# BaseballStats project and write just enough code to allow us to build and run the test. First we create a BaseballStats.fsi file followed by a BaseballStats.fs file. In F# projects a file’s order of appearance matters in the build process.  Make sure that the fsi file appears before the fs file in the project’s listing.  To move a file up or down right click on the file you wish to move and choose the appropriate movement direction.

image


The BaseballStats.fsi file is a signature file, you can think of it like a C/C++ header file. It describes the functions that are available in the BaseballStats.fs file. The val obp line is describing the function's signature. There will be 5 float parameters and it will return a float value.


The BaseballStats.fs file is where the function is implemented. In the real world we’d write just enough code to allow us to run the test again and when it failed we’d drop into unit testing or a RSpec .NET equivalent until the obp function was fully functional.  Then we’d come back to SpecFlow, run the test and get green.  In order to keep this post as brief as possible I’m not going to illustrate the process here. I have added the entire obp function but pretend we went the through process I just discussed.  Once I’ve added the obp function to the fs file build the solution and run the test.   It still comes up as Inconclusive. This time it is due to the last step not being defined.


The final step in my test is assert that the calculated OBP equals the expected value.  Again I’m using regex to grab the expected value which will compared against a rounded off version of the results from the Batting.obp call. Once we have green we know that the feature is working for this set of test data.

Here is what the detailed view of the test run should look like:

image

That’s it, we have green!  The OBP function performed as we had expected.  Obviously we haven’t fully tested it but we now know that the function works with valid inputs.  So what happens when we provide negative values or values that make the denominator zero? SpecFlow has a way that will allow me to use this single scenario to test all the possible permutations I can think of without writing additional scenarios.  I’m going to save that topic for a later post.

My Thoughts on SpecFlow

SpecFlow gives .NET developers a way to get BDD into our projects.  In the beginning using SpecFlow doesn’t seam to flow as smoothly as Cucumber and RSpec in the ruby world.  This may be due to the fact that I haven’t used SpecFlow enough or could be due to the C# and Ruby differences.  Overall, I like the BDD style of development that SpecFlow brings to the .NET world.  BDD seems to fit better to my way of thinking.  I am going to continue to use SpecFlow in my side projects and will work on incorporating it into my ‘day job’ environment.

Resources

You can download the source here

SpecFlow: project web site

TekPub’s free video on SpecFlow

F#: fsharp.net