Tuesday, July 31, 2007

sanitizing Rails input parameters

I really like how Tapestry automagically escapes HTML input when posted from a form. In fact, it was just great never having to worry about that when coding. I'd like to have the same functionality in Rails especially after reading about Rails XSS vulnerabilities and work-arounds. Since the webapp I'm writing has no requirements for allowing formatted user input, I just need something simple to clean/sanitize all the params. Here's the latest:

#escapes all HTML from the given hash's values (recursively applied as needed)
def sanitize(hash)
dirty_hash = hash

dirty_hash.keys.each do |key|
value = dirty_hash[key]

if(value.kind_of?Hash)
dirty_hash[key] = sanitize(value)
else
if (value && value.kind_of?(String))
dirty_hash[key] = CGI.escapeHTML(value)
end
end
end

hash = dirty_hash
end

This is then invoked by a before_filter. Seems to do the job, is there a better/cleaner/faster way of doing this? Let me know how it could be improved...

Wednesday, July 11, 2007

Ruby: constant time for include?

Sure would be nice if the Ruby docs (including this book) would provide more details to the implementation of the Set class. According to this, Set implements its backing collection with a Hash, which would essentially mean that it's synonymous (to some degree) with Java's HashSet. Thus providing a constant time lookup when Set#include? is invoked. Just for grins I benchmarked this in irb with a million Fixnums and was pleased with the 15 microsecond lookups. I looked at the source of both and found a fair amount of similarity.

Friday, June 22, 2007

Sorting serialized objects from a YAML file, Ruby vs Java

I recently had a large dataset that I needed to sort, basically a bunch of objects with 9 string attributes. I dumped them to a YAML file so I could benchmark various aspects of sorting (Class#to_yaml really rocks, really). As it turns out Enumerable#sort_by was the more efficient way to go rather than Enumerable#sort (check it). The dataset contained 5429 unique objects, here's the benchmarking I did in irb:

>> bm do |x|
?> x.report("all"){results.sort{|a,b|a.account_name <=> b.account_name}}
>> end
user system total real
all 0.070000 0.000000 0.070000 ( 0.073835)
>> bm do |x|
?> x.report("all"){results.sort_by{|a|a.account_name}}
>> end
user system total real
all 0.020000 0.000000 0.020000 ( 0.020745)


Noticeable difference between the two methods, and quite pleasing to see how fast sort_by performed (Apple MBP 2.33GHZ 2GB RAM Ruby 1.85). Then the thought occurred to me, since I have this in a YAML file, I could write something really quick in Java and see how fast the sort would be by dumping it into a TreeSet.

So I set off to find out how I could marshal the data into POJOs from the YAML file. JYaml and JvYaml are the only (insofar as I looked) open source Java YAML libraries. Both seem half-baked in their own right, likely containing just the functionality the respective author needed and not much more (at least that's how it appeared). I ended up using JvYaml and had to search-replace the yaml entry identifier("tag:yaml.org,2002:map") so that JvYaml would create HashMap instances for me instead of its completely worthless PrivateType class.

From there I iterated through the maps to create the POJOs and shoved them into a HashSet. Once that was completed I created the comparator for my POJO, passed it into the TreeSet constructor and then timed an addAll giving it the entire HashSet:
#1: 55ms
#2: 28ms
#3: 27ms (pretty much constant thereafter)

Interesting results eh? The dataset I used was from a randomized generator and now that I have these initial numbers ( Ruby appears to have won this round), I want to test a larger set. And to be really fair I should do more research on benchmarking best practices (in addition to still needing to take my stats class).

Tuesday, April 17, 2007

X11 tunneling via SSH in OSX

-X isn't enough for the ssh args in OSX according to this post. So, "ssh -XY hoser@remotebox" worked just great when remoting in to a Suse server and firing executing fvwm.

Thursday, April 12, 2007

1024 vs 2048 RSA encryption/decryption fun in Ruby

Using Tobias's handy openssl wrapper I decided to run a couple timing tests to see how well Ruby's openssl implementation performed in encrypting/decrypting a set of 1000 identical messages, with 1024 bit and 2048 bit keys. Nothing scientific about this, just three runs of the script on my MacBook Pro (2.33 GHZ, 1G RAM).

Encrypted text: "This is a much longer message since than what I intend to encrypt"

Encryption results, avg time
1024: 0.000300691s
2048: 0.001010027s

Decryption results, avg time
1024: 0.005472064s
2048: 0.035524023s

Pick your poison.

Monday, April 02, 2007

One last hitch

According to this, friendly errors such as

/opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/mapping.rb:520:in `class_schema_variable': undefined method `class_variables' for nil:NilClass (NoMethodError)
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/mapping.rb:353:in `schema_ns_definition'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/mapping.rb:380:in `schema_definition_classdef'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/registry.rb:198:in `schema_definition_from_class'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:73:in `any2soap'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:37:in `obj2soap'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:127:in `stubobj2soap'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:114:in `each'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/mapping/literalregistry.rb:114:in `stubobj2soap'
... 15 levels...
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/rpc/proxy.rb:126:in `call'
from /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/lib/soap/rpc/driver.rb:179:in `call'
from (eval):6:in `search'


prohibit the following default generated objects from correctly mapping when invoking the NetSuite searches:

  • OpportunitySearchBasic

  • EmployeeSearchBasic

  • PartnerSearchBasic

  • ItemSearchBasic

  • TimeBillSearchBasic


Each of which contain an entity named 'class', and while soap4r intelligently generates an attribute 'v_class', it also erroneously generates accessor methods that override Object.class. Big woops. Extremely easy fix just by renaming those methods.

Wednesday, March 28, 2007

NetSuite and Ruby, the final chapter

NetSuite requires a maintainable session for subsequent requests after the initial login by way of several cookies they return upon successful authentication. The standard Net::HTTP library requires mucking with headers by hand in order to perpetuate cookie information. We were easily able to override SOAP::NetHttpClient::Response and SOAP::HTTPStreamHandler in the soap4r gem in order to provide the functionality for maintaining the NetSuite session.

However
, NaHi's http-access2 library already provides cookie management (even persistence if needed). After installing visnu's gem it's simply a matter of turning off the SSLConfig#verify_mode and everything works flawlessly. The final test code:
require 'rubygems'
require_gem 'soap4r'
require 'defaultDriver' #generated by soap4r
require 'pp'

driver = NetSuitePortType.new
driver.wiredump_dev = STDOUT
driver.proxy.streamhandler.client.ssl_config.verify_mode = nil

passport = Passport.new
passport.email = '123123123@123123123.com'
passport.password = '123123123'
passport.account ="123123"
passport.role = RecordRef.new
passport.role.xmlattr_internalId = '123123123'

driver.login(LoginRequest.new(passport))

record = RecordRef.new()
record.xmlattr_internalId = '123123123'
record.xmlattr_type = RecordType::Customer

pp driver.get(GetRequest.new(record))

Everything said and done, this has been a good learning experience. But more than anything it has exposed one of the Ruby community's greatest weaknesses: documentation. I've known this since the beginning of my Ruby days (2004) and have grown accustomed to reading code. Nevertheless, that takes time, and usually more time than scanning decently-documented APIs. The other side of this story is that I understand the Japanese/English language barrier which may intimidate or otherwise prevent library maintainers from providing more documentation. In the end, Ruby is a community-driven language, which is not sponsored by massive corporate dollars (cough, Java, cough, .Net) and its beginnings have solid Japanese roots. Here's to hoping that documentation improves as more libraries become available and the language matures. In the mean time, thank goodness for Google, mailing lists, blogs and IRC. I believe that documentation practices exhibited by 37Signals and the Rails community are the guiding example, they're not perfect, but the situation continues to progress.

soap4r and NetSuite revisited

In the last post I mentioned that the generated file (default.rb) contained redundant object definitions. I also ended with the concern about the RecordRef element not appearing to be correct. That led me to look at the generator and as it turns out I was invoking the wrong one. Funny thing, the soap4r gem ships with wsdl2ruby.rb and places them in two different locations. The first (in OSX) was in /opt/local/bin where the second was in /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/bin/. Yesterday we explictly invoked the one in the gem, disregarding the the executable wrapper in /opt/local/bin. That was the mistake, as the results can be easily reproduced with executing
ruby /opt/local/lib/ruby/gems/1.8/gems/soap4r-1.5.5.20061022/bin/wsdl2ruby.rb --wsdl https://webservices.netsuite.com/wsdl/v2_5_0/netsuite.wsdl --type client
The problem being that the wrapper adds the necessary "requires" for referencing the gem's code (correct results) and not the standard (incorrect results).

Using the correct wsdl2ruby the following test works without any modifications to the generated code:


require 'rubygems'
require_gem 'soap4r'
require 'defaultDriver'

driver = NetSuitePortType.new
driver.wiredump_dev = STDOUT

passport = Passport.new
passport.email = 'test'
passport.password = 'test'
passport.account ="test"

role = RecordRef.new
role.xmlattr_internalId = '123123123'

passport.role = role

response = driver.login(LoginRequest.new(passport))

pp "successful login: #{response.sessionResponse.status.xmlattr_isSuccess}"

Tuesday, March 27, 2007

adventures in soap4r and NetSuites webservices

Spent most of the day becoming familiar with soap4r in order to communicate with NetSuite. Turns out that the soap4r shipping with Ruby 1.8.5 is outdated and quite buggy compared to its gem counterpart. In fact, we would've saved a couple hours if we knew that was an option before we even started setting breakpoints.

WSDL2Ruby worked decently enough with NetSuite's WSDL but the generated file containing the classes (default.rb) had redundant entries, like 35 definitions for the Passport object. Next, there was an issue with some of the classes being incomplete. For example, RecordRef was:

class RecordRef
@@schema_type = "RecordRef"
@@schema_ns = "urn:core_2_5.platform.webservices.netsuite.com"
@@schema_element = []

def initialize
end
end
which is incorrect as it is defined as:

<complextype name="RecordRef">
<complexcontent>
<extension base="platformCore:BaseRef">
<attribute name="internalId" type="xsd:string">
<attribute name="externalId" type="xsd:string">
<attribute name="type" type="platformCoreTyp:RecordType">
<!-- primary record internalId -->
<!-- record type -->
</attribute>
</attribute>
</attribute>
</extension></complexcontent></complextype>

The object we created to get the request working correctly is

class RecordRef
@@schema_type = "RecordRef"
@@schema_ns = "urn:core_2_5.platform.webservices.netsuite.com"

attr_accessor :internalId
attr_accessor :externalId
attr_accessor :type

def initialize(internalId = nil, externalId = nil, type = nil)
@internalId = internalId
@externalId = externalId
@type = type
end
end


Finally, calling

response = driver.login(LoginRequest.new(passport))
produced a successful response from NetSuite. However, something still seems awry with the request as the role element is not properly formed:

<n1:login xmlns:n1="urn:messages_2_5.platform.webservices.netsuite.com" xsi:type="n1:LoginRequest">
<n1:passport xmlns:n2="urn:core_2_5.platform.webservices.netsuite.com" xsi:type="n2:Passport">
<n2:email>test </n2:email>
<n2:password>test</n2:password>
<n2:account>test</n2:account>
<n2:role xsi:type="n2:RecordRef">
</n2:role>
</n1:passport>
</n1:login>

Friday, March 09, 2007

Geocoding service findings and comparison

Google Maps:

  • requires an API key

  • http geocode service that'll return results in XML or JSON

  • lots of documentation on javascript implementation

  • issues in resolving bad addresses by suggesting alternatives and flagging them with the best accuracy-level

  • comma separated values on the query string

  • 50k per day limit


Yahoo MapService:

  • http geocode service return results in XML or PHP

  • cleaner documentation

  • ApplicationID required

  • address suggestion behaves more as expected and accuracy seems better

  • query string attributes expected in request

  • 5k per day limit


Note regarding accuracy statements above: I tested both services with variations on my home address which can be identified strictly by number coordinates or with the street name (Valley Sage Drive). Google's service did not find my address in any format I tried but continued to respond with varied suggestions none of which were applicable (though it stated they were the highest level of accuracy). As for yahoo, it found my address in every variation and for those that it suggested it clearly indicated a warning as an attribute of the ResultSet. Odd that Google Maps works for my address, but the geocoding service does not (and they're supposed to be integrated).


Both services provide city and state values when given a zip.

Determining valid addresses may prove to be a bit difficult given that both services "suggest" what the closest match could be.

Thursday, March 08, 2007

a more efficient top for OS X

echo "alias top=\"top -d -o cpu\"" >> ~/.profile


The "-d" bypasses the strain on Mach"-o cpu" and will sort by procs consuming the most CPU.

Wednesday, March 07, 2007

HttpUtility.UrlEncode, fails again

Bitten a couple months ago by the fact that System.Web.HttpUtility.UrlEncode does NOT encode ticks/single-quotes ('). How lovely. I filed a bug, someone at MS closed it out almost a month later with no explanation. It's current state is "won't fix".

Bitten again by another bug in the same function. What a shocking surprise. UrlEncode does not escape/encode the plus (+). It should encode it as %2B, but no, it doesn't, it just leaves it in the string.

Hosers.

Monday, February 26, 2007

Mike Heath stumbles upon pile of java JIT

Mike has had a great time with this and we've had some intriguing conversations about it. Once he posts his svn repo url I'll snag the code, run the tests on my MacBook Pro and add the results to his matrix.

Wednesday, February 21, 2007

UPS xml online tools and Ruby

UPS provides "XML Online Tools" to account holders here. Once you register as a developer you can get access to these tools and the associated specs and sample code. I needed access to the Rates and Service Selection tool, which happened to contain VB script and Java sample code from 2001. The docs are very thorough, but the code (as expected) was very outdated. Once I had it working with 1.5 (not too difficult at all) I was able to better understand what was needed for working with their HTTP-XML services.

The following barebones Ruby code is the result of a quick implementation:

require 'net/https'
require 'uri'

auth_xml = (access request xml doc goes here)
req_xml = (request xml doc goes here)
both_xml = auth_xml + req_xml

url = URI.parse("https://....")
request = Net::HTTP::Post.new(url.path)
request.body = (both_xml)
request.content_type = 'application/x-www-form-urlencoded'

http = Net::HTTP.new(url.host,443)
#http.set_debug_output $stderr
http.use_ssl = true
http.timeout = 30

@response = nil
http.start do
@response = http.request(request)
end

p @response.body

Wednesday, January 31, 2007

Another reason I dislike Microsoft

http://ajax.asp.net

Hooray, welcome to the Ajax world ASP.NET. It's been around long enough, someone bright in Redmond actually started the movement back in the mid 90's. But, Microsoft, could you not kill the term by branding your Ajax components as AjaxThisOrThat when it's really just a wrapper for DHTML functionality? It appears to me that you're trying to capitalize on the current wave by simply naming DHTML controls as Ajaxed controls. Which is simply wrong. Then again, I suppose that's no surprise considering your failed attempt at changing the meanings of things anyway.

Sadly, for the ASP.NET developers who may be new to it, they'll now be dumbed down (once again?) to the real concepts and essentials of what Ajax _really_ is and how/why it works.

Oh, and nice job on making it only work with web services and kudos to making configuration a breeze.

Monday, January 29, 2007

Quote model, has_many and a couple hours of grief

I have two models, Quote and QuoteLineItem using the has_many and belongs_to respectively. I was having issues with AR dumping out insert statements like

INSERT INTO quote_line_items (`created_on`, `quote_id`, `price`, `modified_on`, `product_id`, `quantity`, `modified_by_person_id`) VALUES(#<Quote:0x36551f0>,
#<Quote:0x364dba8>, #<Quote:0x3645728>, #<Quote:0x363efa4>, #<Quote:0x362ef3c>, #<Quote:0x3636728>, #<Quote:0x3624618>))


This drove me nuts so I jumped into rdebug to figure out the issue. Turns out there is an ActiveRecord method name collision when using

belongs_to :quote


so you have to provide an alternative, something like

belongs_to :related_quote, :class_name => "Quote"


Wish I knew that when I was creating this migration....

UPDATE:
http://dev.rubyonrails.org/ticket/4248

Tuesday, January 23, 2007

Spotlight and indexed content

UPDATE: Check it out: http://www.arcadianvisions.com/downloads/RubyImporter.html

Became apparent to me today as I was searching for some files that Spotlight only indexes content for files based on their extension (and plugin, if installed). I was able to confirm this with the following code:

echo "hello you hoser" > something.java
mdls something.java

There's a bunch of data there associated specifically to the .java extension. Find that file based on its contents:

mdfind "you hoser" |grep java

Aha, there's the file.
Trying this again with, say, a .rb extension:

echo "take off you nob" > somethingelse.rb
mdfind "take off you" | grep something

No results. mdls shows that there are some attributes associated with the .rb extension, but somehow the content isn't indexed.

XCode provides a project template called "Metadata Importer" so you can write your own to deal with custom extensions. Apple's doc explains the process.

Friday, January 12, 2007

quicksilver shortcut for locking the screen in OSX

I copied the ScreenSaverEngine.app from System/Library/Frameworks/Screensaver.framework/Versions/A/Resources/ScreenSaverEngine.app
to a cataloged QuickSilver directory. From there I just bound a key through the Triggers. Now I can lock the machine with ctl-option-l.

Monday, January 08, 2007

Suse 10.2 64-bit and VMWare Server

Need a 64-bit server so we can run our VMWare machines on it and utilize the 8+ gig of RAM. Originally tried CentOS but because VMWare Server is compiled against 32-bit libraries, it would be a nasty amount of work to install the needed libraries one-by-one (ldd spit out at least 20 libraries we needed). After digging around online it turns out that VMWare "officially" supports Suse but not much else. Time was of the essence and we bagged Gentoo for that reason.

The 10.1 install was pleasant, but the user experience was very unpleasant. I didn't actually install it, but a coworker did and it was only a matter of days until he migrated to Ubuntu. We were hoping that some of the usability issues were resolved in 10.2, happy to report that is the case. We installed the base system, developer libraries, kernel development and the 32-bit libraries during the initial package selection. After that, once the system was up and running, vmware installed without issue.

To use vnc (via ssh) we enabled Remote Login in Login Window Preferences (not sure why that's a System Application but not in the Control Center). The command to enable port forwarding for local vncviewer execution is "ssh -g -L localhost:5901:machine:5901 user@machine -X", then "vncviewer localhost::5901" will connect right up through that ssh tunnel (also, ssh with the -g enables anyone to use your tunnel).

Wednesday, December 13, 2006

Javascript dialog libraries

We explored various available libraries for managing dialogs within the application. We wanted an easy-to-implement solution that was intuitive to the user and didn't bog down the browser when rendering. We looked into the following solutions:


  • Yahoo UI: YUI is feature-rich, but you pay for it. In order to render dialogs there were (IIRC) 5 javascript files to include. It also appeared to be slower than the other solutions and was a serious amount of code for just opening a URL as a dialog. Wasn't too hard to ditch this after looking at the more simple solutions.
  • Lightbox gone wild: We liked this solution, but was pretty confined in terms of usage. You basically create links with a lightbox-specific class and then the initialization code for the page activates those links to function as lightboxes. No doubt this is easy, but it posed a problem with dynamic pages. We had some page that were returning links via AJAX, which would then require us to manually call the lightbox initialize() in order for those new links to behave properly. Wasn't worth the effort for the little (though nifty) functionality that lightbox provided.
  • jt_appdialogs: This was a very simple solution, but seemed slow (moving around the dialog) and not very friendly to fetching URLs (designed for rendering divs already in the page).
  • submodal: Another rather simple solution, but limited in functionality. Couldn't move the dialog around, though that's supposed to be doable. We also had issues in getting the right sizes.
  • prototype window class: Of all the solutions, this was the easiest to implement, provides a very intuitive user experience, is well-documented and simple to use.