Getting Started With IronRuby And RSpec

This article is based on a prerelease version of IronRuby. All information is subject to change.

 

This article discusses:

§  Ruby and Duck Typing

§  Ruby and the Microsoft .NET Framework

§  Using IronRuby and RSpec

This article uses the following technologies:
IronRuby

Code download available from the MSDN Code Gallery
Browse the Code Online

 Contents

Defining Requirements and Examples in Code
Ruby and Duck Typing
Ruby and MetaProgramming
Ruby and the .NET Framework
Ruby and the CLR
Inside IronRuby
Testing a C# App with IronRuby
Moving Forward

"That's not what we asked for!" I'm sure most developers have heard this cry from a customer shortly after delivering the latest build. The customer could be yelling at the poor developer for various reasons—maybe the requirements were not correctly understood or part of the system simply did not work.

Because customers are often unclear as to their own requirements, they will opt for the safety of the 100-page technical document that tries to define everything the system might have to do. Meanwhile, the developer struggles with undocumented legacy code, trying to understand how the app is intended to work while attempting to implement new requirements without breaking anything.

Times are changing. New approaches to software development are aimed at solving these problems, helping you to meet the customer requirements at the first attempt without causing failures in the process. These approaches are taking advantage of languages such as Ruby, allowing you to create easily readable and maintainable code with much shorter development iterations.

In this article, I will introduce you to Ruby and IronRuby and demonstrate some basics of Ruby interoperating with Microsoft .NET Framework-based code. I also explain how frameworks such as RSpec can be used to generate examples of how objects are intended to behave, providing both documentation and verification that the system is built correctly. This will set the stage for a future article in which I will explain acceptance testing in detail and demonstrate creating acceptance tests with IronRuby.

 

Defining Requirements and Examples in Code

To help write examples and define requirements you need a framework. There are many different approaches to writing automated acceptance tests or executable specifications. Some people use standard xUnit test frameworks successfully, while others use Fit and Fitness frameworks. I have found the best approach is to use Behavior-Driven Development (BDD). Dan North devised a BDD framework called JBehave as a way to define scenarios that describe the application's behavior in a way that can be communicated to the whole team, regardless of technical ability.

JBehave was the result of problems North faced with Test-Driven Development (TDD) and was implemented for the Java language. Later, North created RBehave, which has since been integrated into RSpec, a popular framework for Ruby. RSpec accommodates two different approaches to BDD: Dan North's approach, based on stories and scenarios to describe application's behavior, and Dave Astels' approach , which is more focused on creating examples at an object level.

C# does have some BDD frameworks, such as NSpec and NBehave. The main problem with writing tests in C# is that the true intent of the test is often hidden due to fact that you have extra structural elements and metadata such as braces, public and private. Overall, I don't think what is on offer via C# and NSpec/NBehave can match what is available via IronRuby and RSpec. Previously, this would have been a major problem for C# developers, as you could not have used the Ruby-based RSpec to test C# apps. With IronRuby, this is no longer a problem.

While still early in development, IronRuby is taking advantage of the Dynamic Language Runtime (DLR) to create an implementation of the Ruby language on top of the CLR. With IronRuby, you can employ existing Ruby apps and frameworks together with the .NET Framework and .NET-compliant languages. The result is that you can use the Ruby language and RSpec to test C# applications!

As a language, Ruby is concise, allowing you to write less code and express it in a much more natural manner, making your code easier to maintain. For example, to read all the lines of text from a file and write them out to the console, you would write this:

 

File.readlines('AboutMicrosoft.txt').map {|line| puts line}

The code opens the file AboutMicrosoft.txt and reads all the lines, passing each line into the block, with the line as a parameter that is then written to the console. In Ruby, a block is the collection of statements between the braces and is similar to invoking a method in C#, which uses the yield statement to return control to the calling method.

Its natural language approach is one of the reasons why Ruby is excellent to use when testing. The tests, or scenarios in this case, are much more readable.

 

Ruby and Duck Typing

One of the reasons why Ruby and dynamic languages are easier to read is due to how they handle typing. Instead of the developer defining a type, when the Ruby code is interrupted, the type of the variable is inferred. The language is still strongly typed, but it determines the variable's type dynamically.

With C#, interfaces allow for decoupling of objects while still defining a contract for the implementation. With Ruby, there is no need for contracts and interfaces. Instead, if the object has an implementation of the method you are calling, then it's called. If it doesn't, then an error is returned. As such, the contract for the object is defined by its implementation, not its relationship to other parts of the code.

To demonstrate this, I created a method that accepts a value, the type of which will be inferred at run time. It will then output Hello plus the value:

 

   def SayHello(val)

     puts "Hello #{val}"

   end

Because of the way types are handled, you can reuse the same method for both strings and integers without changing any of the code:

 

   SayHello("Test") => "Hello Test"

   SayHello(5) => "Hello 5"

I could have also have called the method without brackets, as these are optional in Ruby and can make the syntax much more readable:

 

   SayHello "Test" => "Hello Test"

This can be achieved in C# using object, but let's look at how this works with more complex objects. I defined a new method to output the result of a call to GetName. As long as the value of the parameter n implements a method called GetName, the code will work:

 

def outputName(n)

  puts n.GetName()

end

Here are two unrelated classes:

 

class Person

  attr_accessor :name

  def GetName()

    @name

  end

end

class Product

  def GetName()

    "The product with no name"

  end

end

Person has an accessor allowing the name variable to be set, while Product returns a hardcoded value. In Ruby, there is no need to write the return statement, the last result of the last line of code of a method is returned by default.

If you call the method outputName, the GetName method is called, demonstrating how Ruby's typing can enhance code reusability since we are not fixed to a single type:

 

outputName(Product.new) => "The product with no name"

$x = Person.new

$x.name = "Ben Hall"

outputName($x) => "Ben Hall"

If you call the method using an argument that doesn't implement GetName, then a NoMethodError is raised:

 

outputName("MyName") => :1:in 'outputName': \

  undefined method 'GetName' \

  for MyName:String (NoMethodError)

While this concept concerns some people, it can be useful; it allows for improved testability and application design, especially when testing C# applications, which I will discuss in the next section.

 

Ruby and MetaProgramming

Similar to other dynamic languages, Ruby embraces the concept of meta-programming. This allows you to extend any class or object at run time, enabling you to define methods and behavior at run time, giving you the ability to develop a self-modifying application. This is extremely useful in dynamic languages, especially in unit testing, as it allows you to customize the behavior of objects and methods to your own requirements.

In a similar fashion, you can extend the built-in Ruby classes to provide your own functionally, much like the extension methods in C# 3.0. With C#, there are various restrictions about the methods you can create. For example, they have to be static and in a separate class. This all harms readability. With Ruby, you just create a normal method on an existing class. For example, Integer is a class to handle whole numbers. Integer has a series of methods, but it does not have a method to say whether the number is even.

To create such a method, you simply create a new class with the same name (Integer) and define the new method. The question mark at the end of the method denotes that a Boolean is returned with the aim of making it easier to read:

 

class Integer

   def even?()

       self.abs % 2 == 0

   end

end

puts 2.even? => true

puts 1.even? => false

 

Ruby and the .NET Framework

In place of the old debate about whether to use a static or dynamic language, IronRuby lets you use the right language for the job. If it makes more sense to use C#, then you can use C#. If you need a more dynamic approach, you can easily use IronRuby, taking advantage of its interoperability with .NET and the dynamic nature of the Ruby language. I anticipate people using a mixture of different languages and technologies, such as C# for the main application and Ruby for testing.

The DLR provides the foundation to enable .NET interop. With this in place, you can take advantage of UI technology such as Windows Forms, Windows Presentation Foundation (WPF), and Silverlight while writing application code in Ruby.

Taking advantage of WPF from IronRuby is easy. When the following code is executed, you can have a fully functioning WPF window created from IronRuby. You still must reference mscorlib and the two WPF assemblies shipped with the .NET Framework 3.0, as you did with C#, using the require statements:

 

require 'mscorlib'

require 'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'

require 'PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'

You can now create and interact with the WPF objects. First, create aliases for all the objects you want to use. This makes the code cleaner, as you won't need to access the object via its namespace:

 

Window = System::Windows::Window

Application = System::Windows::Application

Button = System::Windows::Controls::Button

Next, create a WPF window and give it a title:

 

win = Window.new

win.title = 'IronRuby and WPF Interop'

Create a button in the same fashion:

 

mainButton = Button.new

mainButton.content = 'I'm a WPF button — press me'

In both instances I'm using the alias to access the full name of the object. When the button is clicked, I want to display a MessageBox to the user. As you would in C#, you can subscribe to the event and provide a block that is called when the click event is invoked:

 

mainButton.click do |sender, args|

  System::Windows::MessageBox.Show("Created using IronRuby!")

end

Finally, set the content of the window to be the button, create a new Application object, and launch the window:

 

win.content = mainButton

my_app = Application.new

my_app.run win

Figure 1 Using IronRuby to Create a WPF App

At this point, a fully interactive WPF window will be displayed with the click event correctly wired up as shown in Figure 1. I interacted with the .NET Framework in the same way I would interact with Ruby libraries. One of the core principals set out by John Lam and team was to stay true to the Ruby language. This is an important principal for adoption by current Ruby developers, who won't want to change the way they create Ruby applications just because they're moving to IronRuby. Instead they can access all the rich .NET code in the same way.

 

Ruby and the CLR

Here is another example. If you attempt to access AddRange on IEnumerable, in C# the code would look like this:

 

ArrayList list = new ArrayList();

list.AddRange(new [] { 1,2,3,4 });

However, with Ruby, the accepted convention is for words within method names to be separated by underscores to improve readability. Creating a separate library to follow this convention would be too time-consuming and error-prone, and it would not support additional third-party development.

Instead, for CLR objects, IronRuby translates Ruby method calls into the equivalent method name for the CLR:

 

   $list = ArrayList.new

   $list.add_range([1,2,3,4])

Here I am accessing the AddRange method following Ruby's approach, which is lower case with words separated by an underscore. If you prefer, you can keep with the CLR naming convention as the method name still exists from Ruby:

 

$list.AddRange([1,2,3,4])

Both work the same; it's just a personal preference which to use.

As I mentioned, Ruby infers the type of objects at run time, and this is the same when handling C# objects. Consider a series of C# methods that return different object types, while the same object is being returned, the method signature returns either the interface or the concrete type. Within my example, I have an interface that defines a single HelloWorld method:

 

public interface IHello {

  string HelloWorld();   

}

I created a Hello4Times class that inherits from Hello3Times, which inherits from the interface but has implemented three extra Hello­World methods. Within the class I define a new method called HelloMethodOn4Times that calls the base implementation:

 

public class Hello4Times : Hello3Times {

  public string HelloMethodOn4Times () {

    return base.HelloWorld();

  }

}

I then defined a static class and methods that will return a new instance of the Hello4Times class but return it to the calling code as an interface. This means the calling code should only know about HelloWorld, not any of the additional methods defined:

 

public static class HelloWorld {

  public static IHello ReturnHello4TimesAsInterface() {

    return new Hello4Times();

  }

 

In my Ruby code, I have two method calls. The first call is on the interface-defined method, which you would expect to work without a problem. However, the second call is to the method on the concrete class. IronRuby has determined the type of the object being returned and can dispatch the method calls:

 

puts InteropSample::HelloWorld.ReturnHello4TimesAsInterface.HelloWorld

puts interopSample::HelloWorld.ReturnHello4TimesAsInterface.HelloMethodOn4Times

All of this happens without you ever needing to worry about casting the object to the correct type to call the method.

Following this theme, you can extend .NET objects in exactly the same fashion as you can with Ruby objects. When displaying a MessageBox, I don't want to keep defining which icons and buttons to use. Instead, I just want to provide a message.

Maybe you don't like the built-in functionality and want to extend the actual MessageBox class for seamless interaction. With Ruby, this is simple. You define a new class that is the same as the built in MessageBox within WPF. You then create a new method on the class that simply calls the show method with various defaults:

 

class System::Windows::MessageBox

  def self.ShowMessage(msg)

    System::Windows::MessageBox.Show(msg, msg, \

      System::Windows::MessageBoxButton.OK, \

      System::Windows::MessageBoxImage.Stop)

  end

end

After executing this block of code

 

System::Windows::MessageBox.ShowMessage( \

  "I'm going to show you a message")

you can call the ShowMessage method, the result being the message box shown in Figure 2 .

Figure 2 Displaying a Custom MessageBox from Ruby

 

Inside IronRuby

How is interop possible? The answer is the DLR. At a high level, when you execute Ruby code using IronRuby, a lot of work takes place behind the scenes. First, the code you write is tokenized and parsed by the IronRuby engine. The parsed code is then converted into the DLR's Abstract Syntax Tree (AST). This is a standardized AST that all language implementations, such as IronPython, need to provide to the DLR in order to execute the code.

Once the DLR has the AST, it converts the tree into Intermediate Language (IL). All .NET code is compiled down into IL to provide a generic language for the CLR. This is how IronRuby can interoperate with the .NET Framework—behind the scenes, all the code is executed as IL instructions. After the DLR has completed the conversion, it passes the IL to the CLR for execution and the result is returned to IronRuby.

During this process there are additional steps to improve performance and reliability, such as caching. For a deeper explanation, I recommend reading Bill Chiles's CLR Inside Out column " Iron­Python and the Dynamic Language Runtime " in the October 2007 issue of MSDN Magazine .

Within a separate assembly there is a hosting API that allows you to embed IronRuby within your own applications, allowing you to execute Ruby code from C# or users to execute their own code.

For more about the IronRuby implementation, download the entire source code from RubyForge . IronRuby is an open-source project implemented in C# and is an excellent example of a dynamic language implementation. IronPython is available as an open source project hosted at CodePlex, and it includes a sample language called ToyScript if you want an introduction into how the DLR works.

To ensure continued compatibility with the core Ruby platform, the IronRuby team is using RubySpecs. This is a shared set of examples based around how the Ruby language should be implemented. The aim of RubySpecs is to ensure different implementations including Matz's Ruby Interpreter (MRI), JRuby, MacRuby, and IronRuby have the same behavior. RubySpecs uses a syntax-compatible version of RSpec called MSpec. These are viewed as the acceptance tests for the IronRuby implementation.

 

Testing a C# App with IronRuby

For many years now, the awareness and practice of TDD has increased as a way to develop software, resulting in higher quality code, in terms of design and maintainability, along with fewer defects along the way. With IronRuby, I can use the RSpec specification framework and runner to provide examples of how my C# objects work, following a BDD approach instead of TDD.

While the scenario framework, which I'll cover in a future article, is more aligned with acceptance testing at the app level, the specification framework is more aligned to developers writing their own specifications about the code they are just about to implement and its expected behavior. The specification framework is based around providing examples describing the behavior at the object level. These examples can be run to verify that the implementation of the system is still working as the developer expects while providing documentation about how the objects are expected to behave.

This is an important distinction compared to unit-testing frameworks such as NUnit and MbUnit. Both of those use test attributes to indicate a method is a test for the system. RSpec takes a different approach. RSpec says that each method is an example of how the code is intended to work. While the distinction is subtle, it changes the way you write the examples, including the terminology you use, the way you organize the methods, and how easily the concept can be understood compared to TDD. With BDD and RSpec, together with the close integration of IronRuby and the .NET Framework, you can start using IronRuby to test C# applications. In terms of an example, the classic RSpec example is the Bowling game.

First, you need to access the bowling game's implementation in a C# assembly:

 

require File.dirname(__FILE__) + \

  '/InteropSamples/Bowling/Bowling/bin/Debug/bowling.dll'

You then need to access RSpec:

 

require 'rubygems'

require 'spec'

Now you can begin writing examples. This is an example showing how bowling implementation should work. RSpec has a Domain Specific Language (DSL) that you must follow for the examples to be executable. The first part of the DSL is the describe block. Here you simply state the object you are "describing" together with an optional description. In this case, I define the Bowling object, which will be implemented in C#:

 

describe Bowling, " defines the bowling game" do

To improve readability, any setup will be performed within a before block:

 

  before(:each) do 

    @bowling = Bowling.new

  End

I am now in a position to interact with the object, create the example and verify that it works correctly. I use the "it" block, providing a string stating the context of the example and what it is demonstrating. I then write the section of code that interacts with the system and verifies the correct action has happened:

 

  it "should score 0 for a gutter game" do

    20.times { @bowling.hit(0) }

    @bowling.score.should == 0

  end

end

The C# implementation simply looks like this:

 

public class Bowling {

  public int Score { get; set; }

  public void Hit(int pins)

    { Score += pins; }

}

Finally, execute this to verify that the bowling example works as expected with RSpec testing C# objects:

 

>ir bowling_spec.rb

.

Finished in 1.0458315 seconds

1 example, 0 failures

If I wanted a more detailed report, I could select the format to be specdoc. This outputs the object description together with all the examples, stating if they have passed or not:

 

>ir bowling_spec.rb --format specdoc

Bowling defines the bowling game

- should score 0 for a gutter game

Finished in 1.43728 seconds

1 example, 0 failures

At the time of writing, the IronRuby team is not regularly shipping binaries. The team has said it is waiting until it is happy with the implementation, compatibility, and performance before releasing official binaries. However, because the source code is freely available, you can download the code and build it yourself.

To download the source code, you will need to have a Git source control client, such as msysgit , installed. The IronRuby source control is available online. If you would like more information then I recommend you visit the IronRuby project site or my blog post " Downloading IronRuby from GitHub ." Once you have downloaded the source code, you can compile the assembly either with Visual Studio using the IronRuby.sln solution file or, if you have MRI installed, then you can use the command:

 

rake compile

Once you have IronRuby compiled, you must download various libraries, such as RSpec, to gain the extra functionality. Ruby has a concept of RubyGems where the gems are packages you can download to gain the functionality and the additional dependencies. To download RSpec, type the following at a command prompt:

 

gem install rspec

You would now be able to access the RSpec library from within your Ruby code. However, at the time of writing, RSpec does not work with IronRuby due to one or two bugs. Hopefully by the time this article is published RSpec should be working with IronRuby. To find out the status of the RSpec support, see the RSpec Web site or the IronRuby mailing list .

If the bug hasn't been fixed, I have made a binary with the bug fixed, together with the RSpec library in place . (Note: don't use this version once the team has fixed the problem.)

Once you have the correct binaries in place, ir.exe is the IronRuby interpreter that can execute your code. If you simply launch the app from the command line, you'll enter the interactive console. In Ruby, code is executed on a line-by-line basis, excellent for learning and debugging, but also for running short commands to solve daily problems. If you provide a file name as a parameter, then the file is executed with the results output to the console.

 

Moving Forward

In my next article, I will introduce the concept of acceptance testing and how it can improve communication between customer and developer. I will demonstrate how acceptance testing can be automated using IronRuby and RSpec to verify .NET applications and create an executable specification for the system.

 

Ben Hall is a C# developer/tester with a strong passion for software development and loves writing code. Ben works at Red Gate Software in the U.K. as a Test Engineer and enjoys exploring different ways of testing software, including both manual and automated testing, focusing on the best ways to test different types of applications. Ben is a C# MVP and maintains a blog at Blog.BenHall.me.uk .

 
 
 
 
 
 
 
本文基于 IronRuby 的预发布版本。 内容可能会和正式版本有出入。


本文讨论:
  • 拼音、 和鸭子类型化
  • 拼音、 和 Microsoft.NET Framework
  • 使用 IronRuby 和 RSpec
本文涉及以下技术:
IronRuby
代码下载可从 MSDN 代码库
浏览代码联机
在代码中定义要求和示例
拼音、 和鸭子类型化
拼写和 MetaProgramming
Ruby 和.NET Framework
拼音、 和 CLR
内部 IronRuby
测试与 IronRuby 一个 C# 应用程序
向前移动
"这不是我们要求 !" 我确信大多数开发人员必须提供最新版本后不久听到来自客户的咆哮。 客户可能由于各种原因在叫嚷,较差的开发人员也许要求不正确理解或系统的部分只是做不工作。
因为客户通常要求的不清楚,他们将选择对试图定义所有系统可能需要执行的 100 页技术文档的安全。 同时,与未记录旧代码,尝试了解如何应用程序为了工作而不会破坏任何实现新的要求时,开发人员 struggles。
将更改时间。 软件开发的新方法旨在解决帮助您满足而不会故障导致进程中的客户要求在第一次尝试在这些问题。 这些方法利用语言如拼音、,使您可以轻松地读取和易于维护的代码创建更短的开发迭代。
此文章中, 我将向您介绍 Ruby 和 IronRuby 并演示的拼写与 Microsoft 基于.NET Framework 的代码进行互操作的一些基础知识。 我还介绍了如何使用如 RSpec 框架生成的方式对象为了行为,示例提供文档和系统生成正确的验证。 这将设置为将来的项目中我将介绍验收测试详细信息,并演示 IronRuby 创建验收测试阶段。


 

在代码中定义要求和示例
为了帮助编写的示例,并定义要求需要一个框架。 有许多不同的方法编写自动化的验收测试或可执行文件的规范。 其他人使用调整和适用性的框架时,有些人成功,使用标准 xUnit 测试框架。 我已发现最好的方法是使用行为驱动开发 (BDD)。 Dan North 设计了一个 BDD Framework 调用 JBehave 作为一种定义方案,介绍一种方式可以将传递到整个团队无论技术的功能的应用程序的行为。
JBehave 是的问题 North 面临用测试驱动开发 (TDD) 和 Java 语言中已实现的结果。 以后,North 创建由于已集成到 RSpec 的拼音、 流行框架的 RBehave。 RSpec 适合 BDD 两种不同方法: 根据文章和方案描述应用程序的行为的 Dan North 方法和 Dave Astels 方法 的更多集中在对象级别创建的示例。
C# 也有一些 BDD 框架 NSpec 和 NBehave。 在 C# 编写测试,主要问题是满足测试的目的通常由于而隐藏您有其他结构元素和元数据 (如大括号公钥和私钥的事实。 总体,我不认为通过 C# 的提供程序是,和 NSpec / NBehave 可以匹配是通过 IronRuby 和 RSpec 可用。 以前,这将已主要问题的 C# 开发,您可能不具有使用拼音、 基于 RSpec 测试 C# 应用程序。 与 IronRuby,这不是一个问题。
时仍在早期开发,IronRuby 利用动态语言运行时 (DLR) 以创建在 CLR 之上拼写的语言的实现。 与 IronRuby,您可以使用现有的拼音应用程序和一起与.NET Framework 和.NET 兼容语言的框架。 结果是可以使用拼写的语言和 RSpec 测试 C# 应用程序 !
一种语言拼音、 是简洁允许您编写较少的代码和 Express 它以更自然方式使您的代码更易于维护。 是例如从文件读取的文本的所有行和写入这些出控制台,您将编写此:

 

File.readlines('AboutMicrosoft.txt').map {|line| puts line}
该代码打开文件 AboutMicrosoft.txt,并显示所有行,将每行传递到包含与一个参数,然后写入控制台在行在块。 在拼音、,块的大括号之间的语句集合并且为类似于调用 C# 中,使用 yield 语句将控制返回给调用方法的方法。
其自然语言方法是拼音、 测试时使用极佳,原因之一。 在测试,或方案在这种情况下是得更容易阅读。


 

拼音、 和鸭子类型化
之一为何拼写和动态语言是易于阅读的原因是由于如何处理输入。 而不是开发人员在中断拼音代码定义一种类型变量的类型推断。 所使用的语言仍强类型的但动态确定变量的类型。
使用 C# 接口允许 decoupling 对象的同时还定义用于实现一个约定。 与拼音、,没有必要为合同和接口。 相反,如果对象具有要调用该方法的实现,然后它的调用。 如果它所不则会返回错误。 在这种情况下,由它的实现不与代码的其他部分的关系定义对象的合同。
为了说明这,我创建接受的值的类型将推断在运行时的方法。 它然后将输出 Hello 加上值:

 

   def SayHello(val)
     puts "Hello #{val}"
   end
因处理类型的方式,可以而不更改任何代码重复使用相同的方法为字符串和整数:

 

   SayHello("Test") => "Hello Test"
   SayHello(5) => "Hello 5"
我可能有也具有调用该方法无括号,这些可选在拼音、 以及可以使语法得更具可读性:

 

   SayHello "Test" => "Hello Test"
这可以实现 C# 中使用对象,但让我们看看这如何使用更复杂的对象。 我定义一个新方法输出 GetName 调用的结果。 只要参数 n 的值实现方法调用 GetName,将工作代码:

 

def outputName(n)
  puts n.GetName()
end 
下面是两个不相关的类:

 

class Person
  attr_accessor :name
  def GetName()
    @name
  end
end
class Product
  def GetName()
    "The product with no name"
  end
end
用户有一个访问器,允许名称变量将设置而产品返回硬编码值。 在拼音、,没有需要写入该返回的语句方法的代码的最后一行的最后结果由默认值返回。
如果您在调用方法 outputName 将 GetName 调用方法,演示如何 Ruby 的键入可以增强代码可重用性由于我们不固定为一种类型:

 

outputName(Product.new) => "The product with no name"
$x = Person.new
$x.name = "Ben Hall"
outputName($x) => "Ben Hall"
如果在调用使用一个参数,不实现 GetName 方法,一个 NoMethodError 将会引发:

 

outputName("MyName") => :1:in 'outputName': \
  undefined method 'GetName' \
  for MyName:String (NoMethodError)
这一概念与某些人时, 它可以是非常有用 ; 它可以改进的 testability 和应用程序设计尤其是当测试我将在下一节中讨论 C# 应用程序。


 

拼写和 MetaProgramming
类似于其他动态语言,拼音、 embraces 的概念元编程。 这允许您扩展任何类或对象运行时使您可以定义方法和行为运行时让您能够开发 self-modifying 应用程序。 这是非常有用,尤其是在单元测试,之类的动态语言中它允许您自定义对象和您自己的要求的方法的行为。
以类似的方式可以扩展以提供您自己功能,内置的拼音类非常像在 C# 3.0 中的扩展方法。 使用 C# 有方法可以创建的各种限制。 是例如他们必须是静态的和单独的类。 此所有损害可读性。 与拼音、,您只是创建一个正常方法的现有类上。 是例如整数是处理整数类。 整数有一系列的方法,但它没有方法是否甚至是数。
若要创建这样的方法,您只需创建一个新类具有相同的名称 (整数),定义新的方法。 该方法的结尾处的问号表示一个 Boolean 类型值返回使易于阅读的目的:

 

class Integer
   def even?()
       self.abs % 2 == 0
   end
end
puts 2.even? => true
puts 1.even? => false


 

Ruby 和.NET Framework
代替有关是否使用静态或动态语言旧的争论,IronRuby 可以为作业使用正确的语言。 如果有意义更多使用 C#,您可以使用 C#。 如果您需要一个更具动态方法,可以轻松地使用 IronRuby,利用与.NET 的互操作性和拼写的语言的动态特征。 我预计使用多种不同语言和等不是针对主应用程序和用于测试的拼音、 C# 的技术的人。
DLR 提供了启用.NET 互操作。 与此位置,您可以利用用户界面技术 (如 Windows 窗体、 Windows Presentation Foundation (WPF) 和 Silverlight 在拼音、 写入应用程序代码时。
利用 WPF 从 IronRuby 很容易。 下面的代码执行时, 您可以从 IronRuby 创建一个功能完善的 WPF 窗口。 您仍然必须引用 mscorlib 和两个 WPF 程序集提供了在 N 一样使用 C# 使用在需要语句:

 

require 'mscorlib'
require 'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
require 'PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
现在可以创建和与 WPF 对象的交互。 首先,创建为您要使用的所有对象的别名。 这会使该代码清洗器,不需要访问对象通过命名空间:

 

Window = System::Windows::Window
Application = System::Windows::Application
Button = System::Windows::Controls::Button
接下来,创建 WPF 窗口,并为其指定标题:

 

win = Window.new
win.title = 'IronRuby and WPF Interop'
创建按钮以相同的方式:

 

mainButton = Button.new
mainButton.content = 'I'm a WPF button — press me'
在这两种情况我使用别名来访问该对象的完整名称。 单击该按钮是,我想向用户显示一个 MessageBox。 像在 C#,您可以订阅事件和提供一个调用 Click 事件时调用的块:

 

mainButton.click do |sender, args|
  System::Windows::MessageBox.Show("Created using IronRuby!")
end
最后,设置将要按钮、 创建新的应用程序对象和启动窗口窗口的内容:

 

win.content = mainButton
my_app = Application.new
my_app.run win
图 1 使用 IronRuby 创建 WPF 应用程序
到目前为止将完全交互式的 WPF 窗口显示与正确有线设置,如图 1 所示的单击事件。 我使用.NET Framework 交互我将交互拼音库方式相同。 通过 John Lam,团队设置出在核心原则之一是保持 True 为拼写的语言。 这是采用由当前拼写开发,那些不希望更改他们仅仅因为要转向 IronRuby 创建拼写的应用程序的方式的一个重要原则。 而是它们可以访问所有丰富的.NET 代码相同的方式。


 

拼音、 和 CLR
下面是另一个示例。 如果试图访问 IEnumerable 中的 AddRange 在 C# 代码如下: 所

 

ArrayList list = new ArrayList();
list.AddRange(new [] { 1,2,3,4 });
不过,使用 Ruby,可接受的约定,在方法名称来将分隔以提高可读性的下划线的词。 创建要遵循此约定单独的库对其是,太耗时以及易于出错,和它不支持其他第三方开发。
相反的 CLR 对象,IronRuby 将转换到 CLR 的等效方法名称的拼音方法调用:

 

   $list = ArrayList.new
   $list.add_range([1,2,3,4])
此处我正在访问以下是包含由一个下划线字符分隔的词语的小写的 Ruby 的方法 AddRange 方法。 如果您更喜欢您可以保留与 CLR 的命名约定因为方法名称仍然存在从拼音、:

 

$list.AddRange([1,2,3,4])
同时工作相同 ; 只是个人首选使用。
如我提到过拼音、 运行时推断出的对象的该类型,并且这是相同是处理 C# 对象时。 考虑一系列的 C# 方法返回不同的对象类型,同一对象将被返回时, 方法签名返回界面或具体的类型。 我的示例中,我有定义一个 HelloWorld 方法的接口:

 

public interface IHello {
  string HelloWorld();    
}
我创建了一个 Hello4Times 类 Hello3Times,从接口继承,但实现了三种额外的 Hello­World 方法所继承的。 类中我将定义一个称为 HelloMethodOn4Times 调用基实现的新方法:

 

public class Hello4Times : Hello3Times {
  public string HelloMethodOn4Times () {
    return base.HelloWorld();
  }
}
我然后定义一个静态类和方法,将返回 Hello4Times 类的新实例,但其返回到调用代码作为接口。 这意味着调用的代码应只知道 HelloWorld,不将任何其他方法定义:

 

public static class HelloWorld {
  public static IHello ReturnHello4TimesAsInterface() {
    return new Hello4Times();
  }

在我的拼音代码中,我有两个方法调用。 第一个调用将是定义接口的方法您会希望工作时不出现问题。 但是,第二次调用是具体类该方法。 IronRuby 已确定的返回的对象类型,并可以分派方法调用:

 

puts InteropSample::HelloWorld.ReturnHello4TimesAsInterface.HelloWorld
puts interopSample::HelloWorld.ReturnHello4TimesAsInterface.HelloMethodOn4Times
所有这些发生而过不必担心如何将对象转换为正确的类型调用方法。
按照此主题,您可以像可以使用拼音对象扩展.NET 对象,以完全相同的方式。 显示一个 MessageBox 时, 我不想保留定义的图标和按钮来使用。 相反,我只想提供消息。
也许您不要在喜欢内置功能,并希望扩展实际的 MessageBox 类,用于进行无缝交互。 与拼音、,这是简单。 您定义新的类,与 WPF 中内置的 MessageBox 相同的。 然后只需调用 Show 方法使用不同的默认值类上创建的新方法:

 

class System::Windows::MessageBox
  def self.ShowMessage(msg)
    System::Windows::MessageBox.Show(msg, msg, \
      System::Windows::MessageBoxButton.OK, \
      System::Windows::MessageBoxImage.Stop)
  end
end
在执行这段代码后

 

System::Windows::MessageBox.ShowMessage( \
  "I'm going to show you a message") 
您可以调用 ShowMessage 方法,在消息框中显示结果 图 2 .
图 2 显示从 Ruby 的自定义 MessageBox


 

内部 IronRuby
互操作可能是什么? 答案是 DLR。 在较高的级别当您执行使用 IronRuby 的拼音代码大量工作将在后台。 首先,您编写的代码是标记,并分析由 IronRuby 引擎。 分析的代码然后转换为 DLR 的抽象语法树 (AST)。 这是一个标准化的 AST,如 IronPython 的所有语言实现都需要向 DLR 以执行代码的。
一旦 DLR 拥有将 AST,它将中间语言 (IL) 转换为树。 所有的.NET 代码下编译到 CLR 中提供一个泛型的语言的 IL。 这是 IronRuby 可以如何使用.NET Framework 互操作,作为 IL 说明在后台执行所有代码。 DLR 完成转换后,它传递给执行 CLR 的 IL,并且结果返回到 IronRuby。
在此过程有是以提高性能和如缓存的可靠性的其他步骤。 一个更深入的解释,我建议阅读 Bill Chiles CLR 列" Iron­Python 和动态语言运行库 "2007 年 10 月发布的 MSDN 杂志 》.
单独的程序集内有是一个托管 API,可以对自己的应用程序中嵌入 IronRuby 使您可以从 C# 或用户来执行其自己的代码执行拼写的代码。
更多有关 IronRuby 实现下载从整个源代码 RubyForge. IronRuby 在 C# 中实现的开源项目并且为一个动态语言实现的极好示例。 IronPython 是可用,因为在 CodePlex,和其托管一个开源项目包含调用 ToyScript 要介绍到 DLR 的工作原理的示例语言。
若要确保与核心拼音平台的连续的兼容,IronRuby 团队使用 RubySpecs。 这是示例基于拼写的语言应如何实现一个共享的组。 RubySpecs 的目的是确保包括 Matz 的拼音解释器 (MRI) 的不同实现,JRuby、 MacRuby,和 IronRuby 具有相同的行为。 RubySpecs 使用语法兼容版本的名为 MSpec RSpec。 这些被视为验收测试 IronRuby 实现的。


 

测试与 IronRuby 一个 C# 应用程序
现在很多年,意识和 TDD 的已增加作为一种开发方面的设计和可维护性一起途中减少缺陷的高质量代码所导致的软件。 与 IronRuby,我可以使用 RSpec 规范框架和 Runner 提供如何我的 C# 对象工作,以下 BDD 方法,而不是 TDD 的示例。
验收测试在应用程序级别的多对齐方案我将在以后的文章中介绍的 Framework 时, 的指定有关代码编写自己的规范的开发人员更对齐 Framework 它们是几乎实现与预期的行为。 指定框架基于提供描述在对象级别行为的示例。 这些示例可以运行验证开发人员的要求同时提供有关如何对象预期的行为的文档仍然工作系统的实现。
这是与如 Nunit 进行测试和 MbUnit 单元测试框架的重要区别。 这两个使用测试属性以指示一种方法是系统的测试。 RSpec 采用其他方法。 RSpec 显示每个方法为如何代码是要使用的一个示例。 细微区别时它将更改方式编写包括您使用,您组织在的方法和如何轻松地概念可理解为 TDD 的比较方式的术语的这些示例。 使用 BDD 和 RSpec,IronRuby 关闭集成和在.NET Framework 可以启动使用 IronRuby 测试 C# 应用程序。 示例,方面的经典的 RSpec 示例是球游戏。
首先,您需要访问球游戏的实现是 C# 程序集:

 

require File.dirname(__FILE__) + \
  '/InteropSamples/Bowling/Bowling/bin/Debug/bowling.dll'
然后需要访问 RSpec:

 

require 'rubygems'
require 'spec'
现在可以开始编写的示例。 这是示例显示如何 bowling 实现应该工作。 RSpec 具有域特定语言 (DSL) 则必须按照示例要执行的。 第一部分在 DSL 的是在介绍块。 此处只是声明您要在"描述"结合可选说明该对象。 在这种情况下我将定义球对象将实现 C# 中:

 

describe Bowling, " defines the bowling game" do
若要提高可读性,将在执行任何安装程序在 before 块:

 

  before(:each) do  
    @bowling = Bowling.new 
  End
现在,我是在位置与对象交互、 创建示例和验证它正常工作正常。 我使用"它"块,提供说明示例而它演示的上下文字符串。 然后,我编写与系统交互和验证发生了正确的操作的代码的部分:

 

  it "should score 0 for a gutter game" do
    20.times { @bowling.hit(0) }
    @bowling.score.should == 0
  end
end
C# 实现只是如下所示:

 

public class Bowling {
  public int Score { get; set; }
  public void Hit(int pins)
    { Score += pins; }
}
最后,执行此验证球将按预期方式与 RSpec 测试 C# 对象有效:

 

>ir bowling_spec.rb
.
Finished in 1.0458315 seconds
1 example, 0 failures
如果我想更详细的报表,我可以选择为 specdoc 格式。 此输出一起使用所有示例,说明是否它们已传递或不在对象说明:

 

>ir bowling_spec.rb --format specdoc
Bowling defines the bowling game
- should score 0 for a gutter game
Finished in 1.43728 seconds
1 example, 0 failures
在编写时,IronRuby 团队不定期传送二进制文件。 团队有说它正在等待直到满意实施、 兼容性和释放正式的二进制文件前的性能。 但是,因为源代码可自由地用的您可以下载代码并构建您自己。
要下载源代码,需要如有 git 源控件客户 msysgit 安装。 IronRuby 源代码管理 联机才可用。 如果您希望更多信息,则建议您访问在 IronRuby 项目网站 或我的博客张贴内容" 从 GitHub 下载 IronRuby ." 源代码您下载后您可以编译该程序集与使用 IronRuby.sln 解决方案文件的 Visual Studio 或者,如果 MRI 安装然后您可以使用命令:

 

rake compile
后 IronRuby 编译必须下载 RSpec,获得额外的功能 (如) 之类的各种库。 拼音、 有一个概念 RubyGems 在宝石的包的您可以下载获得功能和附加依赖项。 要下载 RSpec,请在命令提示符处键入以下:

 

gem install rspec
您将现在能够访问从 RSpec 库,在拼音代码中。 但是,在编写时,RSpec NTE IronRuby 由于到一个或两个错误。 希望时此文章将发布 RSpec 应要与处理 IronRuby 而。 要了解 RSpec 支持的状态,请参阅在 RSpec Web 站点 IronRuby 邮寄列表.
如果 Bug 未被修复,我做一个 与一起与 RSpec 库就地修复的 Bug 的二进制文件. (注意: 团队已修复该问题后,不使用此版本)
一旦您有正确的二进制文件,ir.exe 将是 IronRuby 解释器,可以执行您的代码。 如果只是启动应用程序从命令行则将输入交互控制台。 在拼音、,学习和调试,但也运行短信指令来解决每天的问题的极好在行的行基础上执行代码。 如果提供文件名作为参数,然后将该文件执行与结果输出到控制台。


 

向前移动
在我的下一文章中,我将引入验收测试以及它如何提高客户和开发人员之间的通信的概念。 我将演示如何验收测试才能自动使用 IronRuby 和 RSpec 验证.NET 应用程序,并创建为系统的可执行文件规范。


 

Ben Hall是 C# 开发 / 测试人员与 for Software Development 的强热情,和喜欢编写代码。 本工作在英国的红色 Gate Software 为测试工程师和受浏览包括手动和自动测试侧重于测试的应用程序的不同类型的最佳方式的测试软件的不同方式。 Ben 是 C# MVP,并维护在博客 Blog.BenHall.Me.uk.

 

原文出自MSDN杂志

http://msdn.microsoft.com/zh-cn/magazine/dd434651.aspx

你可能感兴趣的:(Getting Started With IronRuby And RSpec)