Thursday, October 28, 2010

Grasshoppers

We came unto the land whither thou sentest us, and surely it floweth with milk and honey; and this is the fruit of it...And there we saw the giants...and we were in our own sight as grasshoppers, and so we were in their sight.




I HATE being so close and yet so far. OOOOH I hate it. I have practically everything working in my models and database. Yay! but for some reason, I can't get Users to connect to Technicians (I should probably change the name of that model to TechnicalRoles or something) or Characters (via Castings). I'm using ianwhite's nested_has_many_through plugin, and I'm sure there is something I'm just not getting. Also, I wish documentation for this sort of thing was more helpful. You get what you pay for, but maybe I could be of service to the Rails community by first having someone explain their plugin to me, and then writing better documentation. Of course, maybe they're all happy with their documentation and I just need to get better at reading it.

Sigh.

Ok, so here's what I came up with.
The Production model is the nexus of everything. It's the one unique event. A theater does lots of different plays; each play is done by lots of different theaters; actors work for various theaters. A production only happens in one moment in time and is a unique combination of participants, theater spaces, text, etc. This was my breakthrough, and I'm sure it's the right thing. To that end:

class Production < ActiveRecord::Base
  attr_accessible :date
  has_many :castings
  has_many :users, :through => :castings
  has_many :technicians
  has_many :users, :through => :technicians, :source =>:users
  belongs_to :theater
  belongs_to :play
  has_many :characters, :through => :play
end

Castings is basically a join model to connect Users to Characters. The characters are also connected to their plays.

The part that I'm hung up on is connecting Characters to Users.
These work:
>> @production.characters
=> [#, #, #]

>> @user.productions
=> [#, #, #]

These don't:
>> @user.characters
>> @character.productions


It throws this error:
NameError: uninitialized constant User::Characters
    from /home/alisha/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:105:in `const_missing'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2199:in `compute_type'
    from /home/alisha/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/core_ext/kernel/reporting.rb:11:in `silence_warnings'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2195:in `compute_type'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:156:in `send'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:156:in `klass'
    from /home/alisha/code/henslowe/vendor/plugins/nested_has_many_through/lib/nested_has_many_through.rb:43:in `construct_nested_join_attributes'
    from /home/alisha/code/henslowe/vendor/plugins/nested_has_many_through/lib/nested_has_many_through.rb:19:in `construct_conditions'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations/has_many_through_association.rb:201:in `construct_sql'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations/association_collection.rb:21:in `initialize'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations/has_many_through_association.rb:6:in `initialize'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in `new'
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in `characters'

I'm not Rubiriffic enough to understand that error message.

So anyway, here are the relevant bits of the models that it's looking at.
class User < ActiveRecord::Base
  has_many :castings
  has_many :characters, :through => :castings, :dependent => :destroy
  has_many :productions, :through => :castings
  has_many :technicians
  has_many :productions, :through => :technicians
end

class Character < ActiveRecord::Base
  attr_accessible :name, :play_id
  belongs_to :play
  has_many :castings
  belongs_to :user, :through => :castings
end

class Casting < ActiveRecord::Base
  attr_accessible :user, :character
  belongs_to :production
  belongs_to :character
  belongs_to :user
end

class Production < ActiveRecord::Base
  has_many :castings
  has_many :users, :through => :castings
  has_many :technicians
  has_many :users, :through => :technicians, :source =>:users
end

The problem doesn't seem to be in the Casting model -- I can get all of a user's productions, which goes through the Casting model, but I can't get all of a user's characters. I can't get all of a character's productions, but I CAN get all of a production's characters.

I'm getting NoMethod errors when I try it:

@char.productions
NoMethodError: undefined method `productions' for #
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/attribute_methods.rb:260:in `method_missing'
@char.users
NoMethodError: undefined method `users' for #
    from /home/alisha/.gem/ruby/1.8/gems/activerecord-2.3.5/lib/active_record/attribute_methods.rb:260:in `method_missing'

I'm guessing the issue lies in the Character model, but I just can't figure it out. It's probably some stupid "that was singular but by convention it should be plural" crap nonsense. And I'm tired and I feel like a grasshopper today.

Thursday, October 21, 2010

Programming is hard



Let's go shopping!

:-/

I got about half of the schema reorg described above done. Some of the "has"es identified above probably should be "belongs_to" (does a production have a theater, a play, a director, or does a theater have a production and a play has the same production and ...? Given that the production is kind of the nexus of the whole thing, maybe it *does* have everything and belong to nothing....Oh heck, I don't know). My brain is kind of fried. Doing this one-handed on six hours of interrupted sleep probably isn't helping.

I'm going to take a break and do something I'm good at: Theater History! Yay!

Why am I calling this project "Henslowe's Cloud"?
The "cloud" part is for cloud computing. Part of the idea here is to put most of a theater's necessary information in the cloud so that if your theater burns down (hey, it happens, just ask Shakespeare...but only if you're performing Henry VIII), you don't lose all your info. Also, having it in the cloud makes it so some technically savvy person (like me woot) can administer it for you remotely. You don't have to worry about anything but scraping up the money to pay her. :)

The Henslowe part is cool.

 Henslowe as played by Geoffrey Rush in Shakespeare in Love.

Philip Henslowe was a theater manager . He wasn't an actor. He bought plays, hired actors, and ensured the whole enterprise made money. He managed the Admiral's Men, which competed with Shakespeare's company.

Yes, this is actually Henslowe's Diary.

The only reason anyone knows or cares about Henslowe is his "diary," a ledger of records for his theaters. It contains entries like this: "pd vnto the tyre man for mackynge of the devells sute & sperethes & for the witche for the playe of the iij brothers the 23rd of october 1602 some of xs ixd" ("paid unto the tireman for making of the devil's suit and ??? and for the witch for the play of the three brothers, sum of 10 shillings, 9 pence"). Thanks to Henslowe, we have some good idea of how much actors were paid, which plays were popular in the time, what kind of inventory the theaters had, in terms of props and costumes, etc. Henslowe's Diary is an incredibly valuable document for theater historians, but it's madly disorganized. Just imagine what Henslowe could have done if he'd only had a relational database!

...Thus, Henslowe's Cloud. I know it's dumb and obscure and theater history in-jokey. I love it all the same.

Wednesday, October 13, 2010

Productions are complex.

Ok, seriously, why can't I ever start with a simple problem when I decide to learn a new programming paradigm? (not just a language, but a whole paradigm. My last language was PERL, which is sooo not object-oriented. My first project was building a content model for Rosetta Stone, where I work, and mapping it against new texts to determine whether or not a user could read the new text, given X units of Rosetta Stone. Harder than it sounds, since I took into account new forms of known head words).

The problem at hand:
I want to build a stage management system. I want it to do LOTS of cool things.
Examples:
  • Have you ever noticed how every theater box office website SUCKS? I mean, really every one. Even the Kennedy Center, and they've got more money than God has angels. Why do they all suck? Selling tickets is not a hard problem, people. Why not just adapt ticketing software from, like...sports or something? Those people know what they're doing.
  • Maintain actor bios so that making the program is easy. Automatically add a credit whenever the actor is cast in any play managed by the system.
  • Help with doubling. This is when one actor plays several roles. It's frequently done in Shakespeare productions, where you have 40 characters, but several of them are in only a few scenes each. I did Antony and Cleopatra with 12 actors, and there are 43 characters. Doubling is a great theatrical technique, but it sucks to figure out. It's crazy error-prone. I think that writing a program that would create doubling charts is far too complicated for me (or practically anyone--some smart person once explained to me that this is basically the traveling salesman problem, and requires AI). I'd be happy to create a system that allows a director to propose a solution and then says whether or not it will work.
  • Create part-scripts. Say you have a 16th-century play, and you want to do it old-school. Your advertising gimmick is that the rehearsal process is just like it was in Shakespeare's day (as far as we can guess). The American Shakespeare Center's Renaissance Season every spring is like this. Well, you'd want to use part-scripts (also called "rolls"). A part script is where it has the couple lines before your character speaks, what your character says, and then a big jump to the next cue for your character. In the days before printing was cheap and easy, this is how actors received their lines. Now, it is true that many of Shakespeare's plays are available in part-script form. But what if you want to cut the script a bit? What if you are doing doubling and you want each actor to have a part-script that contains all the lines for all of that actor's characters? To the best of my knowledge, practically every theater does this using cut-and-paste. No automated solutions exist (except for the one I wrote in PERL, and which I plan to do in Ruby).
  • The biggie: scheduling rehearsals. This is how most directors/stage managers schedule rehearsals. You have one spreadsheet that has all the actors' conflicts (when they can't rehearse). You have another that shows all the scenes everyone is in. By going back and forth between spreadsheets, you figure out which scenes you can rehearse at each time slot. There's a very high probability for human error here, and it takes HOURS every week.
Those are just a few of the ideas I have. I really think this could be a useful service for theaters. The last problem is the one I chose to start with (because I'm dumb and overly ambitious). My trouble here is that it's so very complex to model a theater production. Then if I want to make it scalable (so that I could have several client theaters, and the whole system wouldn't collapse if two of them did Hamlet, or if a theater decided to do You Can't Take It With You for a second time, or if an actor worked for two different client theaters), it's a whole other can of worms.

I had a lot of this working, using a spaghetti junction of has_and_belongs_to_many join table things. Then I realized that it wasn't extensible. If I were modeling only one production, it would be great. But modeling only one production is not useful. I think that what I need to do is rewrite it to use has_many :through, with a "Production" model as the nexus. I'll still need other models, like a "Casting" model to connect users with roles. It's still a mess. I think the nested_has_many_through plugin will help here, but I'm a bit nervous that it won't. If it doesn't, I'm not sure what to do. So, here's my test schema...any comments would be greatly appreciated. I haven't implemented this yet because I'm sort of nervous about it...I have subversioned my present development instance, so it shouldn't be a big deal...but oh, the obfuscatory error messages! I can hardly bring myself to confront them.

 I need to bite the bullet and just do it...but I'm reluctant.

theater
has_many :productions
has_many :users, through :productions
has_many :plays, through :productions

production
belongs_to :theater
has_many :castings
belongs_to :play

user
has_many :credits, through :castings
has_many :productions, through :castings
has_many :conflicts
has_many :theaters, through :productions
has_many :plays, through :productions
has_many :acts, through :plays
has_many :scenes, through :acts

conflict
belongs_to :user

play
has_many :productions
has_many :theaters, through :productions
has_many :acts
has_many:scenes through :acts

act
has_many :scenes
belongs_to :play

scene
belongs_to :act
casting
has_many :users
has_many :credits #I'm saying credits instead of roles because "role" is a reserved model for my authorization thing. Also, "credit" allows for technicians as well as actors.