Friday, February 29, 2008

MysqlUtils plugin

Here's a little plugin I wrote to help with MySQL migrations. Things like foreign keys, reference tables, join tables are easy to do. This isn't really documented, but the code is pretty self explanatory. Here are a list of the methods:
  
def add_foreign_key(from_table, to_table, is_required = false)

def remove_foreign_key(from_table, to_table)

def remove_foreign_key_column(from_table, col_name)

def define_foreign_key_column(from_table, from_column, to_table, to_column = "id")

# join_table = addresses_users
# must pass in the correct order, it won't order it for you
def create_join_table(table_one, table_two, fk = true)

def drop_join_table(table_one, table_two)

# reference table is a simple table with just an id and name column
# for each table passed in it will create add a foreign key
# relationship
def create_reference_table(tname, *referenced_by)

def drop_reference_table(tname, *referenced_by)

Install by:
./script/plugin install http://stonean.googlecode.com/svn/mysql_utils/trunk

Nothing spectacular, but may be useful to someone.

Wednesday, February 27, 2008

Stage Update

The data partial was out of sync with the helper; method names were different. I have corrected the issue and updated the code.

This was related to the the naming collision issue.

Monday, February 25, 2008

attachment_fu and s3

I kept getting the following error:
AWS::S3::NoSuchBucket (The specified bucket does not exist)

So I did some digging in file vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/backends/s3_backend.rb, I replaced:
#Bucket.create(@@bucket_name)

with:

begin
Bucket.find(@@bucket_name)
rescue AWS::S3::NoSuchBucket
Bucket.create(@@bucket_name)
end

There must have been a reason to comment out the Bucket.create line, but I have yet to find it in my Google searches. So this makes me think there is something more behind the Bucket.create method than I realize at this time.

So, I'm not sure if it is the correct fix, but it works. If you know of a problem with this fix, please let me know.

Matz Google Tech Talk

Here are some notes from the talk Matz gave. It doesn't every little detail of the whole ~31 minute talk with a ~19 minute question/answer session. Just some of the points I wanted to remember.

1.9 Incompatibilities with 1.8

block parameters
local variables only in pipe
blocked scoped
#The following will not work in 1.9
# all will be errors
ary.each{|$var| ... }
ary.each{|obj.attr| ... }
ary.each{|a2[0]| ...}

# in 1.9 gives warning, but does not alter x because the scope is limited
# the inside of the block.
x = 15
loop{|x| x = 10; break}
p x #=> 15

strings
# no longer enumerable
# lines? chars? bytes? : multiple implemenations/uses are ambiguous
# use the following to remove ambiguity:
"foo\nbar".each_line
"foo\nbar".each_char
"foo\nbar".each_byte

# or can use enumerator
"foo\nbar".lines.each{...}
"foo\nbar".chars.each{...}
"foo\nbar".bytes.each{...}

m17n - unicode
# 1.8 code based on bytes
# 1.9 is now encoding aware
# strings are now character based
# can specify encoding (similar to Python)
-* coding: UTF-8 -*-

** will not work with UTF-16 yet


** can specify file encoding:

open(path, "r:UTF-8"){|f|
...
}

2.0 will be targeting scalability but is vaporware now.

Some Features are Gone:
  • VERSION and friends
  • Kernel.to_a
  • Kernel#getc
  • Object#type
  • Hash#index
  • ENVindex
  • Symbol#to_int
  • Removed Array and Hash #indices, #indexes


# A lot of new features...
  • Lamdba by ->
    • ->(x){x*2}.(2) # => 4
    • ->(x=5){x*2}.() # => 10
    • lambda{|x=5| x * 2} # Error
  • method invocation by .()
  • multiple Splats in argument list
  • mandatory arguments at the bottom
  • chain enumerators
    • ary.map.with_index{|x,i| ... }
    • ary.find.with_index{|x,i| ... }
external iterators:
e1 = [1,2,3,4].each
e2 = [10,11,4].each
loop{
p e1.next + e2.next
}
# prints 11, 13, and 7
# silent exception because e2 reaches end of sequence


YARV
Developed by Koichi Sasada
Bytecode Interpreter
Will run other languages. Optimized for Ruby

Core performance improvement max 50 times faster
Benchmarks 2..10 times faster

Improves Core only
Using Same Library
Using Same GC
Programs will NOT run 50 times faster.

Streamlined internal data use to reduce memory consumption.

Answers to some of the questions asked by the attendees.
  • Some features of 1.9 will be added into 1.8 if possible.
  • Official specification coming which is a joint effort between:
    • Ruby Core dev team
    • JRuby team
    • Rubinius team
  • Oniguruma is the regex expression engine
  • Continuations can be used but must be required in 1.9 (require 'continuations').
    • Matz states they are dangerous.
  • 1.9 does not use any specific encoding.
  • 2.0 will have named parameters. Very primative compared to Python
I enjoyed the talk, and would recommend taking the time out to listen to Matz.

If you picked up on something important I missed, please let me know.

Saturday, February 23, 2008

Stage Update: Naming collisions

I've just updated the stage plugin to fix a naming collision issue when Rails utilizes two or more helpers with the same name. Previously the method names were simply the column name: User.first_name would have a first_name method in the users_helper. Now that method is defined as user_first_name.

Having the partial setup makes this a relatively painless fix if you have existing code you need to fix.

Friday, February 22, 2008

Piston

If you haven't heard about it, Piston is a very nice gem that will help you manage your plugins. This is especially true if you have an svn:externals setup with your plugins.

Here's a quick step by step I recently did to get my Stage plugin into my latest project:

#Install the gem
sudo gem install piston -y

piston import http://stonean.googlecode.com/svn/stage/trunk vendor/plugins/stage

piston lock vendor/plugins/stage/

svn commit -m "started stage plugin management with piston"

Checkout the Piston site for more usage options.

François Beausoleil (author of Piston) pointed out that the "piston lock" step is not required. I should have noted this in my original post. Thanks for the notice François.

If you leave this out and run "piston update", all your plugins under piston control will be updated. Just be mindful of that issue.

Tuesday, February 19, 2008

Stage Update

The generator was creating a now defunct 'data' directory in app/views. This has been corrected.

02.22.2008: Cleaned up the helper format

Tuesday, February 12, 2008

collect and flatten

I run into this scenario many times and never really thought about changing it until recently. It's very common, I have an array of values and I want to perform an action on each item. My return value would be an array of the results from the action.

Here's a sample lookup that most likely will return an array (unless it's a small city):
def zip_codes_for_city(city)
ZipCode.find(:all, :conditions => ["city = ?",city])
end
So then I want to get the values for a list of cities. I would, without thinking about it, do the following:
def zip_codes_for_cities(cities)
rvalue = []
cities.each do |city|
rvalue << zip_codes_for_city(city)
end
rvalue
end
That is really readable and easy to follow. Without losing too much readability, we can shorten this by doing the following:
def zip_codes_for_cities(cities)
rvalue =[]
cities.each{|city| rvalue << zip_codes_for_city(city)}
rvalue
end
If you are know a little more about Ruby, this will be equally readable and much more succinct:
def zip_codes_for_cities(cities)
cities.collect{|city| zip_codes_for_city(city)}.flatten
end
If there are no performance differences, then the format would depend on the audience. In a high usage application, the introduction of the extra rvalue variable would seemingly increase your memory footprint. I haven't benchmarked that, just an assumption.

Friday, February 1, 2008

Stage Improvements?

I spent some time lurking on the RubyOnRails list and eventually engaged in a couple of conversations. I came away with three important notes:

1) As this was my first publicly announced code released, I realized I should have done this earlier. Great to get feedback.
2) Some simple design changes were made to further DRY up some code from simply reading other posts (not related to Stage). i.e. moved error_messages_for to the form partial.
3) Listening to others challenge the architecture with their own, well thought out ideas, has been nice. Even though I don't agree with some of the interpretations of design concepts, it was good.

The main goal for this generator is simplicity. I want to get to the HTML as quickly as possible. There obviously has to be some code to format the data as required and I would like to keep that code to a minimum. I also don't want to "junk up" the controller with presentation (HTML) code. This is why I involved the helpers. In order to minimize code in my _data.html.erb file, I thought of calling one method (defined in the helper) to house the presentation logic.

I think using the helper in this fashion is the correct approach. However, I also think there is probably a better way to do this, so if you have any ideas, please let me know. If you think it's a completely wrong approach I would also like to hear your ideas.

To those of you who have some code you would like to release but never can seem to find the time, I highly recommend doing so. I think it's good experience.