tag:blogger.com,1999:blog-32739515264438565552024-03-13T19:13:36.082-07:00Practical DevelopmentA commentary on Software Development in the real world.AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.comBlogger18125tag:blogger.com,1999:blog-3273951526443856555.post-84998849605029523562011-02-13T16:05:00.000-08:002011-02-13T16:28:30.149-08:00Dynamic validation of input with prototypeContext: You're stuck with prototype and you have a client that wants to validate some input with javascript. My case was "if a value exists then validate that it doesn't change by more than 20% - we want it highlighted it when you tab out". Aparently server side validation wasn't wanted, just a visual prompt.<br /><br />Anyway - since my googlefu isn't up to scratch for mooching good javascript solutions that other people did I coded it myself (shocking!). Hopefully this helps someone. If not go switch to jQuery - seriously...<br /><br />Back on topic again, wn load this creates a handler for each input that you find with your selector. The handler is a closure that uses the input's current value for relative validation, ie if the value was 1.0 when the form loaded then it would add the 'warning' class to the input if it was more than 1.2 or less than 0.8 after it's changed.<br /><br />If you want more heavy handed validation (and you likely will) you can do harsher things like refusing to submit the form (good) or redirecting them to failblog (even gooder).<br /><br /><script src="https://gist.github.com/825330.js"> </script><br /><br />*What the hell is with Event.element(event) - if anyone knows a nicer way of doing this please let me know...AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0tag:blogger.com,1999:blog-3273951526443856555.post-20445981996813159582010-12-22T13:25:00.000-08:002010-12-22T13:32:31.975-08:00What to do when code breaks your migrations (Ruby/Git edition)Sometimes, unless you're working for a retarded startup, you'll go away for a week or so and come back to find a massive change merged in. It's likely that you have a long list of pending database migrations.<br /><br />It's also likely that the second migration got broken by a code commit that removed something it needs to work, so you can't run it. You need to figure out a quick way to roll back the code to a revision that works, run the migration, then revert back to the head commit. There's a few ways that git will help you here.<br /><br />1. (easiest) Use git bisect. Find a commit that lets you get past the migration and run it (usually the one that contains the migration works...) then cancel the bisect. Done, total time 2 mins.<br />2. (still easy) Use git reset to reset your repo to the state it was at the failing commit. After the migration runs pull again and you're dandy.<br />3. If you don't have good version control you're a bit screwed. Anonymize a prod database dump and reload your dev database. Or find exactly what code breaks you migrations and temporarily monkeypatch it to get past the problem. Doing this multiple times sucks so after doing this once refuse to work without decent version control. If your boss won't budge then quit and go work for someone that's not a moron.AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0tag:blogger.com,1999:blog-3273951526443856555.post-30457537948789923792010-12-22T12:41:00.000-08:002010-12-22T13:11:37.776-08:006 git lessons for git beginners from a git beginner1) Bookmark this: http://book.git-scm.com/ Just do it.<br /><br />2) Branching is awesome and mentally cheap right now, but can be confusing and cognitively expensive in 5 weeks time. Try to keep your commit history clean (eg. with rebase), your git log will love you for the decrease in merge commits.<br /><br />3) For god's sake do most of your work in a branch. Getting merge issues when you pull just before beer o'clock will ruin your weekend. Keep your master branch looking as much like origin/master as possible.<br /><br />4) Merge master into your branches often if you're planning to merge back later.<br /><br />5) Merging is shit no matter how you do it. Use a tool whenever possible (try lots) but learn to do it manually too, that way you'll never have a problem when your tool of choice can't handle gnarly 3 way merge issues.<br /><br />6) For the similar reasons to point 2, Learn to 'git pull --rebase'AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0tag:blogger.com,1999:blog-3273951526443856555.post-43165048057242930172010-09-02T18:53:00.000-07:002010-09-02T19:21:56.146-07:00ActiveRecord for Oracle won't update recordsI needed a script to fix up some date data in an Oracle database that didn't lend itself to using a pl/sql script. Naturally I turned to Ruby and ActiveRecord to sort out the issue. I drafted the script against a local MySQL DB and it worked flawlessly, but when run against the Oracle instance it wouldn't save any data changes.<br /><span style="font-weight: bold;"><br />Here's what happened, and how to fix it...</span><br /><br />So turns out this has happened before to other - see <a href="http://www.ruby-forum.com/topic/141851">this post on Ruby-Forum.com</a> for details. The OP didn't get any replies so I'm assuming that he figured it out on his own, unluckily for me he didn't share the fix so I had to do some leg work.<br /><br />For starters I'm running Ruby v1.8.7, activerecord v2.3.8 and activerecord-oracle_enhanced-adapter v1.3.0<br /><br />My schema is legacy with a primary key that isn't "id". Easy to sort out, I coded up something like this:<br /><pre class="brush: ruby"><br />class Receipt < ActiveRecord::Base<br /> set_primary_key "rt_receipt_id"<br />end<br /></pre><br />So onto the issue. This worked fine with MySQL but not Oracle so my first stop is to check out the SQL that's being created:<br /><pre class="brush: sql"><br />UPDATE "RECEIPT"<br />SET "RT_RECEIVED" = TO_DATE('2008-01-03 23:58:19','YYYY-MM-DD HH24:MI:SS')<br />WHERE "RT_RECEIPT_ID" = NULL<br /></pre><br /><span style="font-weight: bold;">NULL?</span><br /><br />On any update, AR tries to sql encode the value of the AR object's "id" value as the primary key. Calling the id accessor on my Receipt objects was returning nil, and AR was translating that to a sql NULL. There's a simple fix, alias id to our new primary key accessor:<br /><pre class="brush: ruby"><br />class Receipt < ActiveRecord::Base<br /> set_primary_key "rt_receipt_id"<br /> alias_attribute :id, :rt_receipt_id<br />end<br /></pre>AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0tag:blogger.com,1999:blog-3273951526443856555.post-16290705855190698602010-07-22T15:15:00.000-07:002010-07-22T15:37:34.430-07:00A couple of useful Ruby scripts<span style="font-weight: bold;">Ruby is my goto language for a lot of small scripting tasks, here's a couple of useful examples. Note that these scripts are good enough implementations and could be made easier to use by adding commandline arguments, I've just never had reason to use them regularly enough for that.</span><br /><br /><span style="font-weight:bold;">Gutenberg Crawler</span><br />Sometimes you need some real files with decent content for testing or whatever. Project Gutenberg is a great source of Public Domain books. Their <a href="http://www.gutenberg.org/wiki/Gutenberg:Information_About_Robot_Access_to_our_Pages">robot page</a> shows a number of ways to automate download of their files, however a quick and dirty (or effective, depends on your point of view) is to build the path to the text document itself. Exercise restraint - hammering their servers is rude, there are guidelines on the page linked earlier.<br /><br /><script src="http://gist.github.com/486694.js?file=gutenberg.rb"></script><br /><br /><br /><span style="font-weight:bold;">SVN Stripper</span><br />Git is friendly, it only adds one folder to your project. Subversion is invasive adding one to every single folder. Sometimes clients want source, for whatever reason this script strips out the .svn or _svn folders so you can unversion some subversioned source without folder hunting.<br /><br /><br /><script src="http://gist.github.com/335865.js?file=SVNCleaner.rb"></script>AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0tag:blogger.com,1999:blog-3273951526443856555.post-19673301615828413412010-07-22T14:55:00.000-07:002010-07-22T15:13:38.456-07:00The power of Rails isn't RailsMy team is primarily a Java team (we write servers and services) so when we have to do some web work it always ends up being with a Java web framework, some reasonable, some not so good.<br /><br />There are a lot of reasons to choose Java, it's stable, it's very well understood (by the people who care to dig into it's internals) and it's brilliant for long running processes like servers. What it's not good at, it seems, is web. Of all of the mainstream languages, Java seems to have the worst web frameworks, the worst view engines and the most over-thought data access conventions. If there's one saving grace in the enterprise Java landscape it's Spring, but even their best (Spring @MVC), while better or even the best in the Java landscape, it never quite feels like the right tool for the job.<br /><br />Given this, the Spring team created ROO, a project to add code generation to Spring MVC. While it may not be the case here, many other web frameworks looked at the Power of Rails project and asset generation and thought "YES! That's the secret sauce!", and it turns out they're wrong. Spring ROO generates a whole load of code, it manages your DAO layer, but even with all of that power, it's real world use case has by many been deemed<a href="http://java.dzone.com/news/spring-roo-impressions"> prototyping, not enterprise project ready</a>. I felt the same way when I tried to use it, easy to get running, hard to keep progressing. Turns out all of the code generation in the world isn't the secret sauce.<br /><br /><span style="font-weight: bold;">There are two factors that I believe make Rails a killer web framework. </span><br /><br />The first is The Community (yes, proper noun). The Rails lighthouse shows over <a href="https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets">800 open bug tickets</a>. I've seen people mock this fact (ROO in contrast <a href="http://jira.springframework.org/browse/ROO">appears to have 240</a>, and I couldn't find the ASP.net one...) but the nature of The Community is that bugs, when found, are reported, usually with a failing test and possibly a patch for good measure. The number of found bugs (along with the rate of commits on the Rails Github account) is actually a positive sign, it means that The Community as a whole is invested in the success of Rails, and is working together to improve it rather than letting it atrophy or ignoring the issues with it and working around them. This leads to a vibrant piece of software that is a joy to use, that while not perfect, won't settle for average.<br /><br />The second reason is that Rails, for multiple reasons, naturally lends itself towards a style of application that allows you to maintain the original "Wow I got that project running fast!" feeling (PM types: read "Project Velocity") for the entire duration of the Project. This seems to be why people who love agile seem to gravitate towards Rails, your burndown chart isn't logarithmic, it's flatter, more predictable, and more accurate (until the client changes their mind) than that old Gannt chart.<br /><br />For the same reason that excites customers and project managers is why developers love it, because we love adding features, getting a product past just enough to the wow stage. Working a lot with legacy code teaches you the value of testability, simplicity, readability, and other agile-ilities, so anything that promotes these things to the extent that Rails does or that lets us add value to a product at the same rate regardless of the age or size of a codebase is a good thing, for everyone.<br /><br />I believe that any framework garners all of it's power from the language that it sits on. A Java based web framework will be constrained by Java's inherit strengths and limitations. Likewise, a Ruby based web framework will live and die by Ruby's strengths and limitations.<br /><br />It seems for the web, that Ruby's strengths are paramount to it's success, and it's limitations can be lived with.AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0tag:blogger.com,1999:blog-3273951526443856555.post-45738422916176999432010-01-14T14:56:00.000-08:002010-01-14T18:10:37.421-08:00Scaffolds from existing tablesI find it hard to believe how often I see experienced, skilled and intelligent programmers who when faced with a tiny feature they would like to see in an OSS product resort to whining on forums or to colleagues instead of checking if there's already an existing solution <br /><br />Granted this doesn't seem to come up in the Ruby world as much as it does a few other language circles I'm involved in (I suspect it's because it's usually faster to write the Ruby code to do what you want than to log into a forum...), but there are a few of these "requests" that you see over and over again, even when a good solution already exists and is freely and openly available. In rails it tends to be the "I have an existing schema but I want scaffolding!" one.<br /><br />I had that thought trigger while dealing with a CRUD app I've been working on that deals with a large, legacy schema. It's not because I like scaffolding all that much (I don't) but because I wanted to get a working prototype up and running really fast and Ruby is much better at writing boilerplate code than I am.<br /><br />In an attempt to become more like the programmers I look up to I didn't whine, I didn't log into a tech forum or post to a google group (where they are undoubtedly already 500 emails requesting the same thing already) I just started coding. Here's the "simplest thing that might work", it uses eval, it's not altogether elegant, but it fits all of the requirements.<br /><br /><script src="http://gist.github.com/277683.js"></script><br /><br />We don't need to Google everything, there's nothing wrong with programmers that actually write code to solve problems...AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0tag:blogger.com,1999:blog-3273951526443856555.post-88581524575665380522009-11-23T15:40:00.000-08:002009-11-23T16:46:54.207-08:00Recursion in RubyI recently took part in the <a href="http://rubylearning.com/blog/2009/10/30/rpcfn-short-circuit-3/">RPCFN</a>. Being that this month's challenge was a Shortest Path type challenge and I've done enough discrete maths papers to know that the best graph traversal Algorithm for this is Dijkstra's least cost path one I went away and implemented it in a couple of hours. Many others did exactly the same thing, often much more elegantly that me. What struck me was that the markers were quite obviously looking for people to imagine up their own method of finding the shortest path, and had a massive bias towards recursive algorithms.<br /><br />Now the <a href="http://gist.github.com/228612">winning submission</a> is a very nice piece of code, it's concise, easy to read, simple, well tested, and it clearly lays out it's goals, to find a recursive solution to the problem with disregard of performance. It performed ok, I did wonder however how the recursion would impact the performance on larger problem sets.<br /><br />So I took Todd's code and refactored <a href="http://gist.github.com/237354">mine</a> to work with his data structures. I also changed up my code to match his module's interface and packaged both implementations into their own classes so that there wouldn't be clashes while trying to test them. (modules are for adding abilities to classes, not for providing whole feature sets, if you see yourself including a module into main, you're doing it wrong...)<br /><br /><a href="http://gist.github.com/241483">And here it is.</a><br /><br />I ran benchmarks on a low end computer (Pentium 2.8) against 3 graphs, one with 2 nodes and 2 edges, the RPCFN example with 7 nodes and 10 edges, and a larger, more complicated one of 7 nodes and 21 edges, all of this is in the code linked above. Here's the results over 10000 iterations:<br /><pre bash="">Simple Graph:<br />Iterative Approach took 1.54700second(s).<br />Recursive Approach took 0.95300second(s).<br />RPCFN Graph:<br />Iterative Approach took 6.37500second(s).<br />Recursive Approach took 20.62500second(s).<br />Large Graph:<br />Iterative Approach took 10.70300second(s).<br />Recursive Approach took 472.96200second(s).<br /></pre><br />The numbers speak for themselves, this approach doesn't scale at all well. In fact at first I thought I'd broken some of Todd's code and created an infinite loop of some sort. It's more than likely that the recursive function could be tweaked to get better performance, but short version is that if I were ever to need to implement graph traversal in Ruby for any production system, recursion would be the last approach I'd look at.<br /><br /><span style="font-size:130%;">Final word</span><br />Todd's decision to use a recursive solution in this case was totally justified, readability and simplicity were massive factors in this event. That said I believe that outside of the smaller, more contrived problem scopes, recursion brings a lot of headaches and scaling issues than the iterative approaches.AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0tag:blogger.com,1999:blog-3273951526443856555.post-53612096905125972512009-11-23T11:20:00.000-08:002009-11-23T12:00:49.945-08:00Exception Errno::ENOENT in PhusionPassengerAs Passenger fails so rarely there's not much documentation on the error messages, so I'm posting this for the benefit of googlers everwhere... Scroll to the bottom for the Cliffs notes.<br /><br />Passenger is a fantastic piece of software, I can't praise it highly enough. It's brought Ruby deployments from the complexity of reverse proxying to a mongrel cluster up to being on par of ease with PHP, a difficult and noteworthy achievement that can only increase the adoption of Rack based technologies in the future.<br /><br />That said, in very rare cases the Passenger spawner blows up in spectacular ways that the old Mongrel deployments never even dreamed of. When this happens all of your current and future Rack instances running on Passenger will respond to every request with a 500 error, your only course of action being a full Apache restart, and since it's so rare there's usually nothing that can be found on the net to fix it.<br /><br />Here's an example from one of my company's Sinatra services:<br /><pre class="brush: bash"><br />*** Exception Errno::ENOENT in PhusionPassenger::Rack::ApplicationSpawner (No such file or directory - /tmp/passenger.19250/backends/backend.Hoa3NPf9xWIsBy3D4sBZtTKE1DgjhTKjbSpdwSPWW6FakeTwddzh2hwteMo6qYecQC<br />zgV) (process 8942):<br /> from /opt/ruby-enterprise-1.8.6-20090610/lib/ruby/gems/1.8/gems/passenger-2.2.4/lib/phusion_passenger/abstract_request_handler.rb:279:in `initialize'<br /> from /opt/ruby-enterprise-1.8.6-20090610/lib/ruby/gems/1.8/gems/passenger-<br />...snip...<br />[ pid=725 file=ext/apache2/Hooks.cpp:688 time=2009-11-24 06:31:02.381 ]:<br />Unexpected error in mod_passenger: Cannot spawn application '/opt/number_service': The spawn server has exited unexpectedly.<br />Backtrace:<br /> in 'virtual boost::shared_ptr<passenger::application::session> Passenger::ApplicationPoolServer::Client::get(const Passenger::PoolOptions&)' (ApplicationPoolServer.h:471)<br /> in 'int Hooks::handleRequest(request_rec*)' (Hooks.cpp:485)<br /></passenger::application::session></pre><br />The root cause here is actually <a href="http://code.google.com/p/phusion-passenger/issues/detail?id=365">this</a> ticket - the /tmp/passenger.****/ folder will be almost empty because some temp watcher cleaned it out in a quiet period. The ticket has been closed but unless you feel like building from source you'll have to wait until the next release to fix the issue.<br /><br />Our fix was to move the Passenger temp directory out of /tmp in the Apache httpd.conf to stop temp watchers from nuking our deployments. Instructions on setting this up are found <a href="http://www.modrails.com/documentation/Users%20guide.html#_passengertempdir_lt_directory_gt">in the fantastic Passenger documentation</a>.AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com1tag:blogger.com,1999:blog-3273951526443856555.post-29312578025435314192009-09-16T13:36:00.000-07:002009-09-16T15:15:42.718-07:00Serving Rails sites using Passenger with SSLThis comes up a lot in the rails community, and it's not too difficult to do.<br /><br />This brief guide assumes that you have a working site hosted on a Linux server using Passenger (and also having installed mod_ssl for Apache) and that you want to secure the whole thing using SSL. If not, you'll need to add extra steps like deploying the project, installing the gems, running your migrations, installing mod_ssl and Passenger for Apache etc.<br /><br />We will generate our own (horrible...) temporary SSL cert assuming that later we will get a real one from a company like VeriSign and can swap out the temp one. Also, because the staging environment I've been deploying to runs on <a href="http://www.apachefriends.org/en/xampp.html">XAMPP</a> and we've still needed phpMyAdmin while developing the site, I'll throw in the "Serving a PHP folder from passenger" whirlwind tour (for a much better explanation, see <a href="http://www.abletech.co.nz/2009/06/serving-php-from-a-rails-app-with-passenger/trackback/">AbleTech's description</a>).<br /><br /><font style="font-weight: bold;" size="4">Firstly Passenger will want a log file it can write to.</font><br />You'll want your Passenger logs to come into a log file in your rails site's log folder (near the end of the post you'll see I've configured "<font style="color: rgb(51, 51, 51);" face="courier new">ErrorLog /opt/railssite/log/apache.log</font>" in my httpd.conf). Create one in your rails apps log directory and run "chmod 0666" against it so that Apache will have read/write rights. If you don't, Apache will write all of the Passenger logging to the standard error_log and you'll waste time trying to see why your site isn't working like you thought it should.<br /><br /><font style="font-weight: bold;" size="4">Second create your SSL key/crt files.</font><br />Create a folder to hold your ssl key/crt files. We're going to use openssl to generate our key and certs so if your flavor of Linux doesn't have it installed you'll need to use apt or yum or something to get it. Here's the brief rundown on how to generate a valid key/cert with openssl, with the password "password". You may need to do this as root.<br /><br />Run "<font style="color: rgb(51, 51, 51);" face="courier new">openssl genrsa -des3 -out your.domain.name.key 1024</font>"<br />When prompted enter the passphrase "<font style="color: rgb(51, 51, 51);" face="courier new">password</font>"<br />This generates the file your.domain.name.key. Because you want to get into good security habits early run "<font style="color: rgb(51, 51, 51);" face="courier new">chmod 400</font>" against this file, this makes sure that your key file is only editable by the root user.<br /><br />To generate the self signed cert from your key run "<font style="color: rgb(51, 51, 51);" face="courier new">openssl req -new -key your.domain.name.key -x509 -out your.domain.name.crt</font>"<br />This generates the file your.domain.name.crt which is the certificate that we're going to use to secure this site.<br /><font style="font-weight: bold;" size="4"><br />Thirdly configure Apaches httpd.conf.</font><br />We're going to forward all traffic that comes in on port 80 to port 443 by configuring a RewriteRule in a virtual host listening on port 80 to forward all requests to the virtual host listening on port 443. You don't have to do this but users are easy to frustrate. Since HTTP is roughly equivalent to HTTPS in the mind of the average end user while they're typing a URL, making both "just work" is easy and highly recommended.<br /><br />For the purposes of the folowing httpd.conf snippet, my SSL key/crt files were located in /opt/lampp/ssl and my rails site was in /opt/railssite<br /><pre class="brush: xml"><br /><VirtualHost *:80><br /> RewriteEngine On<br /> RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=permanent]<br /></VirtualHost><br /><br /><IfDefine SSL><br /> <VirtualHost *:443><br /> ServerName your.domain.name<br /> RailsEnv staging<br /> DocumentRoot /opt/railssite/public<br /> ErrorLog /opt/railssite/log/apache.log<br /> SSLEngine on<br /> SSLCertificateFile /opt/lampp/ssl/your.domain.name.crt<br /> SSLCertificateKeyFile /opt/lampp/ssl/your.domain.name.key<br /> SSLProtocol all -SSLv2<br /> SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown<br /> <br /> <Location /phpmyadmin><br /> PassengerEnabled off<br /> </Location><br /> </VirtualHost><br /></IfDefine><br /></pre><font style="font-weight: bold;" size="4"><br />A few notes on the above.</font><br />"<font style="color: rgb(51, 51, 51);" face="courier new">PassengerEnabled off</font>" in the location phpmyadmin ensures that when someone requests your.domain.name/phpmyadmin Passenger doesn't try to serve it up and PHP takes over. If Apache can't make the jump and find the directory you need it to serve up on it's own, add "Alias /phpmyadmin /dir/to/phpmyadmin" above the directory node to force it to look in the right physical location. This is particularly useful if you need to serve up something like a PHP blog alongside your rails application, and want to keep the source code for this in the rails project.<br /><br />Often you see people configure the rails document root in both virtualhosts, it's not necessary and depending on how your copy of Apache is running, it can cause Passenger to spin up a new rails instance just to have the user forwarded to the secured site. On a heavy usage site this could cause a lot of memory to be consumed for no reason.<br /><br />It goes without saying that the secured virtualhost (443) should be configured within an <font style="color: rgb(51, 51, 51);" face="courier new"><IfDefine SSL></font> node. This ensures that the site isn't served up unsecured if SSL fails for some reason.AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com2tag:blogger.com,1999:blog-3273951526443856555.post-55870648860886366412009-08-24T20:57:00.000-07:002009-08-24T21:28:54.045-07:00Drop downs selections for belongs_to in railsRails does a lot of work for you out of the box, and with it's form builders you can get it to do some things that make you never want to go back to .NET and databind a control ever again...<br /><br />Example: Recently I needed to bash together a quick admin screen with a couple of models. We have an organisation model that belongs_to :region and a Region model that has_many :organisations. This dictates a foreign key in organisation that points at a single region. In our generated Rails forms we enter that as a number, but it'd really be a nicer user experience to select the region from a drop down list right?<br /><br />Enter collection_select. This little helper generates a selection dropdown from a collection (not surprisingly). So if in my organisation_controller's edit method I return @regions = Region.all we can get instant rails gratification.<br /><br />Finally, since Edit and New use EXACTLY the same form, we're going to try be DRY and pull all the duplicated code out into a partial. I usually use the default form builder to base it on (you know, "form_for(@organisation) do |f|" ) and call "render :partial => f"<br /><br />When this renders it looks for the partial pointed at by f, which is a form. so _form.html.erb will be rendered. A final note, even though the partial here will be able to see @regions, I like to explicitly pass it in the locals hash, so I know where the heck it came from in 6 months time. If that's confusing you can just strip out that code and use the global set in the organisation controller. Here's the code:<br /><br />models/organisation.rb<br /><pre class="brush: ruby"><br />class Organisation < ActiveRecord::Base<br /><br />  belongs_to :region<br /><br />end<br /></pre><br /><br />models/region.rb<br /><pre class="brush: ruby"><br />class Region< ActiveRecord::Base<br /><br />  has_many :organisations<br /><br />end<br /></pre><br /><br />controllers/organisations_controller.rb<br /><pre class="brush: ruby"><br />class OrganisationsController < ApplicationController<br /><br />  # GET /organisations/new<br /><br />  # GET /organisations/new.xml<br /><br />  def new<br /><br />    @organisation = Organisation.new<br /><br />    @regions = Region.all<br /><br />    respond_to do |format|<br /><br />      format.html # new.html.erb<br /><br />      format.xml  { render :xml => @organisation }<br /><br />    end<br /><br />  end<br /><br />  # GET /organisations/1/edit<br /><br />  def edit<br /><br />    @organisation = Organisation.find(params[:id])<br /><br />    @regions = Region.all<br /><br />  end<br />end<br /></pre><br /><br />views/organisations/_form.html.erb<br /><pre class="brush: ruby"><br /><%= form.error_messages %><br /><br /><p><br /><br />  <%= form.label :org_name %><br /><br /><br />  <%= form.text_field :org_name %><br /><br /></p><br /><br /><p><br /><br />  <%= form.label :org_code %><br /><br /><br />  <%= form.text_field :org_code %><br /><br /></p><br /><br /><p><br /><br />  <%= form.label :provider %><br /><br /><br />  <%= form.text_field :provider %><br /><br /></p><br /><br /><p><br /><br />  <%= form.label :region_id %><br /><br /><br />  <%= collection_select(:organisation, :region_id, regions, :id, :full_name, {:prompt => false}) %><br /><br /></p><br /></pre><br /><br />views/organisations/edit.html.erb<br /><pre class="brush: ruby"><br /><% form_for(@organisation) do |f| %><br /><br />  #:region => regions exposes the regions array as a local variable in the partial<br /><br />  <%= render :partial => f, :locals => { :regions => @regions } %><br /><br /><p><br /><br />  <%= f.submit 'Update' %><br /><br /></p><br /><br /><% end %><br /></pre>AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com6tag:blogger.com,1999:blog-3273951526443856555.post-49196872292322285732009-08-17T19:20:00.000-07:002009-08-17T19:38:45.098-07:00SOAP4R clients with proxy and basic authenticationIf you go and google "soap4r proxy" you'll get a lot of helpful hits that show you how to set your http_proxy environment variable. Unfortunately there's not many (read none) that are devoted to configuring it programatically. So here it is. <br /><br />If you run the wsdl2r tool to generate code you'll end up with a defaultDriver.rb file. This defines the RPC driver that you can call the webservices against. You'll want to ensure that it's going to use the HTTPClient libraries. If you installed it manually you should be fine, but if you installed it with rubygems you'll need to add requires rubygems to the top of your file.<br /><br />Next you'll need to add the basic authentication configuration and the proxy configuration to the SOAP driver (using some horrendously poorly documented features of the SOAP4R library). The best place to do this is in your initialize method. You'll end up with a class that resembles the following (I generated mine from a WSDL pertaining to sending SMS messages):<br /><br /><pre class="brush: ruby"><br />require 'rubygems'<br />require 'xsd/qname'<br />require 'httpclient'<br />require 'soap/rpc/driver'<br /><br />class MessagingService < ::SOAP::RPC::Driver<br /> <br /> DefaultEndpointUrl = "https://a.secure.soap/endpoint"<br /> MappingRegistry = ::SOAP::Mapping::Registry.new<br /> <br /> Methods = [<br /> [ XSD::QName.new("https://somesoapgeneratedmappingname", "sendSMS"),<br /> "",<br /> "sendSMS",<br /> [ ["in", "to", ["::SOAP::SOAPString"]],<br /> ["in", "application", ["::SOAP::SOAPString"]],<br /> ["in", "message", ["::SOAP::SOAPString"]],<br /> ["in", "options", ["::SOAP::SOAPInt"]],<br /> ["retval", "sendSMSReturn", ["::SOAP::SOAPString"]] ],<br /> { :request_style => :rpc, :request_use => :encoded,<br /> :response_style => :rpc, :response_use => :encoded }<br /> ]<br /> ]<br /><br /> def initialize(endpoint_url = nil)<br /> endpoint_url ||= DefaultEndpointUrl<br /> super(endpoint_url, nil)<br /> self.mapping_registry = MappingRegistry<br /> init_methods<br /> self.options["protocol.http.basic_auth"] << [endpoint_url,'username','password']<br /> self.options["protocol.http.proxy"] = "http://yourproxyserver:8080/"<br /> end<br /><br />private<br /><br /> def init_methods<br /> Methods.each do |definitions|<br /> opt = definitions.last<br /> if opt[:request_style] == :document<br /> add_document_operation(*definitions)<br /> else<br /> add_rpc_operation(*definitions)<br /> qname = definitions[0]<br /> name = definitions[2]<br /> if qname.name != name and qname.name.capitalize == name.capitalize<br /> ::SOAP::Mapping.define_singleton_method(self, qname.name) do |*arg|<br /> __send__(name, *arg)<br /> end<br /> end<br /> end<br /> end<br /> end<br />end<br /></pre>AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com4tag:blogger.com,1999:blog-3273951526443856555.post-90538591616328214692009-07-16T16:50:00.000-07:002009-07-16T18:15:11.752-07:00Rails with a legacy Oracle DB<span style="font-size:180%;">It's a bit annoying...</span><br />It seems like every ROR tutorial assumes two things, firstly that you're writing your web application on Mac OSX with Textmate, and second that you're starting with a fresh database schema on MySQL. For a large percentage of people this is correct, but for the other half it's more than frustrating.<br /><br />I am currently investigating using rails to replace a rather old (think 6 years+) and difficult (JSP, the kind that looks like PHP gone bad) reporting site. The site shows various metrics to our business partners by analyzing SMS traffic sent through our gateway. I need to replace it with something simpler and more maintainable, Rails seemed to fit the bill, however there are some hurdles.<br /><ul><li>Firstly, Ruby likes UNIX. I program on a windows box and deploy to UNIX environments, there's nothing I can do to change that. Setting up a Rails environment on windows without resorting to the horribly outdated installers is a feat in itself, I'll cover that in a later post.<br /></li><br /><li>Secondly, my company likes Oracle. For the moment. It wouldn't be my first choice on a new project, but our software (with high availability requirements, somewhere in the 99.9% up time region) has been running smoothly on Oracle databases since I was in first year university. There's nothing like a good track record to make loyal followers of upper management.<br /></li><br /><li>Thirdly the schemas of the data I need to report on are sorely outdated. Everything is capitalized, there's no standard "id" column on any table, and there's a scattering of prefixes on half the columns. This makes for a slow start with ActiveRecord...<br /></li><br /></ul><span style="font-size:180%;">The Oracle to Rails "Stack"</span><br />Slow it may be but start we will. My "Secondly" was pretty easy to fix. Basically we need to plug 3 gaps to get ActiveRecord to talk to an Oracle Database, that can be summarised as: Your pc needs a connection to the Oracle Database Server, Ruby needs to see this connection, and ActiveRecord needs to see Ruby's connection. To make it a bit easier (maybe) here's a diagram, drawn in glorious MS Paint =><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-H1vxg-e5MM5oENYF7NJX5za8ykTsK32p-F5mhhHhQirnfeHdcnNxelBDb8Ky3SLnEjaRXobiLKdyB8ono4HTVPrsK4BRcTtOxlmX8xjJpFwFo_4-kviPZexJpNAX41CLkyAA8819UQ/s1600-h/OracleRailsDBStack.PNG"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 229px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-H1vxg-e5MM5oENYF7NJX5za8ykTsK32p-F5mhhHhQirnfeHdcnNxelBDb8Ky3SLnEjaRXobiLKdyB8ono4HTVPrsK4BRcTtOxlmX8xjJpFwFo_4-kviPZexJpNAX41CLkyAA8819UQ/s320/OracleRailsDBStack.PNG" alt="" id="BLOGGER_PHOTO_ID_5359218733200823074" border="0" /></a><br /><br />Oracle Client: Since I already do a lot of work on Oracle databases I didn't need a client, but if you don't already have one, go find an oracle client preferable a thin (hah!) one. Google Oracle XE if you want to have a local oracle database, it's Oracle's attempt at an express edition, however at over a gigabyte to install on windows it's not for the netbook developer types...<br /><br />Ruby-OCI8: go to your command line and get the Ruby-OCI8 gem via: gem install ruby-oci8 on windows you'll get the ruby-oci8-2.0.2-x86-mswin32 version. This gem lets Ruby talk to your Oracle client.<br /><br />Oracle-Enhanced: This gem is an ActiveRecord adapter. Get it by running gem install activerecord-oracle_enhanced-adapter<br /><br />These different parts will get you a path from ActiveRecord in Rails all the way to the Oracle database you need to work on, but there's a few things we need to tweak in our application's config to get it all the way, see <a href="http://wiki.github.com/rsim/oracle-enhanced/usage">here</a> for a lot of info that is much better than I can give.<br /><br /><span style="font-size:180%;">Schema woes...</span><br />Most things in rails "Just work", so long as you followed EVERY SINGLE CONVENTION. This is a bit much to ask of the developers that designed our legacy database 6 years ago. Luckily you can override and alias enough values to shoehorn your data into a valid ActiveRecord model, and once that (admittedly long and tedious) task is done you can develop like your database schema was never an issue.<br /><br />I'm going to cover a few of the more common issues by rebuilding a hypothetical (cough) table called ORGANISATION. ORGANISATION has the following columns: OR_ID, FK_RE_ID, OR_NAME, OR_CODE, FK_PMP_ID. As you can see it's kind of well structured, but definitely not "Rails Safe".<br /><br /><span style="font-weight: bold;">Issue 1: Rails expects database tables to be pluralised.</span><br />ActiveRecord offers a method set_table_name that takes a string. We'll add set_table_name "organisation" to our model.<br /><br /><span style="font-weight: bold;">Issue 2: Rails wants a column called </span><span style="font-weight: bold;">"id"</span><span style="font-weight: bold;"> for it's primary key.</span><br />ActiveRecord offers a method set_primary_key that takes a string. We'll add set_primary_key "OR_ID" to our model.<br /><br />OK we're half way there. If we were building from a scaffold command our views might work (maybe) but I can think of a few places where they'll break.<br /><br /><span style="font-weight: bold;">Issue 3: By ERB code is breaking, what the hell?</span><br />There's a few issues here, first is that if we use the scaffold command we'll probably get what we asked for. If we asked for upper case column names (as you'd expect) then your views will break when they try to extract the info in "organisation.FK_RE_ID". Oracle SQL is very lenient on the casing of it's table and column names, while something in Rails or our adapters hates uppercase. Use the lowercase "organisation.fk_re_id".<br /><br /><span style="font-weight: bold;">Issue 4: By ERB code is still breaking, what the hell?</span><br />Shortcuts... The scaffolded views will invariably at some stage try to call a method that takes the organisation and try to guess at it's. In the index.html.erb for example (we're scaffolding here) it will try to do something like: "<%= link_to 'Edit', edit_organisation_path(organisation) %>" since the organisation has no "id" column it will just send everything else. Poof, your code broke.<br />To fix it you need to alias your foreign key as "id" in your model, add alias_attribute :id, :or_id<br /><br /><span style="font-weight: bold;">Final niggling issue 5: I don't like all the unintuitive column names in my views</span>.<br />Since we've aliased OR_ID to id, we might as well do the same on all our columns. Just remember to alias to LOWERCASED versions of the column names, otherwise your views will throw errors again. Below is the code for the model I've described above, it works a treat.<br /><br /><pre class="brush: ruby"><br />class Organisation < ActiveRecord::Base<br /> set_table_name "organisation"<br /> set_primary_key "OR_ID" <br /> <br /> alias_attribute :id, :or_id<br /> alias_attribute :region_id, :fk_re_id<br /> alias_attribute :org_name, :or_name<br /> alias_attribute :org_code, :or_code<br /> alias_attribute :provider, :fk_pmp_id <br />end<br /></pre>AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com3tag:blogger.com,1999:blog-3273951526443856555.post-61390923515925590502009-06-25T14:06:00.001-07:002009-06-25T14:27:09.140-07:00Installing Ruby on Windows - it's all about the librariesRuby on windows is a bit lackluster, primarily due to the poor C compiler support making it hard to compile the source there, the documentation doesn't mention the extra libraries it requires and the fact that the One-Click-Installer is always several versions behind. <br /><br />The issue here is that Ruby doesn't give you any hints as to where to find the required libraries, or even that it uses them, so installing is a bit of a trial and error affair. The other issue is that a couple of the error messages are a bit cryptic (there's one that states "Ordinal 3873 could not be located in dynamic library... that means openssl isn't installed) making it hard to pinpoint the fix.<br /><br />So: The Cabin has a great tutorial (that I heartily recommend) for <a href="http://blog.orangecabin.com/2009/05/install-ruby-1-9-on-windows-using-zip-binary/">installing 1.9 on windows from the zipped binary distro</a>, I found it only after I'd done most of these steps myself.<br />For those of you who want the Cliffs Notes version, here it is:<br /><br /><ul>Download the latest Ruby binary distro from <a href="http://www.ruby-lang.org/en/downloads">http://www.ruby-lang.org/en/downloads</a>/</ul><br /><ul>Add the ruby/bin directory to your system path.</ul><br /><ul>Find zlib1.dll, libeay32.dll, libssl32.dll and readline5.dll, place them in your ruby/bin directory, they're libraries from the packages zlib, openssl and readline on <a href="http://gnuwin32.sourceforge.net">http://gnuwin32.sourceforge.net</a></ul><br /><ul>Rename zlib1.dll to zlib.dll, libssl32.dll to ssleay32.dll and readline5.dll to readline.dll</ul><br /><br />And that's it. Installing an up to date Ruby distribution in 4 easy steps.AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com3tag:blogger.com,1999:blog-3273951526443856555.post-52864230309222348162009-06-16T18:19:00.000-07:002009-06-16T18:46:50.956-07:00Using dsget and dsqueryBeing a developer in the support department in a medium-large organisation forces you to do some odd bits and pieces that would usually be performed by the system admins in a bigger company. So this week I was asked to do a bit of command line trickery using some Active Directory administration tools to extract some user information.<br /><br />The company I work for wanted mobile phone numbers extracted to files based on the email groups. For example I belong to a group, let's call it "Support" (how original), that is an email group in AD. Management wanted the name and mobile phone number of each person who's receives an email when it's sent to "Support" spit out to a file called "Support.csv" in the format "Username", "Mobile Number", to be used for updating an SMS application.<br /><br />I won't cover the conversion from the odd format that comes out of the tools to CSV, suffice to say it's easy with python, or ruby or perl, so here's the command:<br /><br /><span style="font-weight:bold;"><br />dsquery group ou="User Groups",dc=domainname,dc=net -name "Support" | dsget group -members -expand | dsget user -display -mobile -c > c:\Support.txt</span><br /><br />In order from left to right, this command runs dsquery to find the any group called Support in the User Groups organisational unit on the domainname.net domain. It then runs dsget against it to spit out the full list of member objects, the -expand makes it expand all of the groups below it. It finally runs dsget against all of these objects and if it's a user pipes the display name and number out to Support.txt. The -c is important, it ensures that any groups that come out of the "dsget group" call don't crash the "dsget user" call.AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0tag:blogger.com,1999:blog-3273951526443856555.post-8002897051255569132009-06-01T16:57:00.000-07:002009-06-01T17:37:10.433-07:00Simple SOAP calls over SSL with RubyI deal with web services a lot in my day to day, some good, some nightmarish. Having scripts and example code to deal with them makes my job a lot easier. My example last week was a piece of python code that implemented CONNECT to allow you to make SSL encrypted requests across a proxy in Python. I had to write this code because the company I work for exposes it's web services to clients via SSL, and being able to offer example code to leverage our services in multiple languages is a good thing. Not being able to offer example code in a pretty mainstream (in web terms) language is a bad thing.<br /><br />Therefore I wrote a similar script to last week's example that uses Ruby to post an XMP SOAP envelope request to an SSL secured site over a proxy. For enterprise use in apps expecting to make thousands of SOAP calls a day I would recommend users to build a full SOAP app using SOAP4R or some equivalent framework. However as this is often overkill for smaller apps that simply want to make a few calls to a web service, doing the request manually as a raw HTTP POST to send the SOAP envelope to the service is usually enough, and doesn't obscure the details of what SOAP really is, an HTTP POST request with a rigorously defined XML payload.<br /><br />As an aside, to build the SOAP envelope for this example I heartily recommend <a href="http://www.soapui.org/">SoapUI</a>, it allows you to open a WSDL for a particular service and easily generate the XML for each SOAP action. It's really good, especially if you build SOAP web services in your day to day job. It's so good, in fact, that as you get more and more used to using it you might find it replacing the test harnesses you inevitably build to test the web services you build.<br /><br />Back on topic, because we are building the XML as a string we need to inject any parameters for the SOAP call <span style="font-weight:bold;">after</span> we have built it. Luckily Ruby lets us replace substrings in strings pretty easily with the .sub! method. I use it to template my SOAP requests with {1} and {2} etc... Also to avoid being stung with content length issues make sure you finalize your XML before creating the headers dictionary so that your Content-Lenght ('Content-Length'=> soap_data.length) is correct. If you don't, the next 2 hours will be wasted while you go round in circles with HTTP errors that don't make too much sense.<br /><br />Anyway: here's the code, you'll notice that it's a lot shorter that the Python code from last week, this is because the standard library gives you native SSL over Proxy support. Note the call 'http_session.use_ssl = true'.<br /><br /><pre class="brush: ruby"><br />require 'net/https'<br />require 'open-uri'<br /><br /># Create the SOAP Envelope<br />soap_data = '''<br />This is where the SOAP xml would be drafted. I use {1} to template value fields.<br />'''<br /># normally I'd inject parameters into the SOAP Envelope using a call like:<br /># soap_data.sub!('{1}', "example string")<br /><br /># Set Headers<br />headers = {<br /> 'Content-type'=> 'text/xml; charset=utf-8',<br /> 'SOAPAction'=> '""',<br /> 'User-Agent'=> 'The useragent you wish to use, useful if you ever have to debug at the other end...',<br /> 'Host'=> 'www.securedurl.com',<br /> 'Content-Length'=> soap_data.length<br />}<br /><br />#create session object<br />uri = URI.parse("https://www.securedurl.com")<br />path = '/WebServiceHome/services/'<br />proxy = Net::HTTP::Proxy("aproxyserver",8080)<br />http_session = proxy.new(uri.host, uri.port)<br />http_session.use_ssl = true<br /><br />#start the http session<br />http_session.start { |http|<br /> # create the request<br /> req = Net::HTTP::Post.new(path)<br /> req.basic_auth mip_user, mip_password<br /> headers.each{|key, val| req.add_field(key, val)}<br /> # Post the request<br /> resp, data = http.request(req, soap_data)<br /> puts 'Code = ' + resp.code<br /> puts 'Message = ' + resp.message<br /> resp.each { |key, val| puts key + ' = ' + val }<br /> puts data<br />}<br /></pre>AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com3tag:blogger.com,1999:blog-3273951526443856555.post-55112920769987184642009-05-24T15:19:00.000-07:002009-06-01T17:28:07.939-07:00Python 3.0 SSL over ProxyWith the IT world the way it is now I'm certain the majority of enterprises put their users behind proxy servers, and I'm also certain that a lot of users behind said proxies need to access SSL secured sites programatically. With python touting itself as an enterprise worthy product and the batteries included philosophy of the core language libraries I'm surprised that there is no built in support for accessing SSL secured sites over a proxy. Admittedly there is a patch in the root bug report for this feature, however it's been around for years and it's only just getting to the implementation stage. Hopefully it's going to make it into 3.1.<br /><br />The python <a href="http://docs.python.org/3.0/howto/urllib2.html">HOW TO</a> documentation points to a<a href="http://code.activestate.com/recipes/456195/"> cookbook recipe</a> that handles this issue, but it's still only available in 2.x version. So since I had to implement this for a little code snippet here it is, SSL over proxy, the Python 3.0 version.<br /><pre class="brush: python"><br />import urllib, urllib.parse, ssl, http.client, socket<br />from urllib.request import Request, urlopen<br />from urllib.error import URLError, HTTPError<br /><br />class ProxyHTTPConnection(http.client.HTTPConnection):<br /> _ports = {'http' : 80, 'https' : 443}<br /> def request(self, method, url, body=None, headers={}):<br /> #request is called before connect, so can interpret url and get<br /> #real host/port to be used to make CONNECT request to proxy<br /> proto, rest = urllib.parse.splittype(url)<br /> if proto is None:<br /> raise ValueError("unknown URL type: %s" % url)<br /> #get host<br /> host, rest = urllib.parse.splithost(rest)<br /> #try to get port<br /> host, port = urllib.parse.splitport(host)<br /> #if port is not defined try to get from proto<br /> if port is None:<br /> try:<br /> port = self._ports[proto]<br /> except KeyError:<br /> raise ValueError("unknown protocol for: %s" % url)<br /> self._real_host = host<br /> self._real_port = port<br /> http.client.HTTPConnection.request(self, method, url, body, headers)<br /><br /> def connect(self):<br /> http.client.HTTPConnection.connect(self)<br /> #send proxy CONNECT request<br /> connect_string="CONNECT {0}:{1} HTTP/1.0\r\n\r\n".format(self._real_host, self._real_port)<br /> self.send(connect_string.encode('utf-8'))<br /> #expect a HTTP/1.0 200 Connection established<br /> response = self.response_class(self.sock, strict=self.strict, method=self._method)<br /> (version, code, message) = response._read_status()<br /> #probably here we can handle auth requests...<br /> if code != 200:<br /> #proxy returned and error, abort connection, and raise exception<br /> self.close()<br /> raise socket.error("Proxy connection failed: %d %s" % (code, message.strip()))<br /> #eat up header block from proxy....<br /> while True:<br /> #should not use directly fp probablu<br /> line = response.fp.readline()<br /> print(line)<br /> if line == b'\r\n': break<br /><br /><br />class ProxyHTTPSConnection(ProxyHTTPConnection):<br /> default_port = 443<br /> def __init__(self, host, timeout = 10, port = None, key_file = None, cert_file = None, strict = None):<br /> ProxyHTTPConnection.__init__(self, host, port)<br /> self.key_file = key_file<br /> self.cert_file = cert_file<br /><br /> def connect(self):<br /> ProxyHTTPConnection.connect(self)<br /> #make the sock ssl-aware<br /> self.sock = ssl.wrap_socket(self.sock, self.key_file, self.cert_file)<br /><br /><br />class ConnectHTTPHandler(urllib.request.HTTPHandler):<br /> def do_open(self, http_class, req):<br /> return urllib.request.HTTPHandler.do_open(self, ProxyHTTPConnection, req)<br /><br />class ConnectHTTPSHandler(urllib.request.HTTPSHandler):<br /><br /> def do_open(self, http_class, req):<br /> return urllib.request.HTTPSHandler.do_open(self, ProxyHTTPSConnection, req)<br /><br /><br />if __name__ == '__main__':<br /> import sys<br /> # build Proxy handler<br /> proxies = {'http': 'http://aproxyserver:8080/', 'https': 'http://aproxyserver:8080/'}<br /> proxy_handler = urllib.request.ProxyHandler(proxies)<br /> # build basic authentication handler<br /> password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()<br /> password_mgr.add_password(None, 'https://www.securedurl.com/', 'username', 'password')<br /> auth_handler = urllib.request.HTTPBasicAuthHandler(password_mgr)<br /> # create "opener" (OpenerDirector instance)<br /> opener = urllib.request.build_opener(ConnectHTTPHandler, ConnectHTTPSHandler, proxy_handler, auth_handler)<br /> urllib.request.install_opener(opener)<br /> request_url = "https://www.securedurl.com/default.html"<br /> req = Request(request_url)<br /> try:<br /> response = urlopen(req)<br /> print(response.read())<br /> except URLError as e:<br /> print(e.headers)<br /> print (e.code) <br /></pre>AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com3tag:blogger.com,1999:blog-3273951526443856555.post-11278987249526733912009-05-24T14:47:00.001-07:002009-05-24T14:52:26.503-07:00Practical DevelopmentSoftware Development is part art, part science. There are parts of it that require a touch of flair and ingenuity and parts of it that require brute force and "the simplest idea that might work".<br /><br />This blog is simply a place for me to document my simple ideas, my philosophies towards an artform and science that I love, and to nut out the latest (usually mundane) challenge that is bugging me.<br /><br />Enjoy.AJhttp://www.blogger.com/profile/00111353619109299390noreply@blogger.com0