Category Archives: IronRuby - Page 3

Dynamic Script Control

Both Silverlight and WPF use XAML markup to describe their user interface.  As I’m currently writing my chapter on WPF for my book IronRuby In Action and I want to use some xaml that has been generated before for a different project but with an IronRuby class to load the xaml I’m in trouble. This is because you can declare assembly references in the xml namespace declarations so you can use the types in that assembly from xaml. 

The DLR based languages don’t compile into static assemblies and this means that you can’t use those xml namespace definitons to reference your assemblies.  I wrote a fairly trivial control that acts like a hook for DLR based controls in the XAML tree.

You can check it out at codeplex.
http://codeplex.com/dynamicscriptcontrol

The idea behind this control is that you can "hook" your DLR based control into the visual tree by setting some properties.  You can set properties on the DLR based control by setting the Attributes property on the DynamicScriptControl

Let’s look at a quick example:

1. The ruby file defining a custom TextBox. But you can do whatever  you want in that ruby file of course.

dynamic_script_control_rubyscript

All this textbox does is preset it’s text property to "I’m prefilled"

2. The xaml for the window

dynamic_script_control_window_xaml 

You first declare a namespace for the assembly that has the DynamicScriptControl. Next I have a StackPanel that contains 2 DynamicScriptControls. The first just contains the 2 mandatory properties. We need to know which class you want to instantiate in the file you provide by setting the ScriptFile property. This script file property is a path to your ruby file in my case prefilled_text_box.rb.
The second DynamicScriptControl is one where I want to initialize the control with my own text property. To declare those properties you have to add them to the Attributes collection of the DynamicScriptControl. At this moment it’s not smart enough to know which datatype you give it so you can specify a format string which was necessary in this case because text is a string.

3. The result

dynamic_script_control_window

Michael Foord the author of IronPython In Action will provide the python integration in this control.

There was a release of the Dynamic Silverlight SDK earlier this week which contained the necessary source code files to compile a common DLR for both IronRuby and IronPython.  That is what makes it possible to support multiple scripting languages from the start. 

I’ve hosted the source code on google and you can find that at:

http://code.google.com/p/dynamic-script-control

The IronRuby in Action book went into EAP today

It’s been a bit of journey but my IronRuby in Action book finally went into EAP today. EAP means exactly what it means in the software world, still very much subject to change. This could be seen as a public review :)   I welcome any suggestions you may have.

You can find the book at http://manning.com/carrero
There is also a forum there where you can post suggestions and/or problems you may encounter when running the code samples. I look at it as my issue tracker :)

I’m currently working on a chapter where I build a complete twitter client with IronRuby and WPF (not silverlight as indicated on the current TOC). It is based on Witty but uses IronRuby instead of C# to drive the xaml. Also some storyboards have been replaced by IronRuby instead of by xaml stuff. It’s also not a straight port of Witty because I haven’t really looked at their code apart from the xaml that is.

Happy reading :)

Update: Some people started calling it the “jugs book”

kick it on DotNetKicks.com

Technorati Tags: ,

Using the mini spec framework that comes with IronRuby

I’ve set out to build my first "real" application (still playground material) with IronRuby today and for me no application can be built without having the unit tests or specs to give me that warm fuzzy feeling that things should work. That being said the first thing on my list of things to do is figure out how to use the minispec framework that IronRuby currently uses to write my specs.

Since my application will be doing a fair bit of http requests I want to be able to mock that out so I don’t have to rely on the webservers to be up and running or being online to run the specs.

The first thing I did was copy a couple of files from the tests\IronRuby\Specs directory in the directory where you downloaded the IronRuby source code into (C:\tools\IronRuby) in my case. I copied those files in a specs subdirectory of my application directory. The files we’re going to need are:

  • mini_mock.rb
  • mini_rspec.rb
  • mspec_helper.rb
  • rspec_helper.rb
  • simple_mock.rb
  • spec_helper.rb
  • spec_runner.rb

The next thing on my to-do list is then to change some of the files so that we can run our own specs on our terms.

The first changes I made are in spec_helper.rb. I replace require ‘mini_mock’ on line 18 with require File.dirname(__FILE__) + ‘/mini_mock’

The next bit is in the spec_runner.rb file. I just removed the word core/ in that file because our specs will just live in a directory structure under the specs folder.

now to create the first spec and mock with that spec framework:

I created a folder models in my specs folder. In that folder I created a file spec_test_spec.rb with the following content

require File.dirname(__FILE__) + "/../spec_helper.rb"

describe "Specs" do

  it "should work" do
    # puts "Yay!!! It works"
  end

  it "should mock methods" do
    mock = Object.new
    mock.should_receive :mocked_method, :returning => "Yay!!! I'm mocking"
    result = mock.mocked_method
    result.should == "Yay!!! I'm mocking"
    # puts result
    Mock.verify
  end

end

This just has a test method to see if it will run the rspec syntax and the second is where I tested to see if I could mock methods on objects. In the future we’ll be able to use mocha and rspec for example and then we’ll have a nicer syntax for mocking stuff.  But this will do for now.

Then I opened up a command console and navigated to the folder that contains my application

+ C:\projects\lumpr\src\Sylvester.DesktopEdition\Sylvester.IronRuby\specs

» ir spec_runner.rb models – pass

Specs should work

Specs should mock methods

Total pass: 2 out of 2 examples

I may post an example later this week with some real tests but this shows you how to get going with the mini spec framework to test your .NET stuff today.

Technorati Tags: ,

Minimal IronRuby/silverlight examples online

I just posted the first post in my series on getting started with Silverlight and IronRuby on the rubydoes.net website

You can read more about it here: http://rubydoes.net/2008/04/14/silverlight-minimal-examples/

Started blogging on rubydoes.net

I just put my first post on http://rubydoes.net 
I’m starting a series there on using Silverlight and IronRuby to develop applications. This post is an introduction and announcement of my series on the topic.

I’m porting the IronPython examples from Michael Foord to IronRuby and will do a new example every day of this week.

See you there :)

 

UPDATE: I didn’t make it clear but this blog will still be active. Just most of my IronRuby stuff will go on rubydoes.net

An IronRuby Digg Client

Scott Guthrie has a series of tutorials on how to consume the Digg API with Silverlight and C#. I wanted to know if I could convert that tutorial to IronRuby.

What I came up has the same end result but due to the fact that IronRuby and its Silverlight implementation isn’t complete yet I can’t mimic the structure of that tutorial yet. I couldn’t yet figure out how to load external xaml files so I put all the xaml in one file.

I have one C# file that resides in the lib folder, this file is called DigStory.cs and contains a DiggStory class which represents a story on digg. It uses XLinq to parse the xml document and return a list of stories.

Download the complete code

The code contained in that file:

public class DiggStory
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public int NumDiggs { get; set; }
    public Uri HrefLink { get; set; }
    public string ThumbNail { get; set; }
    public string UserName { get; set; }

    public static IEnumerable ParseFeed(string feedContent)
    {
        var diggFeed = XDocument.Parse(feedContent);

        var result = from story in diggFeed.Descendants("story")
                     where story.Element("thumbnail") != null && !story.Element("thumbnail").Attribute("src").Value.EndsWith(".gif")
                     select new DiggStory
                     {
                         Id = (int)story.Attribute("id"),
                         Title = ((string)story.Element("title")).Trim(),
                         Description = ((string)story.Element("description")).Trim(),
                         ThumbNail = (string)story.Element("thumbnail").Attribute("src").Value,
                         HrefLink = new Uri((string)story.Attribute("link")),
                         NumDiggs = (int)story.Attribute("diggs"),
                         UserName = (string)story.Element("user").Attribute("name").Value,
                     };

        return result;
    }
}

I won’t post the xaml file because it’s a bit too long. I will however post the app.rb file and offer a little bit more explanation on that file.

The app.rb file:

require "System"
require "Silverlight"
require "System.ServiceModel.Web"

require "Contract"

include System
include System::Net
include System::Windows

class App < SilverlightApplication
  use_xaml

  def get_topic_url(topic)
	  "http://services.digg.com/stories/topic/#{topic}?count=20&appkey=http%3A%2F%2Fflanders.co.nz%2Fblog"
  end

  def initialize

	  search_button.click do |sender, e|
		topic = search_textbox.text

		client = WebClient.new
		client.download_string_completed do |sender, args|
			return unless args.error.nil?

			stories = DiggStory.parse_feed(args.result);
			stories_list.selected_index = -1;
			stories_list.items_source = stories;
		end
		client.download_string_async Uri.new(get_topic_url(topic))

	  end

	  stories_list.selection_changed do |sender, args|
		  story = stories_list.selected_item

		  story_detail.data_context = story;
		  story_detail.visibility = Visibility.Visible
	  end

	  close_button.click do |sender, args|
		story_detail.visibility = Visibility.Collapsed

	  end
  end
end

App.new

The above code contains all the code necessary for our application.

We first tell the application to use the app.xaml file. Next we define a method that builds us the request url for the digg api. I chose to use the xml format to stay as close as possible to the sample of Scott.

Like in the C# sample we’re also downloading the file asynchronous. In the callback for when the request completes we parse the returned string into a list of stories and set that as the item source of our Listbox in the app.xaml file.

We handle the selection_changed event of the ListBox to display a detail view on a story with a link to the digg page.

More explanation on the xaml can be found on Scotts blog.

Make sure you get the bits you need from the dynamic silverlight website needed for running dynamic silverlight applications.

Put the path to chiron.exe in your PATH variable and extract the contents of the downloaded sample. open a command prompt and go to the location where you extracted the files to. type chiron /b and click on index.html

Download the complete code

kick it on DotNetKicks.com

A status report on the IronRuby in Action book

The last couple of months I’ve been fairly busy writing my book. I had a first draft ready and that was submitted for review.

At this moment I’m working through all the remarks that the reviewers gave me. Some of them were quite good and made me realise that not everybody expects what I’m expecting from an IronRuby book. So I’ve been expanding some explanations and rewriting big chunks. I expect that we will have another review round soon. I have to process those remarks again and at that stage we’ll probably be ready to publish it in and EAP style.

Now that my first 3rd is done I’m starting at actually developing applications with IronRuby. I’m pretty curious to a couple of things from the community.

At this stage I have the following things planned for DataAccess.

I’ll include a brief tutorial on how to use IronRuby with:

  • SubSonic
  • Castle ActiveRecord
  • Linq2Sql
  • Rails ActiveRecord
  • LightSpeed

I would like to know which one I should emphasize most and focus on most.

After that I’m starting on WPF and/or Winforms. At this stage I’ve only planned to talk about WPF and not about Winforms. But I’d like to confirm if that is the general idea from the people that are going to take this IronRuby ride with me.

When I start talking about ASP.NET which topics are of most interest ?

That are all the questions I have for now. When I have more questions I’ll surely post them. And I would like to thank everybody that puts his 2c in.

I’ll post more when I have more news around the book.

Technorati Tags: ,

kick it on DotNetKicks.com

Using Ruby to Generate LightSpeed Models – Part 4

UPDATE: I have an update here with a new version and the location on google code where the project is hosted now.

 

In the previous 3 parts (part 1, part 2, part 3) we talked about getting the meta data out of the database and generating the lightspeed entities in memory.

I won’t put the code listings up in this blog post but instead will give you a link to the complete code :) You can download the express edition of LightSpeed and try it for yourself if you want.

Instead I’ll talk about how you can use the ruby script to generate the models from an existing database.

It adds the generated files to the visual studio project file in the same directory or you can specify one.

Currently it only supports Sql Server 2005. You need to create a project first in visual studio so we have

a project file to add items to.

To use it you have to tell it which database it has to find by putting a database.yml file in your visual studio project directory.

The content of that database.yml file may look as follows

# on windows you can use the following connection string

#

# sqlserver:

#   database: northwind

#   host: localhost

#   username: sa

#   password: MaybePassword123

# on OSX and Linux you can connect to sql server through odbc (iODBC or unixODBC in combination with FreeTDS)

#

# sqlserver:

#   dsn: NorthwindSql

#   username: sa

#   password: MaybePassword123

when you’ve done that you can execute it like shown in the screenshot below:

Terminal-screenshot

I have a database.yml file in the directory and I execute the command ruby < >

on linux and OSX you can chmod +x that path and then you don’t have to type ruby anymore.

That will generate the models for your database provided that your database conforms to the conventions needed for LightSpeed.

The next step would be to open visuals studio and build the project. The files got added in visual studio every entity are 2 files one with the generated code and one empty one that you can use to implement behavior on the model if you would want to.

The first picture shows how the solution explorer looks and the second one shows the results of the build without having touched one file after generation.

In the download there is a folder db which contains a slightly modified script from the Northwind database that comes with the samples for lightspeed. I used that database because it has every type of relationship and the script did conform to the lightspeed conventions :)

LightSpeedGenerateTest

LightSpeedGenerateTestBuildResult

del.icio.us Tags: ,,

Technorati Tags: ,,

kick it on DotNetKicks.com

Using Ruby to Generate LightSpeed Models – Part 3

First off I’m writing with windows live writer again, ecto wasn’t up to the job. It tried to “clean” my html, granted it was messy but it should leave my text untouched. The whole editing experience wasn’t satisfying enough. And Ecto already seemed like the best blog editor for mac, slim pickings indeed. From my tools I expect foremost that they stay out of my way and it didn’t. I just talked with Simone about looking at making a .NET based client that runs on mono, we’ll see where that plan goes because I don’t really have time to do that for the moment.

In the previous posts in this series (part 1, part 2) we discovered how to connect to the database and how to get the meta data about that database out. Maybe I should also explain why I’m doing this series with LightSpeed instead of ActiveRecord from Castle or SubSonic or Linq2Sql for that matter. I will definitely touch on all those orms in the coming week, but I started with LightSpeed because it’s the easiest ORM I’ve ever used.

This post will deal with actually doing something useful with that meta data. Today we’re going to generate the represenation of the entities and their properties. Tomorrow we’ll deal with actually generating the files from the the in-memory presentation we’re generating today.

We’re going to need 2 classes in addition to the LightSpeedRepository class. One to represent an entity and one to represent a property. The goal is for tomorrow to render the entities as complete as possible with validation attributes etc.

And without further ado here are the specs we’re going to build:

LightSpeedRepository Conversion
- should convert a given table to light speed metadata
- should convert a given table without relations to a light speed entity definition
- should convert a given table with a m:1 relation to a light speed entity definition
- should convert a given table with a 1:m relation to a light speed entity definition
- should convert a given table with a m:n relation to a light speed entity definition

LightSpeedProperty
- should allow for a property to be set
- should return a predicate for booleans
- should return a predicate for booleans
- should return a sql type
- should be a lightSpeed property

LightSpeedEntity
- should have properties, has many, belongs to and through associations
- should create a valid property name if one doesn’t exists already in the through association properties
- should create a valid property name if one doesn’t exists already in the has many properties
- should create a valid property name if one doesn’t exists already in the belongs to properties
- should create a valid property name if one doesn’t exists already in the properties
- should create a valid property name if one already exists in the through association properties
- should create a valid property name if one already exists in the has many properties
- should create a valid property name if one already exists in the belongs to properties
- should create a valid property name if one already exists in the properties
- should create a valid property name if two already exist in the through association properties
- should create a valid property name if two already exist in the has many properties
- should create a valid property name if two already exist in the belongs to properties
- should create a valid property name if two already exist in the properties

Let’s start with looking at the LightSpeedProperty first. The attributes on this class are implemented using some simple metaprogramming. This class will represent a field in a LightSpeed entity and will take care of rendering that properly into the c# file. We actually create the data in the LightSpeedRepository class.

class LightSpeedProperty

  attr_accessor :attributes

  def initialize(params = {})
    @attributes = params
    LightSpeedProperty.create_methods params

  end

  def [](attribute)
    attributes[attribute]
  end

  def self.create_methods(params)

    params.each do |k, v|
      define_method("#{k}=") do |val|
        @attributes[k]= val
      end

      predicate = %w(primary_key foreign_key unique nullable).any? { |o| o === k.to_s }

      define_method(predicate ? "#{k}?" : "#{k}") do
        @attributes[k]
      end

    end
  end

end

In the LightSpeed entity class we describe the actual Entity. I monkey patched Array so that I could ask it the question if it has a particular property. To avoid naming conflicts we check for properties that exist already and otherwise give them a generic new name by appending a number.

class Array

  def has_property?(name)
    exists = false

    each do |hm|
      exists = hm[:name] == name
      break if exists
    end

    exists
  end
end

class LightSpeedEntity
  attr_accessor :properties, :belongs_to, :has_many, :through_associations, :name, :namespace

  def initialize
    @properties = []
    @belongs_to = []
    @has_many = []
    @through_associations =[]
  end

  def create_property_name_from(from, idx=0)
    tname = build_property_name_from from, idx
    idx += 1 #when the property exists try with a higher number
    return create_property_name_from(from, idx) if has_property?(tname)
    tname
  end

  private

    def has_property?(tname)
      properties.has_property? tname or has_many.has_property? tname or belongs_to.has_property? tname or through_associations.has_property? tname
    end

    def build_property_name_from(from, idx)
      if idx == 0
        from
      else
        "#{from}#{idx}"
      end
    end

end

And this brings us to our last class of today the Repository class. We mixin the DB::MetaData module we created yesterday. Define a read_only property entities, make sure we can set a namespace for our generated entities. The first step is to transform the meta data into data that we can use to represent a LightSpeed Entity. The second and last step of today is to generate the entities with the lightspeed meta data. We have to skip the primary key because that is defined by convention in LightSpeed.

class LightSpeedRepository

  include DB::MetaData

  attr_reader :entities
  attr_accessor :namespace

  def initialize()
    @entities = []
    super
  end

  def to_light_speed_meta_data
    tables.collect do |table|
      col_infos = column_info_for table[:name]

      field_infos = col_infos.collect do |col_info|
        {
          :name => col_info[:name].underscore,
          :sql_type => col_info[:sql_type],
          :max_length => col_info[:max_length].to_i,
          :nullable => !col_info[:is_nullable].to_i.zero?,
          :precision => col_info[:precision],
          :foreign_key => foreign_key?(col_info),
          :primary_key => primary_key?(col_info),
          :unique => !col_info[:is_unique].to_i.zero?
        }
      end

      { :table_name => table[:name], :class_name => table[:name].singularize.camelize, :fields => field_infos }
    end
  end

  def generate_entities
    meta_data = to_light_speed_meta_data
    meta_data.each do |md|
      @entities << generate_entity(md)
    end
    @entities
  end

  def generate_entity(meta_data)
    entity = LightSpeedEntity.new
    entity.name = meta_data[:class_name]
    entity.namespace = namespace

    meta_data[:fields].each do |fi|
      prop = LightSpeedProperty.new(fi)

      prop.name = entity.create_property_name_from prop.name.underscore.camelize
      entity.properties << prop unless prop.primary_key?
      entity.belongs_to << generate_belongs_to_relation(meta_data, fi, entity) if prop.foreign_key?

    end

    entity.has_many = generate_has_many_relations meta_data, entity
    generate_through_associations meta_data, entity

    entity
  end

  private

    def generate_belongs_to_relation(meta_data, field_info, entity)
      {
        :name => entity.create_property_name_from(field_info[:name].underscore.humanize.titleize.gsub(/\s/,'')),
        :class_name => get_belongs_to_table(meta_data[:table_name], field_info[:name]).underscore.camelize.singularize
      }
    end

    def generate_has_many_relations(meta_data, entity)
      hms = collect_has_many_relations meta_data[:table_name]
      hms.collect do |hm|
         hm[:name] = entity.create_property_name_from hm[:class_name].pluralize
         hm
      end

    end

    def generate_through_associations(meta_data, entity)
      tas = collect_through_associations(meta_data[:table_name])
      tas.each do |ta|
        ta[:end_tables].each do |et|
          entity.through_associations << {
            :through => ta[:through_table].classify.singularize,
            :class_name => et.camelize.singularize,
            :name => entity.create_property_name_from(et.camelize)
          }
        end
      end
    end
end

Technorati Tags: ,,

del.icio.us Tags: ,,

Using Ruby to Generate LightSpeed models – Part 2

This is the second post in the series on generating LightSpeed entities with the help from ruby.
In the previous post we connected successfully to the database and were able to execute some sql.

At the end of the series I’ll make the code downloadable.

Today I’d like to talk about the metadata we’ll be needing from the database. We’re going to need a list of tables, we’re going to need to know about the columns of each table. Furthermore we want to exclude the primary keys in the case of LightSpeed. And we also want to know about relationships whether they are has many, belongs to or has many and belongs to many.

I put all this in a separate module because I’ll probably need that meta data for another thing later :) . The above requirements translate in the following spec:

LightSpeedRepository DB::MetaData
- should have meta data
- should resolve the table name from a string
- should resolve the table name from a hash
- should identify a given column as not being a foreign key
- should identify a foreign key given a valid column info
- should not identify a given column as being a primary key
- should identify a given column as being a primary key
- should not identify a table as a join table under the correct conditions
- should identify a table as a join table under the correct conditions
- should return an empty array of has many relations when there are none
- should return the has many relations given a table
- should return the end point tables for a given through association
- should return the through associations

The first thing we’re going to need are the sql statements. At this point I only need the statements for sql 2005 so and these are the ones I used.

def self.sql_statements
      {
        :tables => "SELECT table_name as name FROM information_schema.Tables Where table_type='Base Table' ORDER BY table_name",
        :column_info => "select object_name(c.object_id) as table_name, c.column_id, c.name, type_name(system_type_id) as sql_type, max_length, is_nullable, precision, scale,
              convert(bit,(Select COUNT(*) from sys.indexes as i
                inner join sys.index_columns as ic
                  on ic.index_id = i.index_id and ic.object_id = i.object_id
                inner join sys.columns as c2 on ic.column_id = c2.column_id and i.object_id = c2.object_id
              WHERE i.is_primary_key = 0
                and i.is_unique_constraint = 0 and ic.column_id = c.column_id and i.object_id=c.object_id)) as is_index,
              is_identity,
              is_computed,
              convert(bit,(Select Count(*) from sys.indexes as i inner join sys.index_columns as ic
                  on ic.index_id = i.index_id and ic.object_id = i.object_id
                inner join sys.columns as c2 on ic.column_id = c2.column_id and i.object_id = c2.object_id
              WHERE (i.is_unique_constraint = 1) and ic.column_id = c.column_id and i.object_id=c.object_id)) as is_unique
              from sys.columns as c
              WHERE object_name(c.object_id)  in (select table_name	FROM information_schema.Tables WHERE table_type = 'Base Table')
              order by table_name",
        :primary_keys => "SELECT i.name AS index_name,ic.index_column_id,key_ordinal,c.name AS column_name,TYPE_NAME(c.user_type_id)AS column_type
                    ,is_identity,OBJECT_NAME(i.object_id) as table_name FROM sys.indexes AS i INNER JOIN sys.index_columns AS ic ON
                    i.object_id = ic.object_id AND i.index_id = ic.index_id INNER JOIN sys.columns AS c ON ic.object_id = c.object_id
                    AND c.column_id = ic.column_id WHERE i.is_primary_key = 1 order by table_name",
        :foreign_keys => "SELECT f.name AS foreign_key_name, object_name(f.parent_object_id) AS table_name , col_name(fc.parent_object_id, fc.parent_column_id) AS child_id
                    ,object_name (f.referenced_object_id) AS parent_table ,col_name(fc.referenced_object_id, fc.referenced_column_id) AS parent_id FROM sys.foreign_keys AS f
                    INNER JOIN sys.foreign_key_columns AS fc ON f.object_id = fc.constraint_object_id where OBJECT_NAME(f.parent_object_id) not in ('sysdiagrams')  order by table_name"
       }
     end

Those statements contain all the data we need an probably a little bit more too, if we add a little metaprogramming we can have ruby generate that metadata data for us :)

    def populate
      DB::MetaData.sql_statements.each do |key, value|
        instance_variable_set("@"+key.to_s, @db.fetch_all(value))
      end
    end

    sql_statements.each_key do |key|
       define_method("#{key}_for") do |table|
         send(key, table).select { |item| item[:table_name] == table_name(table) }
       end unless key == :tables
     end

So now we’ve satisfied our first spec the module now contains all the meta data we need. The rest of the specs require far less code than what we wrote here. Below you’ll find the code needed to satisfy all of the specs. It are just a couple of methods that check some conditions and a couple of predicates we’re going to need later on. The get_endpoint_tables method is the only one that doesn’t explain itself easily. That method returns the table names from tables that are the second level in a has many and belongs to many scenario.

module DB
  module MetaData
    attr_accessor :tables, :primary_keys, :foreign_keys, :column_info

    def initialize
      @db = DB::DbiSqlServer.new
      populate
    end

    def populate
      DB::MetaData.sql_statements.each do |key, value|
        instance_variable_set("@"+key.to_s, @db.fetch_all(value))
      end
    end

    def collect_has_many_relations(table)
      fks = foreign_keys.select { |fk| fk[:parent_table] ==  table_name(table)  }

      fks.collect  do |fk|
        unless fk[:table_name].nil?
          { :table_name => fk[:table_name].underscore, :class_name => fk[:table_name].singularize.underscore.camelize }
        end
       end.compact
    end

    def collect_through_associations(table)
      fks = foreign_keys.select { |fk| fk[:parent_table] ==  table_name(table)  }

      fks.collect do |fk|
          { :through_table => fk[:table_name].underscore, :end_tables => get_endpoint_tables(table, fk[:table_name]) } if join_table?(fk[:table_name])
      end.compact
    end

    def get_endpoint_tables(table, through_table)
      fks = foreign_keys.select { |fk| fk[:table_name] == table_name(through_table) and fk[:parent_table] != table_name(table)  }
      fks.collect { |fk| fk[:parent_table].underscore unless fk[:parent_table].nil?  }.compact
    end

    def get_belongs_to_table(table, column_name)
      fks = foreign_keys.select { |fk|  fk[:table_name] == table_name(table) and fk[:child_id] = column_name }
      return fks[0][:parent_table] if fks.size > 0
      nil
    end

    def join_table?(table)
      fks = foreign_keys_for table_name(table)
      fks.size > 1
    end

    def primary_key?(column_info)
      pks = primary_keys.select { |pk| pk[:table_name] == column_info[:table_name] and pk[:column_name] == column_info[:name]   }

      pks.size > 0
    end

    def foreign_key?(column_info)
      fks = foreign_keys.select { |fk| fk[:table_name] == column_info[:table_name] and fk[:child_id] == column_info[:name]  }
      fks.size > 0
    end

    def table_name(table)
      table.is_a?(Hash) ? table[:name] : table
    end

    def self.sql_statements
      {
        :tables => "SELECT table_name as name FROM information_schema.Tables Where table_type='Base Table' ORDER BY table_name",
        :column_info => "select object_name(c.object_id) as table_name, c.column_id, c.name, type_name(system_type_id) as sql_type, max_length, is_nullable, precision, scale,
              convert(bit,(Select COUNT(*) from sys.indexes as i
                inner join sys.index_columns as ic
                  on ic.index_id = i.index_id and ic.object_id = i.object_id
                inner join sys.columns as c2 on ic.column_id = c2.column_id and i.object_id = c2.object_id
              WHERE i.is_primary_key = 0
                and i.is_unique_constraint = 0 and ic.column_id = c.column_id and i.object_id=c.object_id)) as is_index,
              is_identity,
              is_computed,
              convert(bit,(Select Count(*) from sys.indexes as i inner join sys.index_columns as ic
                  on ic.index_id = i.index_id and ic.object_id = i.object_id
                inner join sys.columns as c2 on ic.column_id = c2.column_id and i.object_id = c2.object_id
              WHERE (i.is_unique_constraint = 1) and ic.column_id = c.column_id and i.object_id=c.object_id)) as is_unique
              from sys.columns as c
              WHERE object_name(c.object_id)  in (select table_name	FROM information_schema.Tables WHERE table_type = 'Base Table')
              order by table_name",
        :primary_keys => "SELECT i.name AS index_name,ic.index_column_id,key_ordinal,c.name AS column_name,TYPE_NAME(c.user_type_id)AS column_type
                    ,is_identity,OBJECT_NAME(i.object_id) as table_name FROM sys.indexes AS i INNER JOIN sys.index_columns AS ic ON
                    i.object_id = ic.object_id AND i.index_id = ic.index_id INNER JOIN sys.columns AS c ON ic.object_id = c.object_id
                    AND c.column_id = ic.column_id WHERE i.is_primary_key = 1 order by table_name",
        :foreign_keys => "SELECT f.name AS foreign_key_name, object_name(f.parent_object_id) AS table_name , col_name(fc.parent_object_id, fc.parent_column_id) AS child_id
                    ,object_name (f.referenced_object_id) AS parent_table ,col_name(fc.referenced_object_id, fc.referenced_column_id) AS parent_id FROM sys.foreign_keys AS f
                    INNER JOIN sys.foreign_key_columns AS fc ON f.object_id = fc.constraint_object_id where OBJECT_NAME(f.parent_object_id) not in ('sysdiagrams')  order by table_name"
       }
     end

     sql_statements.each_key do |key|
       define_method("#{key}_for") do |table|
         send(key, table).select { |item| item[:table_name] == table_name(table) }
       end unless key == :tables
     end

  end

end
Page 3 of 41234