Remember #defined FOO = 1; from your C days? Or public static int FOO = 1; from java? Well, I recently had reason to use this functionality within Ruby when I wanted to define a series of types which would each have a unique id used in a database, but I really didn't want to waste the space on a string.
I ended up with two methods, the first which takes a name and an enumerable, sets class variables for each element of an array (such as %w(The fat cat sat on a hat)) as well as a method for resolving an integer to a string.
Taking the following class:
1 2 3 4 5 6 |
require 'definer' class Project define_class_types :states, %w(planning development testing production) define :myself, 1 end |
you would have the following definitions:
Project.planningProject.developmentProject.testingProject.produtionProject.@@states(not accessible outside of class, but that's easily remedied)Project.myselfAdditionally, you would have a method Project.state_name(state) which would take a integer and return the string associated with that state. So, Project.state_name(0) or Project.state_name(Project.planning) would both return "planning".
In the define_class_types method, the name can be either pluralized or singular. The array of the types will be pluralized (as in states above) and the method for getting the string singularized (as in Project.state_name above.
There is one dependency -- it uses ActiveSupport's pluralize and singularize. The code follows below or you can download it here: definer.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
require 'active_support' class Module def class_attr_reader(*symbols) symbols.each do |symbol| self.class.send(:define_method, symbol) do self.class_eval "@@#{symbol}" end end end def define(name,value) name = name.to_s class_variable_set("@@#{name}",value) class_attr_reader(name) end def add_class_types(name, types = []) name = name.to_s class_variable_set("@@#{name.pluralize}",types) types.each_with_index do |type, index| class_variable_set("@@#{type}", index) class_attr_reader(type) end self.class.send(:define_method,"#{name.singularize}_name") do |arg| self.class_eval "@@#{name.pluralize}[#{arg}]" end end end |
1 2 3 4 5 6 7 8 9 |
$ irb >> 5 * "a" TypeError: String can't be coerced into Fixnum from (irb):1:in `*' from (irb):1 >> "a" * 5 => "aaaaa" >> |
The reason is quite simple — everything in Ruby is an object. The '*' behaves differently when applied to different objects (and, in fact, can be overridden). So, because the '*' method as implemented for Fixnum expects a number as its argument, and a String isn't a number, it complains. By the same token, the behaviour of '*' for String is for the string to repeat itself as many times as the argument specifies, it works.
Recently I re-read Rick Cook's Wizard's Bane and The Wizardry Compiled. I strongly recommend that series to anyone who wishes to be a serious developer.
And I realized that I'd been missing out on a lot of fun lately. Ruby's become so dry and boring, it isn't funny. I really miss the days of having a function named corned_beef which uses such wondrous numbers as 65353. And stack based programming really is da-bomb! I remember all of the fun I used to have with it and have realized it's time to go back to it.
rubygems has a built-in server which you can use to peruse the rdocs for a gem. To use it, type gem server at a prompt and then point your browser to href://localhost:8808.
ruport comes with an extension, ruport-util which adds, among other things, the ability to create graphs from reports. However, it's hardcoded to do line graphs. This is a code snippet which will (for png and jpg graphs, the others should follow) allow you to create other types -- it's just bar graphs, but the others would follow....
You only see what your eyes want to see
How can life be what you want it to be
You're frozen... -- Madonna
The way to ensure that your production rails apps don't get messed up should the provider update gems is to freeze them. Moreover, it's a good idea to embed the gems/plugins needed for a particular application; it makes distribution of different versions easier, not to mention that you know exactly with which resources your code is working.
To freeze rails, you can either do a rake rails:freeze:gems, which uses the installed gems to freeze, or rake rails:freeze:edge which freezes off of the rails subversion repository. The last one allows you to specify a version to which to freeze. When it's time to thaw, rake rails:unfreeze will remove the frozen rails installation.
To embed gems in your application, use Gems On Rails, which will place gems into vendor/gems.
This isn't much of a tip, per se, but it's something I ran into today.
The way to having multiple rubygem repositories is to set the GEM_PATH environmental variable. This will allow you to use gems from more than one location -- something useful on shared servers.
Next, to figure out how to specify the version of rubygems you're using -- I've tried GEM_HOME and RUBY_LIB and my PATH, but it's not working properly on the shared host. More once I figure that one out.
students.map_by_name which will return the names of the students as an array. This is a ruby gem and you can find more information about it at: map_by_method
1 2 |
foo = Bar.find(:first, :conditions => {:name => "Fred"}) || Bar.new(:name => "Fred") |
Model.find_or_create_by_COLUMN, where COLUMN is the name of the column.
Ruby2Ruby is a gem which allows you to look at the code of a class. To install do:
gem install ruby2ruby(you may need to do a sudo in front of it)
Here's an example of its use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
irb(main):001:0> require 'rubygems' => true irb(main):002:0> require 'ruby2ruby' => true irb(main):003:0> class Foo irb(main):004:1> def fud irb(main):005:2> puts "groo" irb(main):006:2> end irb(main):007:1> end => nil irb(main):008:0> Ruby2Ruby.translate Foo => "class Foo < Object\n def fud\n puts(\"groo\")\n end\nend" irb(main):009:0> class Foo irb(main):010:1> def fi irb(main):011:2> puts "fo" irb(main):012:2> end irb(main):013:1> end => nil irb(main):014:0> Ruby2Ruby.translate Foo => "class Foo < Object\n def fi\n puts(\"fo\")\n end\n \n def fud\n puts(\"groo\")\n end\nend" irb(main):015:0> class Foo irb(main):016:1> def fud irb(main):017:2> puts "Hello World!" irb(main):018:2> end irb(main):019:1> end => nil irb(main):020:0> Ruby2Ruby.translate Foo => "class Foo < Object\n def fi\n puts(\"fo\")\n end\n \n def fud\n puts(\"Hello World!\")\n end\nend" irb(main):021:0> |
Notice how it changes the class output. One thing I have found is that if you create instance variables outside of a method they won't show up in the output. Otherwise, it works quite well and is a good addition to a metaprogramming toolkit.
sub does not do the same thing as sub!:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
irb(main):001:0> a="something" => "something" irb(main):002:0> b=a.sub(/e/,"E") => "somEthing" irb(main):003:0> b => "somEthing" irb(main):004:0> a => "something" irb(main):005:0> c=a.sub!(/thing/,"one") => "someone" irb(main):006:0> c => "someone" irb(main):007:0> a => "someone" irb(main):008:0> b=a.sub(/thing/,"body") => "someone" irb(main):009:0> b => "someone" irb(main):010:0> c=a.sub!(/thing/,"body") => nil |
They behave the same when there is a pattern matched and a change, however, when there is not a pattern matched they behave differently. This can cause grief as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
irb(main):011:0> a="something" => "something" irb(main):012:0> b=a.tr("n-za-m","a-z") => "fbzrguvat" irb(main):013:0> b=a.sub(/thing/,"one").tr("n-za-m","a-z") => "fbzrbar" irb(main):014:0> a => "something" irb(main):015:0> b=a.sub!(/body/,"one").tr("n-za-m","a-z") NoMethodError: undefined method `tr' for nil:NilClass from (irb):15 from :0 |
This is the general behaviour of any method ending in !, so be careful when you are using them.
method_missing is to leave out a super. This means that if you don't handle all the cases which could cause an invocation of method_missing then the exception will be lost. So, always include super, unless you have a good reason not to do so.
DateTIme objects to the seconds since the epoch:
1 2 3 4 5 6 7 8 9 10 |
>> d=DateTime.now => Thu, 03 Jan 2008 15:49:21 -0500 >> t=Time.parse(d.strftime("%c")) => Thu Jan 03 15:49:21 -0500 2008 >> t.to_f => 1199393361.0 >> t.to_f.floor => 1199393361 >> |
This is just a simple piece of code to create a hash of the values of properties of a Ruby object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Object # All objects will inherit this method def make_hash h={ } # start out with a "known quantity" self.instance_variables.each do |v| # for each instance variable do # Instance variables start with an @, so strip it v.sub!(/@/,"") # add our key/value pair to the array only if the variable has # been exposed h[v] = self.send(v) if self.respond_to? v end h # return the hash end end |
Ok, let's test it out:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$ irb >> load 'nifty.rb' => true >> "a".instance_variables => [] >> "a".make_hash => {} >> class Foo >> attr_reader :bar, :fud >> def initialize >> @bar="Hello World" >> @fud=123 >> @groo = Time.now >> end >> end => nil >> f=Foo.new => #<Foo:0xb7857174 @groo=Wed Jan 02 10:20:36 -0500 2008, @fud=123, @bar="Hello World"> >> f.instance_variables => ["@groo", "@fud", "@bar"] >> f.make_hash => {"bar"=>"Hello World", "fud"=>123} >> quit |
Note that although Foo has three instance variables, only two of those have been exposed (the attr_reader line).