Best ruby questions in September 2010

Why are scripting languages (e.g. Perl, Python, Ruby) not suitable as shell languages?

82 votes

What are the differences between shell languages like bash, zsh, fish and the scripting languages above that makes them more suitable for the shell?

When using the command line the shell languages seem to be much easier. It feels for me much smoother to use bash for example than to use the shell profile in ipython, despite reports to the contrary. I think most wil agree with me that a large portion of medium to large scale programming is easier in Python than in bash. I use Python as the language I am most familiar with, the same goes for Perl and Ruby.

I have tried to articulate the reason but am unable to, aside from assuming that the treatment of strings differently in both has something to do with it.

The reason of this question is that I am hoping to develop a language usable in both. If you know of such a language, please post it as well.

Edit: As S.Lott explains, the question needs some clarification. I am asking about the features of the shell language versus that of scripting languages. So the comparison is not about the characteristics of various interactive (REPL) environments such as history and command line substitution. An alternative expression for the question would be:

Can a programming language that is suitable for design of complex systems be at the same time able to express useful one-liners that can access the file system or control jobs? Can a programming language usefully scale up as well as scale down?

There are a couple of differences that I can think of. (Just thoughtstreaming here, there's no particular order to those.)

  1. Python & Co. are designed to be good at scripting. Bash & Co. are designed to be only good at scripting, with absolutely no compromise. IOW: Python is designed to be good both at scripting and non-scripting, Bash cares only about scripting.
  2. Bash & Co. are untyped, Python & Co. are strongly typed, which means that the number 123, the string 123 and the file 123 are quite different. They are, however, not statically typed, which means they need to have different literals for those, in order to keep them apart. Example:

    • Ruby: 123 (number), Bash: 123
    • Ruby: '123' (string), Bash: 123
    • Ruby: /123/ (regexp), Bash: 123
    • Ruby: File.open('123') (file), Bash: 123
    • Ruby: IO.open('123') (file descriptor), Bash: 123
    • Ruby: URI.parse('123') (URI), Bash: 123
    • Ruby: `123` (command), Bash: 123
  3. Python & Co. are designed to scale up to 10000, 100000, maybe even 1000000 line programs, Bash & Co. are designed to scale down to 10 character programs.

  4. In Bash & Co., files, directories, file descriptors, processes are all first-class objects, in Python, only Python objects are first-class, if you want to manipulate files, directories etc., you have to wrap them in a Python object first.
  5. Shell programming is basically dataflow programming. Nobody realizes that, not even the people who write shells, but it turns out that shells are quite good at that, and general-purpose languages not so much. In the general-purpose programming world, dataflow seems to be mostly viewed as a concurrency model, not so much as a programming paradigm.

I have the feeling that trying to address these points by bolting features or DSLs onto a general-purpose programming language doesn't work. At least, I have yet to see a convincing implementation of it. There is RuSH (Ruby shell), which tries to implement a shell in Ruby, there is rush, which is an internal DSL for shell programming in Ruby, there is Hotwire, which is a Python shell, but IMO none of those come even close to competing with Bash, Zsh, fish and friends.

Actually, IMHO, the best current shell is Microsoft PowerShell, which is very surprising considering that for several decades now, Microsoft has continually had the worst shells evar. I mean, COMMAND.COM? Really? (Unfortunately, they still have a crappy terminal. It's still the "command prompt" that has been around since, what? Windows 3.0?)

PowerShell was basically created by ignoring everything Microsoft has ever done (COMMAND.COM, CMD.EXE, VBScript, JScript) and instead starting from the Unix shell, then removing all backwards-compatibility cruft (like backticks for command substitution) and massaging it a bit to make it more Windows-friendly (like using the now unused backtick as an escape character instead of the backslash which is the path component separator character in Windows). After that, is when the magic happens.

They address problem 1 and 3 from above, by basically making the opposite choice compared to Python. Python cares about large programs first, scripting second. Bash cares only about scripting. PowerShell cares about scripting first, large programs second. A defining moment for me was watching a video of an interview with Jeffrey Snover (PowerShell's lead designer), when the interviewer asked him how big of a program one could write with PowerShell and Snover answered without missing a beat: "80 characters." At that moment I realized that this is finally a guy at Microsoft who "gets" shell programming (probably related to the fact that PowerShell was neither developed by Microsoft's programming language group (i.e. lambda-calculus math nerds) nor the OS group (kernel nerds) but rather the server group (i.e. sysadmins who actually use shells)), and that I should probably take a serious look at PowerShell.

Number 2 is solved by having arguments be statically typed. So, you can write just 123 and PowerShell knows whether it is a string or a number or a file, because the cmdlet (which is what shell commands are called in PowerShell) declares the types of its arguments to the shell. This has pretty deep ramifications: unlike Unix, where each command is responsible for parsing its own arguments (the shell basically passes the arguments as an array of strings), argument parsing in PowerShell is done by the shell. The cmdlets specify all their options and flags and arguments, as well as their types and names and documentation(!) to the shell, which then can perform argument parsing, tab completion, IntelliSense, inline documentation popups etc. in one centralized place. (This is not revolutionary, and the PowerShell designers acknowledge shells like the DIGITAL Command Language (DCL) and the IBM OS/400 Command Language (CL) as prior art. For anyone who has ever used an AS/400, this should sound familiar. In OS/400, you can write a shell command and if you don't know the syntax of certain arguments, you can simply leave them out and hit F4, which will bring a menu (similar to an HTML form) with labelled fields, dropdown, help texts etc. This is only possible because the OS knows about all the possible arguments and their types.) In the Unix shell, this information is often duplicated three times: in the argument parsing code in the command itself, in the bash-completion script for tab-completion and in the manpage.

Number 4 is solved by the fact that PowerShell operates on strongly typed objects, which includes stuff like files, processes, folders and so on.

Number 5 is particularly interesting, because PowerShell is the only shell I know of, where the people who wrote it were actually aware of the fact that shells are essentially dataflow engines and deliberately implemented it as a dataflow engine.

Another nice thing about PowerShell are the naming conventions: all cmdlets are named Action-Object and moreover, there are also standardized names for specific actions and specific objects. (Again, this should sound familar to OS/400 users.) For example, everything which is related to receiving some information is called Get-Foo. And everything operating on (sub-)objects is called Bar-ChildItem. So, the equivalent to ls is Get-ChildItem (although PowerShell also provides builtin aliases ls and dir – in fact, whenever it makes sense, they provide both Unix and CMD.EXE aliases as well as abbreviations (gci in this case)).

But the killer feature IMO is the strongly typed object pipelines. While PowerShell is derived from the Unix shell, there is one very important distinction: in Unix, all communication (both via pipes and redirections as well as via command arguments) is done with untyped, unstructured, ASCII strings. In PowerShell, it's all strongly typed, structured objects. This is so incredibly powerful that I seriously wonder why noone else has thought of it. (Well, they have, but they never became popular.) In my shell scripts, I estimate that up to one third of the commands is only there to act as an adapter between two other commands that don't agree on a common textual format. Many of those adapters go away in PowerShell, because the cmdlets exchange structured objects instead of unstructured text. And if you look inside the commands, then they pretty much consist of three stages: parse the textual input into an internal object representation, manipulate the objects, convert them back into text. Again, the first and third stage basically go away, because the data already comes in as objects.

However, the designers have taken great care to preserve the dynamicity and flexibility of shell scripting through what they call an Adaptive Type System.

Anyway, I don't want to turn this into a PowerShell commercial. There are plenty of things that are not so great about PowerShell, although most of those have to do either with Windows or with the specific implementation, and not so much with the concepts. (E.g. the fact that it is implemented in .NET means that the very first time you start up the shell can take up to several seconds if the .NET framework is not already in the filesystem cache due to some other application that needs it. Considering that you often use the shell for well under a second, that is completely unacceptable.)

The most important point I want to make is that if you want to look at existing work in scripting languages and shells, you shouldn't stop at Unix and the Ruby/Python/Perl/PHP family. For example, Tcl was already mentioned. Rexx would be another scripting language. Emacs Lisp would be yet another. And in the shell realm there are some of the already mentioned mainframe/midrange shells such as the OS/400 command line and DCL. Also, Plan9's rc.

Why don't they implement python and ruby on the web browsers?

15 votes

I wonder, why don't they implement other languages like python and ruby on the web browsers?

Don't they fit as client programming languages or did it just happen to be that Javascript was the first one to be implemented, and they then kept on only supporting javascript because it just worked.

I mean...I really hate Javascript compared to Ruby, no matter how hard I try to like it, as soon as I see Ruby code I want to cry for Javascript for being so ugly.

Will there be no chance at all for Ruby and Python on the browser side (without having to use Silverlight) for the next 10 years?

At the time JavaScript was designed, Python would have been at a very immature stage (1.2-ish) and Ruby wouldn't have existed at all. What we consider a modern scripting language now didn't exist then. Python didn't gain Unicode support (vital for a web browser) until version 1.6, several years later; Ruby... well, yeah.

The dominant scripting language then was Perl. Let us be thankful Eich didn't copy that.

Technically, a language for execution on the client side needs strong sandboxing capabilities that CPython and Ruby don't have. Whilst Python can be integrated into IE via the Windows Scripting Host, doing so completely hoses your security. It is not trivial to create a sandboxed version of a language that wasn't designed for it.

Will there be no chance at all for Ruby and Python on the browser side

No, none whatsoever, even in a restricted form that solved the security problems. Even Microsoft couldn't make VBScript for the web catch on. JavaScript is the language that works everywhere; you aren't going to be able to beat that inertia.

At this point we must concentrate on improving the language. The standardisation of ECMAScript Fifth Edition is a big step forward, offering new methods that really help with writing terse code that passes around functions like Ruby blocks. And Mozilla's JavaScript implementation offers some interesting new features like Python-style generators. (On the other hand, it also supports E4X, a vile pox on the language, so whatever.)

JS is not so bad, written tastefully. Of course, the majority of code out there, and in tutorials, is anything but tasteful. But that's hardly a problem limited to JS.

is ruby on rails (or at least the community) dying?

12 votes

This is an honest question and I am not trolling.

As a newbie to rails I've been search for good rails resources. But I've been noticing many sites that apparently were once popular now being completely abandoned. Some examples:

Am I just coincidentally going to all the wrong websites/blogs (even though they're the top hits on google) or is the rails community slowly dying off? If I just happen to be going to the wrong sites can someone please point me to some currently updated sites?

Ruby on Rails was a Hype. That means a lot of people jumped on the bandwagon because that is what they do: jumping on bandwagons (for a living).

After that hype, many communities popped up, in various languages that mimique Rails. Or try to. Or just took the good ideas and applied them to their community. Now you have gazillion halfbaked PHP-frameworks, and a few actually good ones. You have Django (python), Zend, Symphony (PHP) and even in Ruby, some alternative frameworks. That has spread the attention. There used to be only One Good Framework (sic.) now there are many.

That said, Rails 3 has just been released. Rails 3 is cutting-edge again. It has all the ingredients for noSQL (the one-but-latest Hype) HTML5 (the latest Hype) and many javascript-frameworks and interactions (the next-to-be Hype).

That said, Rails is not just Hypes. It is actually a fantastic framework. With a still very active community around it. Just look at github, and visit the trending repo's there once in a while and you will see a Great Rails Thing there every week.

If you want to keep up to date, I would advice:

Why doesn't Ruby support i++ or i-- for fixnum?

11 votes

The pre/post increment/decrement operator (++ and --) are pretty standard programing language syntax (for procedural and object oriented languages, at least).

Why doesn't Ruby support them? I understand you could accomplish the same thing with += and -=, but it just seems oddly arbitrary to exclude something like that, especially since it's so concise and conventional.

example:

i = 0    #=> 0
i += 1   #=> 1
i        #=> 1
i++      #=> expect 2, but as far as I can tell, 
         #=> irb ignores the second + and waits for a second number to add to i

I understand FixNum is immutable, but if += can just instanciate a new FixNum and set it, why not do the same for ++?

Edit

Is consistency in assignments containing the = character the only reason for this, or am I missing something?

Here is how Matz explains it in an old thread:

Hi,

In message "[ruby-talk:02706] X++?"
    on 00/05/10, Aleksi Niemelä <aleksi.niemela@cinnober.com> writes:

|I got an idea from http://www.pragprog.com:8080/rubyfaq/rubyfaq-5.html#ss5.3
|and thought to try. I didn't manage to make "auto(in|de)crement" working so
|could somebody help here? Does this contain some errors or is the idea
|wrong?

  (1) ++ and -- are NOT reserved operator in Ruby.

  (2) C's increment/decrement operators are in fact hidden assignment.
      They affect variables, not objects.  You cannot accomplish
      assignment via method.  Ruby uses +=/-= operator instead.

  (3) self cannot be a target of assignment.  In addition, altering
      the value of integer 1 might cause severe confusion throughout
      the program.

                            matz.

What components make VIM a good (great) ruby editor?

9 votes

I'm learning ruby on rails on a linux box and dusting off my VIM skills (skillz?).

alt text

When I got started on VIM way back in my c++ days, I had a friend with a great vimfiles folder that had tons of stuff to get started. Starting from scratch, vim is great, but it feels like it could be a lot better.

I currently have:

I know that barely scratches the surface of what some more experienced vim/ruby devs have (including the one offs in the vim.rc file).

Is there a list somewhere (or could we create one) of a bunch of the standard vim configurations needed to make programming ruby (and rails) more fun? Is there a zip/tarball somewhere with a good base setup?

take a look at tim pope's repos on git hub. Many, many awesome vim plugins and extensions for working with ruby and rails

http://github.com/tpope

What prevents a statically typed language from having something like Ruby's method_missing?

9 votes

I don't have much experience with statically typed languages (currently learning Scala and loving it!) but one thing I've noticed is that they don't ever seem to have anything like Ruby's method_missing or ColdFusion's onMissingMethod. Is there some inherent limitation in statically typed languages that prevent or make this difficult?

Certainly a mechanism for handling them could be added, but it is at odds with what static typing is: Compile-time determination that your program is free of type errors.

Need Advice: Is this a good use case for a 'NoSQL' Database? If so, which one?

9 votes

I have recently been researching NoSql options. My scenario is as follows:

We collect and store data from custom hardware at remote locations around the world. We record data from every site every 15 minutes. We would eventually like to move to every 1 minute. Each record has between 20 and 200 measurements. Once set up the hardware records and reports the same measurements every time.

The biggest issue we are facing is that we get a different set of measurements from every project. We measure about 50-100 different measurement types, however any project can have any number of each type of measurement. There is no preset set of columns that can accommodate the data. Because of this we create and build each projects data table with the exact columns it needs as we set up and configure the project on the system.

We provide tools to help analyze the data. This typically includes more calculations and data aggregation, some of which we also store.

We are currently using a mysql database with a table for each client. There are no relations between tables.

NoSql seems promising because we could store a project_id, timestamp then the rest would not be preset. This means one table, more relationships in the data, yet still handling the variety of measurements.

Is a 'NoSql' solution right for this job? If so which ones?

I have been investigation MongoDB and it seems promising...

Example for Clarification:

Project 1 has 5 data points recorded, the mysql table columns look like: timestamp, temp, wind speed, precipitation, irradiance, wind direction

Project 2 has 3 data points recorded mysql table columns: timestamp, temp, irradiance, temp2

The simple answer is that there is no simple answer to these sort of problems, the only way to find out what works for your scenario is to invest R&D time into it.

The question is hard to answer because the performance requirements aren't spelled out by the OP. It appears to be 75M/year records over a number of customers with a write rate of num_customers*1minute (which is low), but I don't have figures for the required read / query performance.

Effectively you have already a sharded database using horizontal partitioning because you're storing each customer in a seperate table. This is good and will increase performance. However you haven't yet established that you have a performance problem, so this needs to be measured and the problem size assessed before you can fix it.

A NoSQL database is indeed a good way of fixing performance problems with traditional RDBMS, but it will not provide automatic scalabity and is not a general solution. You need to find your performance problem fix and then design the (nosqL) data model to provide the solution.

Depending on what you're trying to achieve I'd look at MongoDB, Apache Cassandra, Apache HBase or Hibari.

Remember that NoSQL is a vague term typically encompassing

  • Applications that are either performance intensive in read or write. Often sacrificing read or write performance at the expense of the other.
  • Distribution and scalability
  • Different methods of persistency (RAM/Disk)
  • A more structured/defined access pattern making ad-hoc queries harder.

So, in the first instance I'd see if a traditional RDBMS can achieve the required performance, using all available techniques, get a copy of High Performance MySQL and read MySQL Performance Blog.

Rev1:

In light of your comments I think it is fair to say that you could achieve what you want with one of the above NOSQL engines.

My primary recommendation would be to get your data model designed and implemented, what you're using at the moment isn't really right.

So look at Entity-attribute-value model as I think it is exactly right for what you need.

You need to get your data model right before you can consider which technology to use, being honest modifying schemas dynamically isn't a datamodel.

I'd use a traditional SQL database to validate and test the new datamodel as the management tools are better and it's generally easier to work with the schemas as you refine the datamodel.

Install Rails 3 on OSX with RVM

8 votes

Trying to install the new Rails 3 release on OSX 10.6.

Have never touched Ruby or Rails on this machine since purchased.

I was able to get rvm and get Ruby 1.9.2. installed. From there, I am stuck.

I tried:

rvmsudo gem install rails -v 3.0.0
sudo gem install rails --pre
sudo gem install rails
sudo gem update rails

And I get the same result error each time:

ERROR:  While executing gem ... (Errno::ENOENT)
    No such file or directory - /Users/kevin/.rvm/gems/ruby-1.9.2-head@rails3/cache/activesupport-3.0.0.gem

If I do gem list, it says LOCAL GEMS and doesn't list anything.

I have read a few walkthroughs but honestly none of them address this issue and its kind of pissing me off. Why is this so difficult to install? Would love to learn it if someone could help me get it running.

I was trying to follow this:

http://eddorre.com/posts/installing-rails-3-beta-4-using-rvm

and this:

http://hivelogic.com/articles/compiling-ruby-rubygems-and-rails-on-snow-leopard

Which is actually linked from the ROR guides website. Am I missing dependencies? How do I get them in?

If I do rails -v I get:

rails -v
/Library/Ruby/Site/1.8/rubygems.rb:779:in `report_activate_error': Could not find RubyGem rails (>= 0) (Gem::LoadError)
    from /Library/Ruby/Site/1.8/rubygems.rb:214:in `activate'
    from /Library/Ruby/Site/1.8/rubygems.rb:1082:in `gem'
    from /usr/bin/rails:18

Older versions of rvm had a bug that can cause your ruby versions to get crosswired because the OS can cache executable paths for the which command (particularly if you are using zsh). See this long, detailed, mind blowing post by Yehuda Katz on the subject.

What I had to do this morning:

rvm update && rvm reload # update rvm
rvm gemset delete rails3 # delete old gemset
rvm install 1.9.2
rvm use 1.9.2
rvm gemset create rails3
rvm use 1.9.2@rails3
which ruby          # check to be sure the ruby interpretter is properly set to 1.9.2
hash -r             # if ruby interpretter is not pointing to 1.9.2
gem install rails
which rails         # check to be sure we are using rvm version of rails

How to move from .NET-style TDD to Ruby?

8 votes

I've been struggling to adapt my standard approach for test-driving .NET code to Ruby.

As an example, I am writing a class that will:

grab all *.markdown files from a directory
  foreach file:
    extract code samples from file
    save code to file.cs in output directory

Normally for .NET I'd end up with something like:

class ExamplesToCode {
  public ExamplesToCode(IFileFinder finder, IExampleToCodeConverter converter) { ... }
  public void Convert(string exampleDir, string targetDir) { ... }
}

In my test (written first), I'd mock finder and converter. Then I'd stub out finder.FindFiles("*.markdown") to return say ["file1", "file2"], and check converter.Convert("file1", targetDir) and converter.Convert("file2", targetDir) was called.

Where I struggle applying this to Ruby is that Ruby tends to use blocks and internal iterators (e.g. array.each { |x| puts x }), and including modules over constructor injection. I'm not sure on how to unit test code in those cases (without setting up a full integration test), and the .NET approach just seems incredibly un-rubyish; it seems to fight the way Ruby naturally works.

Any suggestions on how to do this the Ruby way? An example of a Ruby test for this example would be great.

You could have a very course test that goes something like this:

class ExamplesToCodeTest < Test::Unit::TestCase
  def test_convert
    # have some example markdown files in a fixtures directory
    ExamplesToCode.convert("test/fixtures/*.markdown")
    assert_equal expected_output_1, File.read("test/output/file_1.cs")
    assert_equal expected_output_2, File.read("test/output/file_2.cs")
    assert_equal expected_output_3, File.read("test/output/file_3.cs")
  end
  private
    def expected_output_1
      "... expected stuff here ..."
    end
    def expected_output_2
      "... expected stuff here ..."
    end
    def expected_output_3
      "... expected stuff here ..."
    end
end

I suppose that would make a decent integration test, but that's not what I really like, I like to have my code in bite-size chunks

First I'd create a class that can handle parsing a markdown file, e.g.:

class MarkdownReaderTest < Test::Unit::TestCase
  def test_read_code_sample_1
    reader = MarkdownReader.new
    code_sample = reader.read("fixtures/code_sample_1.markdown")
    # or maybe something like this:
    # code_sample = reader.parse(File.read("fixtures/code_sample_1.markdown"))
    # if you want the reader to just be a parser...
    assert_equal code_sample_1, code_sample
  end
  # ... repeat for other types of code samples ...
  private
    def code_sample_1
      "text of code sample 1 here..."
    end
end

Now all the code to read and parse markdown files is in the MarkdownReader class. Now if we don't want to have to actually write files you can get fancy and do some mocking with RR or Mocha or something (I'm using rr here):

class CodeSampleWriter < Test::Unit::TestCase
  include RR::Adapters::TestUnit
  def test_write_code_sample
    # assuming CodeSampleWriter class is using the File.write()...
    any_instance_of(File) do |f|
      mock(f).write(code_sample_text) { true }
    end
    writer = CodeSampleWriter.new
    writer.write(code_sample_text)
  end
  private
    def code_sample_text
      "... code sample text here ..."
    end
end

Now assuming the ExamplesToCode class uses the MarkdownReader and CodeSampleWriter classes, you can again use mock objects with RR like so:

class ExamplesToCodeTest < Test::Unit::TestCase
  include RR::Adapters::TestUnit
  def test_convert
    # mock the dir, so we don't have to have an actual dir with files...
    mock(Dir).glob("*.markdown") { markdown_file_paths }
    # mock the reader, so we don't actually read files...
    any_instance_of(MarkdownReader) do |reader|
      mock(reader).read("file1.markdown") { code_sample_1 }
      mock(reader).read("file2.markdown") { code_sample_1 }
      mock(reader).read("file3.markdown") { code_sample_1 }
    end
    # mock the writer, so we don't actually write files...
    any_instance_of(CodeSampleWriter) do |writer|
      mock(writer).write_code_sample(code_sample_1) { true }
      mock(writer).write_code_sample(code_sample_2) { true }
      mock(writer).write_code_sample(code_sample_3) { true }
    end
    # now that the mocks are mocked, it's go time!
    ExamplesToCode.new.convert("*.markdown")
  end
  private
    def markdown_file_paths
      ["file1.markdown", "file2.markdown", "file3.markdown"]
    end
    def code_sample_1; "... contents of file 1 ..."; end
    def code_sample_2; "... contents of file 2 ..."; end
    def code_sample_3; "... contents of file 3 ..."; end
end

Hopefully this gives you some ideas of how to approach testing in Ruby. Not be inflammatory, but for the most part, dependency injection is not something seen or used in the Ruby world -- it generally adds a lot of overhead. Mocking/Doubles are generally a much better option for testing.

Why is this recursion NOT infinite?

8 votes

My friends and I are working on some basic Ruby exercises to get a feel for the language, and we've run into an interesting behavior that we're yet unable to understand. Basically, we're creating a tree data type where there's just one class, node, which contains exactly one value and an array of zero or more nodes. We're using rspec's autospec test runner. At one point we started writing tests to disallow infinite recursion (a circular tree structure).

Here's our test:

it "breaks on a circular reference, which we will fix later" do
  tree1 = Node.new 1
  tree2 = Node.new 1
  tree2.add_child tree1
  tree1.add_child tree2
  (tree1 == tree2).should be_false
end

Here's the Node class:

class Node
  attr_accessor :value
  attr_reader :nodes

  def initialize initial_value = nil
    @value = initial_value
    @nodes = []
  end

  def add_child child
    @nodes.push child
    @nodes.sort! { |node1, node2| node1.value <=> node2.value }
  end

  def == node
    return (@value == node.value) && (@nodes == node.nodes)
  end
end

We expect the last line of the test to result in an infinite recursion until the stack overflows, because it should continually compare the child nodes with each other and never find a leaf node. (We're under the impression that the == operator on an array will iterate over the array and call == on each child, based on the array page of RubyDoc.) But if we throw a puts into the == method to see how often it's called, we discover that it's called exactly three times and then the test passes.

What are we missing?

Edit: Note that if we replace be_false in the test with be_true then the test fails. So it definitely thinks the arrays are not equal, it's just not recursing over them (aside from the three distinct calls to ==).

If you click on the method name of the RubyDoc you linked to, you will see the source (in C) of the Array#== method:

{
    // [...]
    if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
    if (rb_inspecting_p(ary1)) return Qfalse;
    return rb_protect_inspect(recursive_equal, ary1, ary2);
}

This implementation (specifically the "recursive_equal") suggests that Array#== already implements the infinite recursion protection you're after.

Boson vs Thor for console applications

8 votes

Has anyone used both:

Boson: http://tagaholic.me/2009/10/14/boson-command-your-ruby-universe.html

and

Thor: http://github.com/wycats/thor

Thor are very popular and have more followers and contributers than Boson, but Boson looks far more powerful than Thor and the architecture is very well thought out.

In Boson you:

  • can add methods that are used both in the console and ruby environment. So you don't have to both have Thorfiles for console and gems for ruby.
  • can have aliases.
  • don't have to install your script files, you just put them in ~/.boson/commands. I always have to struggle with uninstalling and installing Thorfiles after each update (which could be every minute when editing the source code, very frustrating).
  • have much nicer commands output than thor.
  • don't have to write the argument descriptions by hand like in Thor.
  • work with modules, which are better than with classes cause you can include modules inside other modules.
  • wrap open source snippets (eg. from Gist) inside a module automatically and it works with Boson immediately.
  • have different views for your method results.
  • don't have to recode anything in your snippets to fit Thor, since it only use native ruby code (modules). That means if you one day don't want to use Boson, you don't have to recode everything, which you have to if you are using Thor.
  • The API http://rdoc.info/github/cldwalker/boson is more well documented - like tutorials inside each class.
  • You can just include the "boson" modules inside your ruby script and use them directly, something I cannot with Thor, cause it is only for Thor. You can't share the Thor methods with other Thor classes (not as mixins)

I noticed all these benefits just from reading the documentation and played with Boson for a couple of minutes.

Should I use Thor just because it's more popular (cause I can't find anything else where it shines over boson) or should I take the risk that Boson may be unmaintained after a while, since the author is the only contributor?

Although it's just one guy you see how he has managed to code in a rapid speed and with outstanding quality. Would be great if more contributers like him contributed to that library. I really hope more rubyists are going to use it cause it has a lot of potential for being THE scripting framework for all system automation. Like a Rails for the backend. And the author really helps you out very fast when you file an issue.

Thor only works for the shell (which I guess is its purpose) while boson as I see it has 3 main functionalities. It allows you to have code working in the shell, in ruby (irb and scripts) and you can have nice collections of all your Ruby codes, without modifications.

I have always wanted a framework to be my backend scripting framework, and now I don't have to reinvent the wheel. It seems that boson could be it.

Has someone used both these libraries and could share some thoughts?

Disclaimer: I'm the author of boson.

I've used both and thor was what inspired me to write boson. While the two have overlapping functionality, I see them as having different goals.

Thor is a scripting framework which quickly and beautifully gives applications a commandline interface. The 116 gems (including rails) that depend on it are good evidence of that. Initially I tried using thor to manage and use snippets but after awhile, the forced namespacing, the lack of aliasing, writing redundant usage lines, and poor searching, made me realize thor wasn't optimized to manage snippets.

So I wrote boson to manage the endless number of ruby snippets I used to put in ~/bin with this philosophy in mind. At 400+ commands, I'm able to instantly find and use any ruby snippet as a full-blown executable. There are too many features to go over here, though you seem to know some of boson's strengths. As for being the sole contributor, I welcome anyone to contribute their ideas.

If there was one simple comparison to make between the two, I'd say thor is centered around creating executables for projects and apps while boson is centered around creating them for users.

What is the best way of running shell commands from a web based interface?

7 votes

Imagine a web application that allows a logged in user to run a shell command on the web server at the press of a button. This is relatively simple in most languages via some standard library os tools.

But if that command is long running you don't want your UI to hang. Again this is relatively easy to deal with using some sort of background process or putting the command to be executed onto a message queue (and maybe saving the output and status somewhere for later consumption). Just return quickly saving we'll run that and get back to you.

What I'd like to do is show the output of said web ui triggered shell command as it happens. So vertically scrolling text like when running in a terminal.

I have a vague idea of how I might approach this, streaming the output to a websocket perhaps and simply printing the output to screen.

What I'd like to ask is:

Are their any plugins, libraries or applications that already do this. Something I can either use or read the source of. Ideally an open source python/django or ruby/rails tool, but other stacks would be interesting too.

So, I've tried to answer my own question with code as I couldn't find anything to quite fit the bill. Hopefully it's useful to anyone coming across the same problem.

Redbeard 0X0A pointed me in the general direction, I was able to get a stand along ruby script doing what I wanted using popen. Extending this to using EventMachine (as it provided a convenient way of writing a websocket server) and using it's inbuilt popen method solved my problem.

More details here http://morethanseven.net/2010/09/09/Script-running-web-interface-with-websockets.html and the code at http://github.com/garethr/bolt/

Cannot install thin on windows

7 votes

I cannot install thin gem on windows. Something happen during build of native extensions.

Do anybody face same issues?

Here is the stack trace:

>gem install thin --no-ri --no-rdoc
Temporarily enhancing PATH to include DevKit...
Building native extensions.  This could take a while...
ERROR:  Error installing thin:
        ERROR: Failed to build gem native extension.

C:/Ruby/bin/ruby.exe extconf.rb
checking for rb_trap_immediate in ruby.h,rubysig.h... no
checking for rb_thread_blocking_region()... yes
checking for inotify_init() in sys/inotify.h... no
checking for __NR_inotify_init in sys/syscall.h... no
checking for writev() in sys/uio.h... no
checking for rb_thread_check_ints()... yes
checking for rb_time_new()... yes
checking for windows.h... yes
checking for winsock.h... yes
checking for main() in -lkernel32... yes
checking for main() in -lrpcrt4... yes
checking for main() in -lgdi32... yes
checking for main() in -lssl... no
creating Makefile

make
g++ -I. -IC:/Ruby/include/ruby-1.9.1/i386-mingw32 -I/C/Ruby/include/ruby-1.9.1/ruby/backward -I/C/Ru
by/include/ruby-1.9.1 -I. -DBUILD_FOR_RUBY -DHAVE_RB_THREAD_BLOCKING_REGION -DHAVE_TBR -DHAVE_RB_THR
EAD_CHECK_INTS -DHAVE_RB_TIME_NEW -DOS_WIN32 -DHAVE_WINDOWS_H -DHAVE_WINSOCK_H -DWITHOUT_SSL -DHAVE_
MAKE_PAIR    -O3 -g -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -
Wno-missing-field-initializers -Wno-long-long   -o binder.o -c binder.cpp
In file included from project.h:137:0,
                 from binder.cpp:20:
binder.h:35:34: warning: type qualifiers ignored on function return type
In file included from project.h:138:0,
                 from binder.cpp:20:
em.h:91:47: warning: type qualifiers ignored on function return type
em.h:92:76: warning: type qualifiers ignored on function return type
em.h:93:56: warning: type qualifiers ignored on function return type
em.h:95:57: warning: type qualifiers ignored on function return type
em.h:96:60: warning: type qualifiers ignored on function return type
em.h:97:58: warning: type qualifiers ignored on function return type
em.h:98:55: warning: type qualifiers ignored on function return type
em.h:99:36: warning: type qualifiers ignored on function return type
em.h:101:47: warning: type qualifiers ignored on function return type
em.h:106:42: warning: type qualifiers ignored on function return type
em.h:123:45: warning: type qualifiers ignored on function return type
em.h:132:36: warning: type qualifiers ignored on function return type
In file included from project.h:145:0,
                 from binder.cpp:20:
eventmachine.h:45:61: warning: type qualifiers ignored on function return type
eventmachine.h:46:112: warning: type qualifiers ignored on function return type
eventmachine.h:47:69: warning: type qualifiers ignored on function return type
eventmachine.h:49:73: warning: type qualifiers ignored on function return type
eventmachine.h:62:75: warning: type qualifiers ignored on function return type
eventmachine.h:63:74: warning: type qualifiers ignored on function return type
eventmachine.h:64:77: warning: type qualifiers ignored on function return type
eventmachine.h:65:41: warning: type qualifiers ignored on function return type
In file included from project.h:145:0,
                 from binder.cpp:20:
eventmachine.h:99:60: warning: type qualifiers ignored on function return type
eventmachine.h:100:58: warning: type qualifiers ignored on function return type
eventmachine.h:102:60: warning: type qualifiers ignored on function return type
eventmachine.h:105:41: warning: type qualifiers ignored on function return type
g++ -I. -IC:/Ruby/include/ruby-1.9.1/i386-mingw32 -I/C/Ruby/include/ruby-1.9.1/ruby/backward -I/C/Ru
by/include/ruby-1.9.1 -I. -DBUILD_FOR_RUBY -DHAVE_RB_THREAD_BLOCKING_REGION -DHAVE_TBR -DHAVE_RB_THR
EAD_CHECK_INTS -DHAVE_RB_TIME_NEW -DOS_WIN32 -DHAVE_WINDOWS_H -DHAVE_WINSOCK_H -DWITHOUT_SSL -DHAVE_
MAKE_PAIR    -O3 -g -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -
Wno-missing-field-initializers -Wno-long-long   -o cmain.o -c cmain.cpp
In file included from project.h:137:0,
                 from cmain.cpp:20:
binder.h:35:34: warning: type qualifiers ignored on function return type
In file included from project.h:138:0,
                 from cmain.cpp:20:
em.h:91:47: warning: type qualifiers ignored on function return type
em.h:92:76: warning: type qualifiers ignored on function return type
em.h:93:56: warning: type qualifiers ignored on function return type
em.h:95:57: warning: type qualifiers ignored on function return type
em.h:96:60: warning: type qualifiers ignored on function return type
em.h:97:58: warning: type qualifiers ignored on function return type
em.h:98:55: warning: type qualifiers ignored on function return type
em.h:99:36: warning: type qualifiers ignored on function return type
em.h:101:47: warning: type qualifiers ignored on function return type
em.h:106:42: warning: type qualifiers ignored on function return type
em.h:123:45: warning: type qualifiers ignored on function return type
em.h:132:36: warning: type qualifiers ignored on function return type
In file included from project.h:145:0,
                 from cmain.cpp:20:
eventmachine.h:45:61: warning: type qualifiers ignored on function return type
eventmachine.h:46:112: warning: type qualifiers ignored on function return type
eventmachine.h:47:69: warning: type qualifiers ignored on function return type
eventmachine.h:49:73: warning: type qualifiers ignored on function return type
eventmachine.h:62:75: warning: type qualifiers ignored on function return type
eventmachine.h:63:74: warning: type qualifiers ignored on function return type
eventmachine.h:64:77: warning: type qualifiers ignored on function return type
eventmachine.h:65:41: warning: type qualifiers ignored on function return type
In file included from project.h:145:0,
                 from cmain.cpp:20:
eventmachine.h:99:60: warning: type qualifiers ignored on function return type
eventmachine.h:100:58: warning: type qualifiers ignored on function return type
eventmachine.h:102:60: warning: type qualifiers ignored on function return type
eventmachine.h:105:41: warning: type qualifiers ignored on function return type
cmain.cpp:98:71: warning: type qualifiers ignored on function return type
cmain.cpp:109:122: warning: type qualifiers ignored on function return type
cmain.cpp:119:79: warning: type qualifiers ignored on function return type
cmain.cpp:129:83: warning: type qualifiers ignored on function return type
cmain.cpp:260:85: warning: type qualifiers ignored on function return type
cmain.cpp:270:84: warning: type qualifiers ignored on function return type
cmain.cpp:280:88: warning: type qualifiers ignored on function return type
cmain.cpp:290:51: warning: type qualifiers ignored on function return type
cmain.cpp:300:70: warning: type qualifiers ignored on function return type
cmain.cpp:320:55: warning: type qualifiers ignored on function return type
cmain.cpp:546:70: warning: type qualifiers ignored on function return type
cmain.cpp:669:68: warning: type qualifiers ignored on function return type
cmain.cpp: In function 'int evma_send_file_data_to_connection(long unsigned int, const char*)':
cmain.cpp:752:6: error: cannot convert 'stat*' to '_stati64*' for argument '2' to 'int _fstati64(int
, _stati64*)'
make: *** [cmain.o] Error 1


Gem files will remain installed in C:/Ruby/lib/ruby/gems/1.9.1/gems/eventmachine-0.12.10 for inspect
ion.
Results logged to C:/Ruby/lib/ruby/gems/1.9.1/gems/eventmachine-0.12.10/ext/gem_make.out

Regards, Alexey Zakharov

Installing from the git repository no longer works (currently) under 1.9.2. Instead, you need to simply:

gem install eventmachine --pre
gem install thin

If you try to build the gem under 1.9.2 from the github repo you may see this (included for search engine hits):

C:\eventmachine> rake build
(in C:/eventmachine)
cd ext
C:/Ruby/bin/ruby.exe extconf.rb
checking for rb_trap_immediate in ruby.h,rubysig.h... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.

Streaming data from Sinatra/Rack application

7 votes

I am trying to stream textual data (XML/JSON) from a Ruby (1.9.1p378) Sinatra (1.0) Rack (1.2.1) application. The suggested solutions (e.g. http://stackoverflow.com/questions/3027435/is-there-a-way-to-flush-html-to-the-wire-in-sinatra) do not seem to work - the server just blocks when I yield elements of some infinite stream (e.g. from %w(foo bar).cycle). I tried webrick and thin as servers.

Any suggestions on getting this done? Should I use http://sinatra.rubyforge.org/api/classes/Sinatra/Streaming.html and if so how would I use it in my application?

Neither Webrick nor Thin support streaming that way. You could try Mongrel or Unicorn. If you want to use Thin or Rainbows!, you have to hook into the event loop in order to achieve streaming:

require 'sinatra'

class Stream
  include EventMachine::Deferrable
  def initialize
    @counter = 0
  end

  def each(&block)
    if @counter > 10
      succeed
    else
      EM.next_tick do
        yield counter
        each(&block)
      end
    end
  end
end

get '/' do
  Stream.new
end

I recently wrote a EventSource implementation that way:

require 'sinatra'

class EventStream
  include EventMachine::Deferrable
  def each
    count = 0
    timer = EventMachine::PeriodicTimer.new(1) do
      yield "data: #{count += 1}\n\n"
    end
    errback { timer.cancel }
  end
end

get '/' do
  EventMachine.next_tick do
    request.env['async.callback'].call [
      200, {'Content-Type' => 'text/event-stream'},
      EventStream.new ]
  end
  [-1, {}, []]
end

If you want to use Webrick for Streaming: here is a patch.

When to use node.js vs sinatra vs rails?

7 votes

What are the best uses for these 3 languages/frameworks? Is it useful to mix all of them (or 2)?

If you're building a complete web application, you should probably use Rails as it provides the most comprehensive services. You can also leverage an enormous amount of work produced by the community.

Sinatra is great for producing really thin, no-nonsense application services. You don't get much to work with, but it is very fast. If you need a database connection, you will have to add it in, and things like this can make creating even medium-sized applications a challenge. Basically if you need something very simple and don't need Rails, you probably need Sinatra.

node.js is a great new framework for producing responsive, scalable applications, but it doesn't have nearly the library of add-ons that a mature platform like Rails does. node.js really excels at applications based on streaming and on-demand data transformation. Some of the examples produced in Node Knockout are very interesting, but these were produced by some exceptional teams.

While the Node Knockout entries are very well done, when compared to the sort of applications that were produced in the 2009 Rails Rumble they seem to come across as toys lacking depth and complexity. This is not to discredit the work done by the Node Knockout teams, but it does show that the strengths of node.js are mostly to do with real-time events and less with conventional more ordinary DB-based apps.

What do i need to know to contribute to Rails?

7 votes

I am a user of Ruby on Rails framework and am thinking of giving back to it by contributing to the code. I understand that there is a need for thorough understanding of the Ruby language concept to contribute? Ive cloned the project, looked at the code, check out the tickets and have no clue how to begin? From what i see the Rails Framework utilizes metaprogramming alot? So what other aspect of Ruby do i have to master in order to start contributing? or to contribute is to know the ins and outs of Ruby? Thank you.!

It has been quite awhile since I ran across ruby code I didn't understand, but there are parts of rails that really push my understanding of ruby.

That being said, even if you are at more of an intermediate level with the language, figuring out how rails works would probably be a great way to up your game.

As for where to begin, I would start by writing plugins. Think of a cool plugin you could write that hasn't been done yet, or something that you think you could do better. Whatever it is you want to do, chances are you are going to need to hook into some sort of rails internals to do it, which will give you a good place to start. Once you have a bit of knowledge of a given area of the framework, see if there are any open bugs in that area you can fix.

The barrier is quite high, since rails is a quite large and complex program written by people who have a high level of mastery with an incredibly flexible language. It is also a great challenge that will probably make you a better programmer in the end. :)

Edit: to help out banister

This is actually pretty interesting if it is a snippet designed to illustrate how class variables and singleton classes work, but there are much clearer ways to accomplish the same thing if it is "real" code.

Singleton Class

In ruby, every object has a parent class that it gets its methods from. This is pretty normal for OO languages. What isn't normal is ruby also has a concept called "object individuation, that means that objects can have methods that are only on a specific instance, but not on the parent class. here is a quick irb example

irb(main):001:0> foo = "bar"
=> "bar"
irb(main):002:0> def foo.foo_method
irb(main):003:1>   "this is _only_ on the foo instance, not on all strings"
irb(main):004:1> end
=> nil
irb(main):005:0> foo.foo_method
=> "this is _only_ on the foo instance, not on all strings"
irb(main):006:0> "bar".foo_method
NoMethodError: undefined method `foo_method' for "bar":String
    from (irb):6
irb(main):007:0> foo = "New string"
=> "New string"
irb(main):008:0> foo.foo_method
NoMethodError: undefined method `foo_method' for "New string":String
    from (irb):8

First, we assign a string to a variable. Next, we define a method on that instance. Calling the method works, but calling it on a new string does not work. Assigning a new string instance to our foo variable also does not have that foo method.

To make object individuation possible, there needs to be something in between the instance, and the parent class. That thing is the singleton class, and every instance has one that is unique from all other object instances. When ruby is looking up a method, it will look at the singleton class before the parent class.

Believe it or not, even if you have never heard of this before, you have probably already used it. If you want to add a "class method" to something, you usually do this

class Foo
  def self.bar
    "i am on the class"
  end
end

Foo.bar #=> "I am on the class"

To start from the top, the statement class Foo is the exact equivalent of this Foo = Class.new do. What is happening is you are assigning a new instance of type Class to the constant Foo. (as an aside, this is what I love about ruby. most of the syntax is actually just sugar around some core concept)

Inside a class definition, self refers to the class, so saying def self.bar is essentially saying "define the method bar on the singleton class of the Class instance that is stored in the constant Foo".

If you find this stuff complex, it is because it is. I don't think there is anything more complected in ruby other then what happens during method lookups, and singleton classes are a big part of what makes it so complex.

Class Variables

A class variable is prefixed by @@, and basically is a variable whose scope is all instances, the class object, and all classes that inherit from the class. I'll do a quick irb to illustrate this in action

irb(main):003:0* class Foo
irb(main):004:1>   @@instance_count = 0
irb(main):005:1> 
irb(main):006:1*   def initialize
irb(main):007:2>     @@instance_count += 1
irb(main):008:2>   end
irb(main):009:1> 
irb(main):010:1*   def count
irb(main):011:2>     @@instance_count
irb(main):012:2>   end
irb(main):013:1> end
=> nil
irb(main):014:0> 
irb(main):015:0* class Bar < Foo
irb(main):016:1> end
=> nil
irb(main):017:0> 
irb(main):018:0* f = Foo.new
=> #<Foo:0x7fa9089c7da0>
irb(main):019:0> f.count
=> 1
irb(main):020:0> 
irb(main):021:0* b = Bar.new
=> #<Bar:0x7fa9089be0e8>
irb(main):022:0> b.count
=> 2
irb(main):023:0> 
irb(main):024:0* f.count
=> 2

First, we define Foo. Foo has a class variable that tracks how many instances were created. To do that, we increment that variable in the constructor, and lastly just define a quick getter. Bar just extends Foo.

Now, to see it in action, we make a new foo, and check the count, which is 1. Next, we create a new bar, and check the count, we are now at 2. Just to make sure that both Foo and Bar share the same count, we check our foo instance again, and it is 2 now.

Class variables are a very situational feature that you usually don't see outside of framework code. It is usually used when you want to keep track of all instances of a given class.

Banisters Code

class << Object.new 
  @@var = 6 
end 


String.class_variable_get(:@@var) #=> 6

Your code is showing those two concepts in action. First, we open up the singleton class of a new object, and put a class variable on it. If it were anything other then a class variable, those first three lines would be the most useless ruby code in the world. But since it is a class variable, that becomes visible to the class, all instances of the class, all classes that inherit from the class, and all instances of all classes that inherit from the class. Since the class we are talking about is object, that means everything. So doing class_variable_get on String gives it to you, but you could access that variable from any class or instance you wanted to.

I am sorry

This is horribly complected stuff that is very hard to explain clearly. These concepts took me about a week or so of fairly concerted effort to wrap my head around. If you have any questions about what I wrote, feel free to ask, but don't be discouraged if you don't "get it".