Shes Geeky (un)conference announcement

August 27th, 2007 by comment desi

She’s Geeky is a women’s tech (un)conference being held Mountain View CA. It sounds really cool and while I am thinking I probably won’t be able to attend I think it is going to be a great event. You should check out their site and register. She’s Geeky

Also they are looking for sponsors and contacts from other groups like DevChix so email Laura, Kaliya, and Deb over at shesgeeky at gmail dot com if you are interested in that.

Cheers

ˆ Back to top

An Attachment Walked Into A Bar. Was That U, Fu?

August 22nd, 2007 by comment sarah g

I recently spent some time working on a Rails application that needed to have various kinds of attachments, such as PDFs and images, for various types of resources, such as Programs and Questions. The app in question lets you create programs and questionnaires for the purpose of granting continuing medical education (CME) credits to doctors — so they’re apprised of the bleeding edge techniques for sawing off your arm or tying your tooth to a doorknob. Not that this matters for the piece, but context — like sugar or salt — makes things go down easier.

If you can picture an office of harried administrators dealing with ever-changing AMA requirements — “This brochure needs to go with this program!”, “This xray picture needs to go with Question 7″, “This program needs to sing ‘Missing You’ while showing you diagrams of a root canal”.. (OK, maybe not the second item), you’ll see why I wanted to build a system that could expand easily. I did not want to find myself in the position of trying to look tough while shamefacedly muttering, “I’m sorry, PDFs can only go with *Programs*, as you initially specified” and watching these not-so-gentle administrative souls stare at me with fully warranted incredulity. Blaming the client is like [fill-in-the-blank]: weirdly fun for about 10 minutes before the hangover sets in.

This project turned into a tour of attachment_fu, single table inheritance, polymorphism and functional testing of uploads. Thanks to input from friends, blogs and the usual Internet suspects, I got it up and running in its first form and decided to write it up. Hopefully what I’ve learned can help someone else. “Link-outs”, the reclusive programmer’s form of the “Shout Out” (aka a list of great resources), can be found at the bottom of the article.

Goal

To implement a system that would allow for many types of files to be uploaded and attached to many types of resources.

Requirements

  1. Many types of assets (image, pdf, etc.)
  2. Assets belong to many types of resources (program, question, etc)
  3. Assets upload to the file system
  4. Assets validate in the context of their parent resource [you upload the pdf in the program form; the image in the question form, etc]
  5. All functional tests pass for uploads

Design

Rather than have database tables for each attachment type (PDFs and images, then eventually MP3s) I made one table called Assets. Listening to my requirements, a friend suggested setting up my Assets model using polymorphism and single table inheritance and that I look into attachment_fu for the uploads. For this exercise I will only be focusing on the PDF upload. Readers can extrapolate from there for image or other media types. This article does not address image resizing, or anything related to uploading images. There are some great links at the bottom that deal with these topics in depth.

Modeling the domain:

Assets (the table) has a field called type that is be [PDF, Image (etc)]; a field called resource_type which is the name of the class it belongs to, [Program, Question, etc.] and resource_id which is the id of the resource it belongs to.

The Asset class will inherit from ActiveRecord. PDF and Image (and any future file types we add to the system) inherit from Asset. Line items from Assets (the table) will look like this (leaving out the other fields for now)

type   | resource_type | resource_id

Pdf      | Program         | 82
Image  | Question         | 79

For my assets, I created the following models:
asset.rb, image.rb and pdf.rb. The latter two inherit from Asset.

# [Asset.rb]
class Asset < ActiveRecord::Base
  belongs_to :resource, :polymorphic=>true
end

# [Pdf.rb]
class Pdf < Asset
  belongs_to :program
  has_attachment  :content_type=>'application/pdf',
                         :storage=>:file_system,
                         :path_prefix=>'public/uploads/pdfs/',
                         :size => 1.megabyte..3.megabytes
  validates_presence_of   :content_type, :filename

   def validate
     if filename &&  /pdf$/.match(filename).nil?
         errors.add(:filename, "must be a PDF ")
     end
  end

end

Because I am using attachment_fu, I need to have a set of basic fields in my table that will handle image and file uploads. The migration I created looks like this:


t.column :parent_id,  :integer     # for the thumbnails of resized images, required for plugin
t.column :type, :string               # for STI on this table (curent types are image, PDF)
t.column :resource_type, :string  # for polymorphism on this table (current types are Program, Question)
t.column :resource_id, :integer    # for polymorphism, (program_id, question_id, etc)
t.column :content_type, :string    # Fields here [content_type] and below all attachment_fu requirements
t.column :filename, :string
t.column :thumbnail, :string
t.column :size, :integer
t.column :width, :integer
t.column :height, :integer

To create the relationship for my parent models (question and program) I set up the polymorphic relationship like so:

# [program.rb]
class Program < ActiveRecord::Base
   has_many    :pdfs, :as=>:resource,  :dependent => :destroy
end

# [question.rb]
class Question < ActiveRecord::Base
  has_many      :images, :as=>:resource, :dependent => :destroy
end

Note the difference between delete and destroy. Destroy will remove the entry from the database and the file from the filesystem. Delete will only delete the database entry.

Once this is set up, you can test it in the console and see the magic of Rails at work. Through the column names and relationships we've built, entries in Assets are automatically created. It's one of the things that's great about Rails. It doesn't simply describe different modeling patterns: it implements them if you set up your code correctly. It's a beautiful thing.

Uploading

Without moving files to the server, the system is not complete. So, onto attachment_fu. The attachment_fu tutorials I found online all made the assumption that the attachment you are uploading is an entity of its own, in its own form: a mugshot, for instance. My case was different: I needed to embed the attachment in the forms for their parent resources (pdf in the program form; image in the question form, etc) rather than independently. So I needed to validate a form for more than one model. In my _form.rhtml partial for program I have the pdf field like so:

# [views/program/_form.rhtml]

< label for="program_notes" >Notes
<%= f.text_area :notes %> # ... more fields ... < label for="program_status" >Add Brochure? [PDF Files only]< /label > <%= file_field("pdf", "uploaded_data") %>

This means that both a pdf object and a program object are being passed back to the program controller. Since we know that a program has_many pdfs, we can use program.pdfs.build in order to create -- then validate and ultimately save -- pdf objects.

Validating in context of parent resource:

I needed to check for errors with my program fields and my pdf field. This snippet is what I ended up with. The second line, validate_and_build_pdf is just a result of refactoring: it allows the validation methods to be run if you select a file through the upload file field (so if you try to upload a gif, the validation in Pdf.rb will squak) -- but not if you just leave that field empty.

#[programs_controller.rb]
def create
  @program = Program.new(params[:program])
   validate_and_build_pdf(params[:pdf],params[:pdf][:uploaded_data])
   if @program.save
      flash[:notice] = 'Program was successfully created.'
      redirect_to :action => 'list' and return
   else
      render :action => 'new'
   end
end

# this method stops program from saving the asset if the field is blank,
# and returns type-errors if relevant (files not PDF)
def validate_and_build_pdf(fileParams,file)
  if Asset.is_valid_file?(file)
     @pdf = @program.pdfs.build(fileParams)
  end
end

The reason for the Asset.is_valid_file? method was the same: if no file was uploaded an entry was still being made in the assets table due to the polymorphic relationship. So I had to make sure I was dealing with an actual fileobject, not an empty field. Since this would be used across the application for all types of file uploads, I made this a class method of Asset.

[Asset.rb]
# Checks that the object is one of the following types before running validation on it
def self.is_valid_file?(fileObj)
  if fileObj.is_a?(Tempfile)|| fileObj.is_a?(StringIO)
     || (defined?(ActionController::TestUploadedFile) && fileObj.instance_of?(ActionController::TestUploadedFile))
     true
  else
     false
  end
end

You may be wondering what's up with the ActionController::TestUploadedFile condition -- it's because when you run functional tests of uploaded assets, that is what they are -- they are not instances of fileObj.

Once it's determined that you're dealing with a valid file and you hit custom validation methods (such as informing the user that the file needs to be a pdf), you can include them with your program error messages so you only get one messages box at the top of your form, not two:

<%= error_messages_for :program, :pdf %>

Pass Functional Tests

This part is where you test all your uploads from your functional tests. To test uploaded files, you need to put files in your fixtures/files directory then build your tests.

# [programs_controller_test.rb]
def test_create_with_pdf
  fdata = fixture_file_upload('/files/semi.pdf', 'application/pdf')
  num_programs = Program.count
  post :create,   :multipart => true,
  :pdf=>{"uploaded_data"=>fdata},
  :program => {:name=>"Newest Created Program",
                    :is_ongoing=>1,
                    :status=>"Edit"}
  assert_response :redirect
  assert_redirected_to :action => 'list'
  assert_equal num_programs + 1, Program.count
end

I had a gotcha here -- I realized that I has to have the pdf array separate from the program array (since in the form it’s field_for pdf). I fixed it by submitting two objects, program and pdf, to the controller, seen above.

Finally, running the functional testing leaves files on the file system and they should be deleted at the end of all tests. So I added a destroy method in the teardown, which removes them.

def teardown
  Pdf.find(:all).each { |p| p.destroy }
end

Failing Upload Tests
Just to check that only Pdfs can be uploaded for programs, I added a handful of other file types to my directory and created an array of invalid file types in my setup:

# [programs_controller_test.rb]
def setup
 # other stuff....
 @invalid_files = [['lipsum.doc',"application/doc"],['lipsum.rtf',"application/rtf"],
             ['lipsum.txt',"text/plain"],['squiggle.gif','image/gif' ],
             ['squiggle.jpg','image/jpg' ], ['squiggle.png','image/png' ],
             ['squiggle.psd',"image/x-photoshop"],['squiggle.zip', 'application/zip']
             ]
end

and created a test to check these uploads will fail:

  def test_create_with_invalid_file_types
    @invalid_files.each do |name, type|
      fdata = fixture_file_upload('/files/' + name, type)
      post :create,   :multipart => true,
                      :pdf=>{"uploaded_data"=>fdata},
                      :program => {:name=>"Newerest Created Program",
                             :is_ongoing=>1,
                             :status=>"Edit",
                             :start_date=>Time.now.tomorrow,
                             :end_date=>''}
      assert_response 200
    end
  end

This gave me peace of mind that my files are being uploaded, only the right types are accepted, and all is good in the land of PDFs and Programs. And voila, we are done and on to the next task.


Link Outs

Here's a listing of various online resources on the topics covered in this post. This should be enough for a good start. Happy Uploading!

ˆ Back to top

RESTful Thoughts on a Web 2.0 Python Project

August 16th, 2007 by comment gloriajw

In September of last year, I was contracted by an independent investor to do the server side implementation of a Web 2.0 application from the ground up. It was an exciting new project, one which kept me up late at night through many nights, doing design and development. It had been a very long time since I had worked on a project that I found so exciting that it replaced my need for sleep. It was a geeks’ dream come true. It was great fun.

My first step was to get to know the front end Dojo guru who was working on the user interface. We were separated by thousands of miles and many time zones. We had never met in person (and still have not to this day). We were going to be spending long and occasionally stressful nights together in virtual space, so we spent time chatting to get to know each other. After the first month, we could both almost intuit when the other was tired, frustrated, or both. After a short time we worked so well together that communication was fast and efficient, and the work was not hindered by our physical distance. There were occasions where it would have been great to draw together on a real whiteboard. But we compensated for this loss fairly well. It was the first entirely virtual project I had worked on, and the social aspects of how we worked together were just as interesting as the technical aspects of the project.

We spent time evaluating tools which would achieve our goals of rapid design and development, and excellent performance both in the browser and on the server. For the server development I chose CherryPy and SqlAlchemy, using Postgresql. For the front end we chose Dojo and Javascript, using JSON to communicate between the browser and the server.

I started with the database schemas in Postgresql, then the wrappers which automatically generate one database per set of client data. The basics on how I do this in Postgresql are here:

http://www.devchix.com/2007/05/27/dynamic-web-content-what-you-can-do-in-postgresql-that-you-cannot-do-in-oracle/

This model of one database per data set per client worked so well with the client’s particular data that it was a natural fit. Scripting dynamic archiving and recovery took under one day in Python and Psycopg, and worked like a charm.

Here is a peek at how Postgresql can be used in CherryPy (or any other web framework, for that matter) to snapshot to backup tables, and subsequently to disk:

         def take_snapshot(self):
                try:
                        '''
                        All timestamps are UTC.
                        '''
                        snaptime = datetime.datetime.now()
                        snaptime = pytz.timezone(config.server_timezone).localize(snaptime)
                        snaptime = snaptime.astimezone(pytz.timezone('UTC'))
                        snaptime_str = snaptime.strftime("%Y%m%d_%H%M%S_%Z")

                        db = self.login.open_session_db()
                        #query = 'select datname from pg_database'
                        query = "select tablename from pg_tables where tablename not like 'pg_%' and tablename not like 'sql_%' and tablename not like '%_utc';"
                        our_tables = db.queryAndFetchAll(query)
                        mod = ""
                        for table in our_tables:
                                table = table[0]
                                '''
                                This table grows forever. Never recover it
                                from snapshot.
                                '''
                                if table in ('user_change'):
                                        continue

                                mod += "create table %s_%s as (select * from %s);\n" % (table,snaptime_str,table)
                        print "Taking snapshot: %s" % mod
                        db.executeMod(mod)

                except:
                        raise
                        db.close()
                        ft=open(TemplatePath + '/login')
                        showPage=ft.read()
                        ft.close()
                        showPage = "<P> Invalid User Name or Password.</P>" + showPage
                        return showPage

                db.close()
                return snaptime_str

        take_snapshot.exposed = True

Small snippets on the key concepts of flushing/retrieving snapshots from/to disk:

         def archive_to_disk(self,snaptime_str):
                        [...]
                        for archive_table in archive_tables:
                                archive_table = archive_table[0]

                                shmod += "mkdir -p %s; pg_dump -U tp -t %s -d -f %s/%s %s\n" % (archive_dir,archive_table,archive_dir,archive_table,dbname)
                                mod += "drop table %s;\n" % archive_table

                        print "Archiving old snapshot tables to disk: %s" % shmod
                        os.popen(shmod)
                        [...]

        def revert_to_snapshot(self,snaptime_str):
                        [...]
                        for revert_table in revert_tables:
                                '''
                                Move current tables to current snapshots.
                                Copy older snapshot tables to current tables.
                                '''
                                revert_table = revert_table[0]
                                table = re.sub('_%s' % snaptime_str,'',revert_table)
                                mod += "alter table %s rename to %s_%s; create table %s as (select * from %s);\n" % (table,table,curr_snaptime_str,table,revert_table)

                        print "Taking snapshot, and reverting to old snapshot: %s" % mod
                        db.executeMod(mod)
                        [...]

Why spend so much Web 2.0 article space talking about database concepts and client data handling? Because while designing Web 2.0 applications, all matters involving client data take up somewhere between one-third to 50% of the development time and energy. Client data is the very reason the application exists in the first place. The more time and energy placed into making sure it is kept, stored and manipulated in the best possible way for the application, means less headaches and refactoring as the product matures.

Once the database schema and wrappers were in place (much of those details were mercifully left out of this article), the business logic was next. When designing a RESTful application, a great bit of thought goes into the design of the state transactions between the browser and server. For our needs, passing massive XML structures back and forth to communicate state seemed like overkill. Passing JSON dictionaries with well defined fields back and forth seemed like a better solution, for speed and ease of customization.

Once the ‘how’ part is resolved, the next question is what will be passed back and forth between browser and server. In a REST implementation, the goal is to pass a complete transaction from browser to server, and a complete transaction from server back to browser. The definition of a transaction will vary based on the application. For RSS feeds, a transaction is only one-way, where the server constructs and sends a complete XML document to an RSS feed reader. This is probably the simplest REST implementation in existence right now. I’ll be addressing a more complex transaction model, where client and server pass each other data, and either side can manipulate the data and pass it back to the other side.

To achieve a RESTful state transaction model, in a nutshell, means passing enough data back and forth so that a user can click on any feature or function of the application, in any order, and the server will (1) know what to do every time without fail, and (2) will not ‘remember’ each client’s previous states. This implies that the server is ‘stateless’ with respect to specific client data. The server has a state of it’s own, and knows it’s own transaction state, of course. But it is only ‘aware’ of each client’s state when it is contacted by the individual clients, and the client notifies the server of it’s state.

It helps tremendously to work out the ‘chatter’ between the client and the server in human language before designing it. Here is an example of RESTful chatter between a client inventory application, and it’s corresponding server application:

server’s current state: “My current state is fifty boxes of paper clips in inventory.”

client one: “Hi server. Last time I contacted you, you had seventy boxes of paper clips in inventory. I am placing an order for sixty. I will accept a smaller quantity if you have >= forty in inventory. Bye!”

client two: “Hi server. I need twenty boxes of paper clips. I have no clue how many you had in inventory last time I contacted you, and I don’t really care. Fulfill this exact order or cancel it, no exceptions. Smell ya later.”

server: “Received client two’s request. Hi client two, you are properly authenticated, so I’ll look at your state. You want exactly twenty boxes of paper clips. You will make no exceptions. I currently have fifty boxes. I don’t care how many you have, I only care about how many you need. Your order is fulfilled, and I have thirty left. Bye.”

server: “Received client one’s request. Hi client one, you are properly authenticated, so I’ll look at your state. You want sixty boxes of paper clips. You will settle for a minimum of forty. My current state is only thirty in inventory. Your order is not fulfilled, and I have thirty left. Bye.”

Compare this to non-RESTful chatter between client and server:


client two:”Hi server.”

server: “Hi client two. You are authenticated, so I’ll continue to talk to you.” (server stores the state of talking to client two, properly authenticated.)

client one:”Hi server.”

server :”Hi client one. You are authenticated, so I’ll continue to talk to you.” (server stores the state of talking to client one, properly authenticated.)

client one:*BOOM* (crashed, blue screen of death, user restarts session once machine reboots)

client two: “server, hook me up with twenty boxes of paper clips.”

client two: *WHOMP* (browser crashes, user restarts browser)

server: “done, server two….hey wait, I can’t respond to you. How strange.”

client one: “Hi server.”

server: “Client one, you just authenticated, and you’re trying to authenticate again? I have to reject your request. Bye.”

client two: “Hi server.”

server: “client two: you have an outstanding transaction, but now your session ID is different. Are you trying to trick me? Get out of here. Bye.”

client two: “Huh? I just loaded, and have no idea what you’re talking about.”

server: “You are in a messed up state. Your authentication is rejected. Call customer support at 1-800-….”
client two: “????”

The point of the chatter is to ensure that the client is passing a complete transaction with every request, and that the server passes a complete transaction with every response. Similar to the CRC cards for OO design, the Chatter ensures that you have your states, transactions and interfaces well defined.

Note that a crash of the server in a REST implementation means that the client can’t reach the server with it’s complete transaction, so it waits and accumulates transactions, sending bulk transactions when the server is available once again. The crash of a client in a REST implementation means that if the server cannot acknowledge the client’s transaction, it can either drop it, knowing it will arrive again, or process it and ignore the duplicate transaction that may arrive again when the client is back up. There are no “partial states” which need to be resolved, the transaction was either accepted and completed, or rejected. Recovery in a REST implementation becomes trivial because of this simplicity, and this saves a tremendous amount of development time and energy.

Once your chatter seems complete, it can be translated into a header and data components. For this application, this means that the JSON structure coming from the client to the server has fixed fields with expected values, and data arrays/dictionaries with predefined formats.

The server receives the JSON data, decodes it into Python dictionaries and arrays, and first evaluates the ‘header’, as defined by the developers. The header tells the server the client’s current state, as well as the server’s last known state when the client last contacted the server. (For our application, this level of detail was necessary. Not all applications would need to know about the server state, and this needs to be decided while devising the chatter.)

Once the client’s full state is known, the client’s data can be processed against the server’s current state data.
The server constructs a reply header and data, as defined by the developers, comprised of expected fields and proposed values. In the case of inventory, let’s say client two needed to be notified of client one’s transaction, because these are franchise stores, and all inventory data is shared between them. The returned data set from server to client one would be the acknowledgment header (processed_order=False) plus the data received from other clients (other_orders_since_you_last_contacted_me=client two placed this order at that date and time).

CherryPy turned out to be an excellent framework for this task. It’s thin, fast, threaded architecture, and clean session handling made REST implementation quite easy. The same methods are called by every client, to contact the server and pass across it’s data. The same method is called by the server threads to respond to every client. The API between client and server is reduced down to about five methods. Granted, the object which checks the client’s current state against the server’s current state is quite large. But the transaction recovery code is quite small, and the objects are extensible, to handle new transaction types.

This article assumes the model of user authentication separate from transactions. The subject of cookieless authentication won’t be addressed here, but it is an option to consider, based on the application requirements.

This article also only addressed a Python implementation. Rails developers have a bit of an advantage, having a built-in REST methodology. I hope some articles to come will address Rails implementations.

ˆ Back to top

Ruby East Registration Open

August 16th, 2007 by comment desi

Ruby East registration is open. It is a one day Philadelphia based Ruby on Rails Conference that is being held 9/28. They have limited space so if you are planning to go you should probably go ahead and register. I am pretty happy to say that there will be 3 DevChix members presenting at the conference. I think it will be a great day.

ˆ Back to top

Book Review: Pro Drupal Development

August 12th, 2007 by comment Nola

Book Site | Sample Chapter: The Theme System | Table of Contents

Many of you are aware of my current total infatuation with Ruby, and that I’ve used PHP for about 6 years and at one point decided I hated PHP…until, I needed it for a quick one-off page and then realized that PHP had its place. Then again, I was totally frustrated with Ruby when making my moms bakery site and then turned to Drupal and Gallery (another fine PHP project), which saved my bacon and I got a website and photo gallery up in a weekend. So, PHP and I have had our moments but I’m not abandoning it!

Drupal powers some big sites, its not just for joe smoe’s blog. This is an interesting page about Is Drupal Right For You? and if you are wondering if its something that would even work for you.

I was excited to get my hands on a review copy of Pro Drupal Development. Its no secret that coders hate documentation and Drupal has one of the most complete online documentation I’ve seen for an Open Source project, but its almost too hard to find what you need amongst so much. The Pro Drupal Book is a godsend for the drupal programmer, new and experienced alike. I wish it was written a year ago!

The book starts off with a quick overview of how Drupal is structured and defines terms such as hooks, node and blocks in just 10 pages. Chapter 2 is a A step-by-step tutorial with making a module. That is a great idea to start off quickly writing code. It get the reader involved and hands on. I really tire of books that have to start off with the history of the internet, html and how things have evolved. Get to the code dangit!! Kudos to the Authors for that! Chapter 3 gets into module specific settings, like how to get your module to show up on the admin page and storing user settings that your module needs.

After you’ve had some experience with the code then the book goes into details on the specific parts of Drupal:

  • Menu System
  • Databases
  • Users
  • Nodes
  • Themes
  • Blocks
  • Form API
  • Filter System
  • Searching and Indexing
  • Files
  • Taxonomy
  • Caching
  • Sessions
  • JQuery
  • Localization
  • Using XML-RPC

Drupal is a pretty amazing framework, when I read the code I say “why didn’t I think of that?” … the module and hook system is genius.

Then some more general topics:

  • Writing Secure code
  • Development Best Practices
  • Optimizing Drupal
  • Installation Profiles

One of the chapters I skipped ahead to read was The Form API. In my years of PHP I’ve often tried to come up with a framework for doing forms and I wanted to see how they did it. This chapter follows a tutorial style as well. The Form API allows you to define fields, their label, their value, description. Some frameworks take the template approach, where you hammer out your HTML. Some are more configuration based like Drupal making a multi-dim array with keys and values. I can see advantages to both. There is a hook function for validation which allows you to write your validation checks.

PHP gets a bad wrap for security, partly because its pretty easy to learn PHP and newbies don’t always realize what they are doing. There is a chapter devoted to security and includes even some things I didn’t know about — encoding mail headers. The Form API is very secure,  one thing it does is check values that come from dropdowns were actually in the options and it wasn’t something that the hacker made up.

Developer Best Practices are great for the new developer, it talks about using cvs, tags, branches. It talks about how to create and apply patches (hint – you can contribute back to drupal). That is awesome. Alot of open source projects are like “HELP us, submit patches!” and the new user is left with uhhhhhh..how?

Caching is another interesting chapter. You will learn  how caching works and how Drupal Core uses it. There is a Cache API that has methods for module creators to make their modules faster.

JQuery … I am not sure if I like it or not, but its part of Drupal 5! I skipped ahead to this chapter to see what its all about. There is a javascript hook built into Drupal making it easy to add, thats pretty cool.

One thing I found lacking in the book is anything about Testing. There are few pages on debugging and some modules to help with testing, but I would like to see more. At least some talk about selenium, which is great for a site made with any framework/cms.

Over all, Thanks APress for another great book!

ˆ Back to top

ASP.NET – 'PASSING PARAMETERS' IN BUTTON CLICK-HANDLER

August 10th, 2007 by comment Kris

A recent posting to a technical mailing list I’m on asked how to pass a parameter to a button click-event handler. The programmer wanted to do the equivalent of this:

<asp:button ID="btnSubmit" runat="server" OnClick="Btn_Onclick(PARAM)" />

But, she knew this didn’t work, because the default signature of event handlers in ASP.NET is this:

void myBtn_OnClick(Object sender, EventArgs e)

So, she was stumped on how to pass some sort of argument to the event handler.

Now, there a certainly some kludges that would work, such as having a hidden form field which contains the ‘parameter’ value, and having the event-handler get the value that way. But surely, there’s a better way? Well, yes, of course there is! :)

This better way is to use the “CommandArgument” property on the button control. This property is directly accessable in the event handler. The added bonus is that you can write a single even-handler for all of your button clicks, and use the associated “CommandName” property to know which button fired.

So, now, in your aspx page, your button object will be declared thusly:

<asp:Button ID="test" runat="server" CommandArgument="MyVal" CommandName="ThisBtnClick" OnClick="MyBtnHandler" />

Then, in the OnClick handler, you can cast the sender object to the correct type, and get the values of
CommandName & CommandArgument. (You could make this even more generic and handle the OnClick event for different senders, but we’ll leave that for a later post! :)

void MyBtnHandler(Object sender, EventArgs e)
{
	 Button btn = (Button)sender;
          switch (btn.CommandName)
          {
                case "ThisBtnClick":
                    DoWhatever(btn.CommandArgument.ToString());
                    break;
                case "ThatBtnClick":
                    DoSomethingElse(btn.CommandArgument.ToString());
                    break;
           }
}

You can see that the switch statement uses the CommandName property to determine which button was clicked, and then can use the CommandArgument property passed in by the button click event.

Voila! You’ve “passed a parameter” to a button click-event handler!

[UPDATE] Thanks to Jim for pointing out some confusion in my sample code. I hope that this version is clearer.

ˆ Back to top

Searching for Up-To-Date Help

August 5th, 2007 by comment Ana Nelson

When you are searching for help on a programming topic, frequently the answer will come in the form of a blog post. Preferably one with lots of useful feedback in the comments to help you decide if it’s good or bad advice! But, with the speed of software development these days, the answer to your configuration question or what to do if you get a particular error message might be very different today than it was 18 (or even 6) months ago.

A quick way to narrow down search results is to take advantage of the fact that many blogs will incorporate the creation date of a post in the URL, and use google’s inurl syntax in your search. For example, to search for recent posts on session storage in Ruby on Rails, try “inurl:2007 rails session storage”. This isn’t foolproof, you’ll miss out on any resources that don’t use a URL scheme like this, but it will filter out lots of articles from 2005 telling you to empty your tmp/sessions directory on a regular basis. You can even try to include a month parameter, and conveniently “inurl:2007/08″ matches both “2007-08″ and “2007/08″ style URLs.

ˆ Back to top

Parallel Python Review: In a Nutshell: Wow!

August 3rd, 2007 by comment gloriajw

A colleague came to me about a month ago and asked: “Have you seen this?” “No, but it looks great, if it really works the way it claims.” I replied. We were ogling this link, with expressions of “Ooo”, “Ahhh”, and “Wow”:

http://www.parallelpython.com/

He installed it on his XP desktop, I installed it on my Fedora Core 6 Desktop, and on our Linux test server with eight CPUs. The installation in an existing Python distribution took five minutes. Reading the daemon instructions and starting Parallel Python daemons on all of these machines took another three minutes.

We modified a test example, a distributed sum of primes, to run for many minutes, giving it a huge upper limit. On the Linux machines, running the top command, then pressing the number 1 reveals the activity on all CPUs. We started the modified test example, closely watching top and the task table on XP. We saw an even distribution of jobs across machines, and a consistently even balance amongst all CPUs. Parallel Python is written so well that it balances jobs according to a machine’s capacity and speed.

I decided to integrate it into my web scraper project. I need to spawn jobs to scrape different pages in parallel. The problem I had was mapping my OO model to the existing procedural examples. I have a scraper class, and I want Parallel Python to run it’s run() method, and see it’s self variables.

Looking for help, I find a help forum on the Parallel Python web site. Not a bad start. I post my question, and get a response within 24 to 48 hours from the most helpful geek I have ever dealt with.

Vitalii Vanovschi, the author of the examples, and probably also the author of most of the Parallel Python code, offers damned near flawless, quick, and ego-free assistance with this software. To answer my question, he rewrote an example ‘on the fly’, to show how classes can be used in Parallel Python. His responses to everyone are consistently respectful, concise, and helpful, with a strong undertone of of the genuine love for the Python language and it’s capabilities. This is the best tech support I have ever gotten for any product, EVER.

To top it off, there is a link on his main page, asking people to talk about what drew people to Python. This adds a ‘Ruby-like’ social element to his web site which is still unfortunately rare in the Python realm. I was thrilled to find some social aspect to this web site and it’s subject matter. It is refreshing to be able to talk about the technical and social aspects of the language and the tools, simultaneously, with the same crowd of people. Rubyists seem to understand this, and Vitalii’s hope to bring this aspect into the Python world does not go unnoticed.

Vitalli has brought so many good things together in one application, on one web site. He has created a simple, easy-to-navigate web interface with related forums, great documentation and examples. The software itself is robust, well-written and so easy to use that one can integrate it into an existing project very quickly.

This project is an excellent example of good software, good presentation, good tech support, and good social support. I think Python programmers can learn a lot from this example. My hopes are that more Python project developers make better attempts at the social aspect of their projects, asking people to share their experiences with the project and the related languages in an ego-free environment. Maybe user communities will form from these environments. And maybe, just maybe, Python programmers can one day experience the sense of community that Ruby developers already enjoy.

Vitalli, kudos, and thank you for such a great, well-rounded project. Thank you for setting the the technical, social and presentation precedents. May all new projects follow this model.

ˆ Back to top