Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 1843 lines (1354 sloc) 64 KB
NOTES
Notes on what I did while I was doing it. Some of the time I didn't
know what I was doing but I still wrote it down.
--William Denton <wtd@pobox.com>
* 20 July 2008
$ rails openfrbr
Used the notes here to put everything under git:
http://toolmantim.com/article/2007/12/5/setting_up_a_new_rails_app_with_git
$ cat .gitignore
log/*.log
tmp/**/*
.DS_Store
doc/api
doc/app
*~
$ touch log/.gitignore
$ touch tmp/.gitignore
$ ./script/generate scaffold work
$ ./script/generate scaffold expression
$ ./script/generate scaffold manifestation
$ ./script/generate scaffold item
$ ./script/generate scaffold person
$ ./script/generate scaffold family
$ ./script/generate scaffold corporate_body
$ ./script/generate scaffold concept
$ ./script/generate scaffold frbr_object (object is a reserved word)
$ ./script/generate scaffold event
$ ./script/generate scaffold place
$ git init
$ git add .
$ git commit -m "Initial checkin in; a bit of scaffolding"
On the Eee I ran
$ cd src
$ git clone buff@10.1.0.9:src/openfrbr
and it copied everything over.
First database migration
$ ./script/generate migration create_works_table
exists db/migrate
create db/migrate/20080721001523_create_works_table.rb
I edited the file and added basic stuff to make the works table
class CreateWorksTable < ActiveRecord::Migration
def self.up
create_table :works do |table|
table.column :title, :string
table.column :form, :string
table.column :date, :date
table.column :comment, :string
end
def self.down
drop_table :works
end
end
$ rake db.migrate
There was a bunch of output about all the various tables.
* 25 July 2008
I'm not using these migrations right. I wanted to load up the
development database with some initial data, and I knew that could be
done in migrations. (Is this new to Rails 2? Much of what I found
about this online seems out of date now.) I made a new migration file
to load in the data, but it didn't take. I then edited an early migration,
which didn't take either, so I had to do rake:db:migrate VERISON=0 to back to
the very start and then rake:db:migrate to catch up on everything. I
don't know if this is the right way or not, but it works.
When I refreshed things on the Eee and ran script/server and hit
/works I got some kind of cookie error. Data copied over by git that
shouldn't have been? And then the migration stuff didn't work. Said
I'd already created the works table!
* 28 July 2008
rake db:drop; rake db:migrate seems to work well for getting around any
migration errors. It just drops everything and then you can start over.
Put a bunch of table configuration and test data into the migration files,
including setting up the HABTM join tables. Kept resetting the database
and migrating it from scratch. Probably not the best way to do it, but it
does sort of keep it tidy and gives me a decent baseline to work with for
later, when I add attributes to the relationships between things.
* 31 July 2008
Did some more setting up of the entities and defining some test
data. Didn't set up any relationships between PHILOSOPHER'S STONE
and Platform 9 3/4 as a place, though, or anything like that.
I had a look at the entity-relationship diagram I did that shows all of
the FRBR entities and relationships, and realized my data model was wrong.
I need to have attributes on the relationships, so instead of
has_and_belongs_to_many I need to use has_many :through, and then set up
the join with attributes. A work can be created by Person A and also have
Person B as a subject. I don't want to clutter it all up with tons of
different tables for all those joins.
Looked at a polymorphism plug-in but I don't feel like tackling that.
Since sqlite won't work on the Eee I tried to get it going with
Postgresql, but I don't think I was running the daemon right because it
couldn't connect. I'd better tackle fixing sqlite again, before messing
about with anything more serious.
* 2 August 2008
Checked FRBR and FRAD and updated the attributes on the entities. Thought
about how to connect them all with has_many :through but haven't done
that yet. Should use these expressions of FRBR in RDF:
http://vocab.org/frbr/core
http://vocab.org/frbr/extended
Created app/views/layouts/application.html.erb and then removed all of the
other layouts. The application layout will be used by all views now.
I'll do a bit of CSS and styling to get a left nav going, or something
like that.
For fun I thought I'd try autocompletion on manifestation titles, just
to see how that worked. Followed what is explained here:
http://codeintensity.blogspot.com/2008/02/auto-complete-text-fields-in-rails-2.html
After a few minutes of this I decided I was getting ahead of myself, so I stopped.
Fiddled with the CSS so there's a left nav.
Removed public/index.html and added this to routes.rb:
map.root :controller => "works"
Now the default for http://localhost:3000/ is to list all the works.
* 5 August 2008
I installed Ubuntu on my Eee and now I can use Rails without any
trouble. I was getting errors with rake db:migrate on the old OS but
now all is well.
* 9 August 2008
After some fiddling around so that associated works are listed on the
subject pages, I'll try setting up has_many :through relations between
works and people just for starters. What I think I need is a
polymorphic many-to-many relationship between Group 1 and Group 2
entities, with attributes on the relationships. Any Group 1 entity
can be in a variety of many-to-many relationships with any Group 2
entity, and if I can have that all in one model, that would be cool. I
think. I don't know. I'll start small and build up. Not sure how this
will all go in migrations. I think I need to use fixtures to set up
my test data.
I couldn't think of a word or phrase to sum up all of these different
relationships, so I just called the model TwoAndOne.
$ ./script/generate model TwoAndOne -g
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/two_and_one.rb
add 'app/models/two_and_one.rb'
create test/unit/two_and_one_test.rb
add 'test/unit/two_and_one_test.rb'
create test/fixtures/two_and_ones.yml
add 'test/fixtures/two_and_ones.yml'
exists db/migrate
create db/migrate/20080809163307_create_two_and_ones.rb
add 'db/migrate/20080809163307_create_two_and_ones.rb'
After some time not getting this to work, I decided to make a model
called Creator, and start by making it so that works and people were
in a many-to-many relationship this way. That's more natural.
That's the way we talk.
./script/destroy model TwoAndOne -g
had a lot of output.
Later: Got this working. My models look like this:
class Work < ActiveRecord::Base
has_many :expressions
has_many :people, :through => :creators
has_many :creators
end
class Person < ActiveRecord::Base
has_many :works, :through => :creators
has_many :creators
end
class Creator < ActiveRecord::Base
belongs_to :work
belongs_to :person
end
I set up creators.yml with
one:
work_id: 1
person_id: 1
relation: isAuthorOf
two:
work_id: 2
person_id: 1
relation: isAuthorOf
three:
work_id: 3
person_id: 1
relation: isAuthorOf
And now this does it in show.html.erb for people:
<ul>
<%- @person.creators.each do |c| %>
<li> <%= c.relation %> <%= link_to Work.find(c.work_id).title,
:controller => "works",
:action => "show",
:id => c.work_id %>
</li>
<%- end %>
</ul>
That says that she "isAuthorOf Harry Potter and the Philosopher's
Stone" and two others.
Getting families and corporate bodies into the mix won't be hard, but
I'm going to commit this now.
Question: How can I change the method name on people so that I can say
work.creators but person.creations? I want the name of the
many-to-many relation to be different depending on which way I look at
it. Must be some way to do that.
Later: Installed the has_many_polymorphs gem.
* 16 August 2008
Now I'm on the train back to Toronto. The Creation and Creator models
are both broken, but I'm going to leave them that way for now. To get
moving along I'm going to do a basic has_many :through polymorphic
relation to set up the Subject relationship. Work is in a
many-to-many relationship with every other entity as a Subject: a Work
can have any of Work, Expression, Manifestation, Item, Person, Family,
Corporate Body, Concept, Event, Object, or Place as a subject.
There's only one relation: hasSubject (inverse isSubjectOf).
./script/generate model Subject -g
Hmm, I think that was stupid. What I'd be doing there is a has_many
:through polymorphic association, which is what I was trying to do
with Creation, but couldn't get to work, so now I three broken and
incomplete things going on.
All right, I'm going to revert to the last checkin to just wipe all
that out.
After more fooling around: bugger this, I need net access and I need
to get it working on a simple test example with no other stuff going
on.
* 22 August 2008
I decided to start of simple and made a basic has_many :through
relation between Work and Person and called it Creation.
class Creation < ActiveRecord::Base
belongs_to :person
belongs_to :work
end
class Work < ActiveRecord::Base
has_many :creations
has_many :people, :through => :creations
end
class Person < ActiveRecord::Base
has_many :creations
has_many :works, :through => :creations
end
And the migration:
class CreateCreations < ActiveRecord::Migration
def self.up
create_table :creations do |t|
t.column :person_id, :integer
t.column :work_id, :integer
t.column :relation, :string
end
end
def self.down
drop_table :creations
end
end
That lets me do this:
buff:eee:~/src/openfrbr$ ./script/console
Loading development environment (Rails 2.1.0)
>> w = Work.find(1)
=> #<Work id: 1, created_at: "2008-08-22 18:29:58", updated_at: "2008-08-22 18:29:58", title: "Harry Potter and the Philosopher's Stone", form: "novel", date: "1997", comment: "First book">
>> p = Person.find(1)
=> #<Person id: 1, created_at: "2008-08-22 18:29:57", updated_at: "2008-08-22 18:29:57", name: "J.K. Rowling", dates: "31 July 1965", title: nil, other_designation: nil, affiliation: nil, country: nil, comment: nil>
>> c = Creation.new(:relation => 'isAuthorOf')
=> #<Creation id: nil, person_id: nil, work_id: nil, relation: "isAuthorOf">
>> p.creations << c
=> [#<Creation id: 1, person_id: 1, work_id: nil, relation: "isAuthorOf">]
>> w.creations << c
=> [#<Creation id: 1, person_id: 1, work_id: 1, relation: "isAuthorOf">]
>> c.save
=> true
>> p.creations
=> [#<Creation id: 1, person_id: 1, work_id: nil, relation: "isAuthorOf">]
>> p.works
=> [#<Work id: 1, created_at: "2008-08-22 18:29:58", updated_at: "2008-08-22 18:29:58", title: "Harry Potter and the Philosopher's Stone", form: "novel", date: "1997", comment: "First book">]
>> w.creations
=> [#<Creation id: 1, person_id: 1, work_id: 1, relation: "isAuthorOf">]
>> w.people
=> [#<Person id: 1, created_at: "2008-08-22 18:29:57", updated_at: "2008-08-22 18:29:57", name: "J.K. Rowling", dates: "31 July 1965", title: nil, other_designation: nil, affiliation: nil, country: nil, comment: nil>]
>>
Good.
So that's a many-to-many relationship done, with an attribute on the
relation. One Work can be created by many People, and one Person can
create many Works.
What I want to say is that Work is in a many-to-many Creation
relationship with all of People, Families, and Corporate Bodies.
That's a many-to-many polymorphic relation. I'll use
has_many_polymorphs for that, but first, I think I'll get used to a
basic polymorphic relationship by setting up a one-to-many polymorphic
Realization relation between Expression and Person, Family, and
Corporate Body. Then I'll have a many-to-many and a one-to-many
polymorphic and it should be easier to set up has_many_polymorphs with
that knowledge.
Insight: this is a one-to-many relation, so there's no new model to
set up. Aha. I did this:
class Person < ActiveRecord::Base
has_many :expressions, :as => :realizer
end
class Family < ActiveRecord::Base
has_many :expressions, :as => :realizer
end
class CorporateBody < ActiveRecord::Base
has_many :expressions, :as => :realizer
end
class Expression < ActiveRecord::Base
belongs_to :realizer, :polymorphic => true
end
And this is the migration:
class CreateRealizations < ActiveRecord::Migration
def self.up
add_column :expressions, :realizer_id, :integer
add_column :expressions, :realizer_type, :string
end
def self.down
remove_column :expressions, :realizer_id
remove_column :expressions, :realizer_type
end
end
$ ./script/console
Loading development environment (Rails 2.1.0)
>> p = Person.find(1)
=> #<Person id: 1, created_at: "2008-08-22 19:43:37", updated_at: "2008-08-22 19:43:37", name: "J.K. Rowling", dates: "31 July 1965", title: nil, other_designation: nil, affiliation: nil, country: nil, comment: nil>
>> e = Expression.find(1)
=> #<Expression id: 1, created_at: "2008-08-22 19:43:36", updated_at: "2008-08-22 19:43:36", title: "Harry Potter and the Philosopher's Stone", form: "text", date: "1997", language: "English", comment: nil, work_id: 1, realizer_id: nil, realizer_type: nil>
>> c = CorporateBody.find(1)
=> #<CorporateBody id: 1, created_at: "2008-08-22 19:43:35", updated_at: "2008-08-22 19:43:35", name: "Raincoast Books", dates: nil, other_designation: nil, place: "Vancouver, BC", comment: nil>
>> e2 = Expression.find(2)
=> #<Expression id: 2, created_at: "2008-08-22 19:43:36", updated_at: "2008-08-22 19:43:36", title: "Harry Potter and the Chamber of Secrets", form: "text", date: "1998", language: "English", comment: nil, work_id: 2, realizer_id: nil, realizer_type: nil>
>> e.realizer = p
=> #<Person id: 1, created_at: "2008-08-22 19:43:37", updated_at: "2008-08-22 19:43:37", name: "J.K. Rowling", dates: "31 July 1965", title: nil, other_designation: nil, affiliation: nil, country: nil, comment: nil>
>> e2.realizer = c
=> #<CorporateBody id: 1, created_at: "2008-08-22 19:43:35", updated_at: "2008-08-22 19:43:35", name: "Raincoast Books", dates: nil, other_designation: nil, place: "Vancouver, BC", comment: nil>
>> e.save
=> true
>> e2.save
=> true
Then going into ./script/dbconsole and doing
sqlite> .dump expressions
shows that the ID number and type of the realizer have been stored,
for example:
INSERT INTO "expressions" VALUES(1,'2008-08-22 19:43:36','2008-08-22 19:50:27','Harry Potter and the Philosopher''s Stone','text','1997','English',NULL,1,1,'Person');
So that's how to do a one-to-many polymorphic relation.
I will check this in.
All righty. For my next move I will try using has_many_polymorphs with
a Production model to handle the relation between Manifestation and
Person, Family, and Corporate Body.
Got it working!
class Manifestation < ActiveRecord::Base
has_many_polymorphs :producers, :from => [:people, :families, :corporate_bodies], :through => :productions
end
class Production < ActiveRecord::Base
belongs_to :manifestation
belongs_to :producers, :polymorphic => true
end
Nothing is needed in the Person, Family, and Corporate Body models,
because of the way that has_many_polymorphs works.
Here's the migration:
class CreateProductions < ActiveRecord::Migration
def self.up
create_table :productions do |t|
t.references :producer, :polymorphic => true
t.references :manifestation
end
end
def self.down
drop_table :productions
end
end
Here are some examples (leaving out some of the output):
>> p1 = Person.find(1); p2 = Person.find(2); c1 = CorporateBody.find(1); c2 = CorporateBody.find(2)
>> m1 = Manifestation.find(1); m2 = Manifestation.find(2)
>> m1.producers << p1; m1.producers << c1
>> m2.producers << p1; m2.producers << p2; m2.producers << c1; m2.producers << c2
>> m1.producers.each do |p|; puts "#{p.name} (#{p.class})"; end
J.K. Rowling (Person)
Raincoast Books (CorporateBody)
>> m2.producers.each do |p|; puts "#{p.name} (#{p.class})"; end
J.K. Rowling (Person)
Raincoast Books (CorporateBody)
Arthur A. Levine (CorporateBody)
William Denton (Person)
>> p1.productions
=> [#<Production id: 1, producer_id: 1, producer_type: "Person", manifestation_id: 1>, #<Production id: 3, producer_id: 1, producer_type: "Person", manifestation_id: 2>]
>> p1.productions.each do |p|; puts Manifestation.find(p.manifestation_id).title; end
Harry Potter and the Philosopher's Stone
Harry Potter and the Chamber of Secrets
xSo a Manifestation has producers and a Person, Family, or
CorporateBody has productions. That's perfect. I will check this in.
The Production model is done.
There is one small thing I can't get working right, about the test
fixtures. I put this in test/fixtures/productions.yml:
PhilosopherProducer:
manifestation_id: 1
producer_id: 1
producer_type: CorporateBody
ChamberProducer:
manifestation_id: 2
producer_id: 1
producer_type: CorporateBody
AzkabanProducer:
manifestation_id: 3
producer_id: 1
producer_type: CorporateBody
When I do
rake db:drop; rake db:migrate; rake db:fixtures:load
then the database looks right and the productions table has the right
data in it. But:
>> c = CorporateBody.find(1)
>> c.productions
NoMethodError: undefined method `productions' for #<CorporateBody:0xb6df6cac>
from /var/lib/gems/1.8/gems/activerecord-2.1.0/lib/active_record/attribute_methods.rb:256:in `method_missing'
from (irb):2
>> m = Manifestation.find(1)
>> m.producers
=> [#<CorporateBody id: 1, created_at: "2008-08-23 02:29:11", updated_at: "2008-08-23 02:29:11", name: "Raincoast Books", dates: nil, other_designation: nil, place: "Vancouver, BC", comment: nil>]
>> m.producers << c
=> [#<CorporateBody id: 1, created_at: "2008-08-23 02:29:11", updated_at: "2008-08-23 02:29:11", name: "Raincoast Books", dates: nil, other_designation: nil, place: "Vancouver, BC", comment: nil>]
>> c.productions
=> [#<Production id: 366820064, producer_id: 1, producer_type: "CorporateBody", manifestation_id: 2>, #<Production id: 564650414, producer_id: 1, producer_type: "CorporateBody", manifestation_id: 3>, #<Production id: 831263640, producer_id: 1, producer_type: "CorporateBody", manifestation_id: 1>]
The productions method for a corporate body doesn't exist right off
the bat, but if I add it as a producer of a manifestation then all of
its other prouctions appear as set up in the text fixture. One side
of the relation is getting set up right, but not the other. There may
be some aspect of test fixtures and has_many_polymorphs that I don't
know about, but without net access I can't check it. I don't see
anything in the has_many_polymorphs docs and I can't see anything
relevant in its test fixtures.
I will leave it for now and check this in.
Next, I will set up an Ownership relation between Item and Group 2
entities that is otherwise identical to Production.
Check that in.
Now I'll redo the Creation model so it uses has_many_polymorphs. I
think i'll do it the wrong way and just edit the migration that I
created earlier. Oh well.
* 23 August 2008
I did Creation and made a Realization model, so now all of the
relations between the Group 1 entites and Group 2 entities as
creators, realizers, producers, and owners exist.
The last big data model challenge will be to set up the many-to-many
Subject relation between Work and every other entity (because a Work
can have as a subject any Work, Expression, Manifestation, Item,
Person, Family, Corporate Body, Concept, Event, Object, or Place).
I'll use has_many_polymorphs for that, but there's the added twist
that there's a many-to-many Work-to-Work relationship in there, a
self-referential many-to-many relation, and I'll have to look into how
to make that work. Can't remember if that'll require a bit of extra
configging or not. If I can't get it to work I can always set up the
relations to all of the other entities and then add it in later.
That's one of the nice things about migrations.
TODO: Set up Subject relation between W and all entities.
Next, though, it's time to make it so that when I view a work I can
see a list of its creators, and so on for the other Group 1 entities.
I think a helper to generate the links will do the trick.
The generate_group2_entity_link_list method in
app/helpers/applications_helpers did that.
TODO: Noticed that when I'd made up the has_many :through relation for
Works and Group 2 entities I'd added a relation attribute to the
relation, e.g. "isAuthorOf." That's gone now. I could add in role or
something like that. Perhaps more appropriate for Expression?
Translator, conductor, performer, reader, etc.
That helper method I added was wrong. It should have been a partial
layout. I changed it.
TODO: Add more expressions for the works, and manifestations and items
for each, so that the hierarchical tree has enough in it to be
interesting.
TODO: See how to use Ajax drag and drop to move a manifestation from
one expression to another in that tree view, and have the connection
relation updated in the database.
TODO: Add an autocompleting search in the left nav that searches for
... just works? Or any entity, and it tells you what it is in
brackets?
TODO: Set up self-referential Work-to-Work relation with attributes
(hasSequel/isSequelOf etc.) and display that in the work views.
* 24 August 2008
Done: Edit familes, corporate bodies, and Group 3 entities.
TODO: One-button "remove" to drop a link between a G1 entity and a G2
entity, to go beside a name, to remove it as creator, owner, etc.
p = Person.find(5)
w = Work.find(1)
w.creators << p # Create creation reation
w.creators.delete(p) # Delete creation relation
Do this in an Ajaxy way so the page doesn't reload, the relation just
disappears.
* 25 August 2008
TODO: After deleting an entity, delete all of the associations it has
with other entities. If a Person is the realizer of an Expression and
the Person is deleted, e.realizers will still list the person as a
realizer. (Use same function as above.)
Was reading a bit about routes.rb but I don't understand it all, or
what everything shown in rake routes means
Added map.resources :creations
and edited map.resources :works, :has_many => :expressions
which gives new URLs like /works/1/expressions/new, but a basic GET on
that just gives an error. I have no idea what's going on here.
Am unsure of the best way to handle remove the creation relation
between a Work and a G2 entity. Should I do it in a Creation
controller? If so, how? Or should it be a method in the Work
controller? What is the RESTful way to do it?
I added the Aboutnesses model, which defines subject. (Took some
fiddling because I called it Subject first, which confused things, and
then I changed it to Aboutness, which is singular so it also confused
things. Lots of moving stuff around by hand.)
Now for a given work I can say w.subjects, but the name of the subject
is different in the three entities! Is it title, name, or term? What
about item--what's the best way of referring to that on its own? Hmm.
Maybe I need some sort of helper so if I pass in a class and an id, it
picks out the proper term to use in the display.
* 27 August 2008
Yesterday I set up the Aboutness model so I could say that a Work has
as subject this Concept or this Person or this Expression. The two W-E
relations confused things, though, and I couldn't get at the basic W-E
realization relation. I set up an aboutness branch in git and moved
the Aboutness stuff into there an worked on it until I had it figured
out--very nice. I branche and then did a git reset --hard HEAD~2 on
the master branch to set it back to how it had been before I broke
things.
I turned W-E realization into a model of its own, with an attribute.
Since Realization was in use (for the relation between an Expression
and a G2 entity), I called it Reification. That worked, but it
required a bunch of cleanup. (Note that in FRBR all of the WEMI an
G1-G2 relations are uniquely named except for realization: an
expression realizes a work, and a G2 realizes an expression. Caused
me confusion--worth pointing out.)
TODO: Set up Embodiment and Exemplification models for E-M and M-I
relations, with a relation attribute. Then every relation has a
model. On the other hand, I don't think I need a model for M-I,
because it's a basic one-to-many relation without any attributes.
Probably good to set up a model just for completeness, though.
TODO: Make a partial layout so if I pass in an object it will tell me
every work for which the object is a subject (use Aboutness).
Then I added an anchor_text method to every class so that I can always
just refer to object.anchor_text and it will return a properly
formatted name for the object. Work is title (form), Manifestation is
title (form_of_carrier, identifier), Person is name, Concept is term,
etc. Went through all pages and updated them to use anchor_text.
----------
A thought about Reification, Embodiment and Exemplification models. I
made Reification a many-to-many relation but then thought it should be
one-to-many: one Work can have many Expressions. But that's wrong. An
expression could be an abridgement of two or more works, for example,
a Reader's Digest version of The Lord of the Rings. Does that count as
a new work on Tillett's chart? I don't have it with me so I don't
know, but I think it would (or could) count as a new expression of
several works at once. So Reification should stay a many-to-many
model, for that reason and just so as not to limit implementattions
later. I'm not sure what the FRBR report says about this but I'll
leave it as is.
----------
TODO: Do not assume that an E has only one W. Anywhere we list the Ws
from which an E is reified, make it an array. Allow one E to be
associated with multiple Ws with select lists.
I took a stab at this and thought I had it working but ran into an
error I can't explain.
To start I defined this in expression.rb:
def work
# For expression e, e.work a Work object representing
# the first Work for which this Expression is a reification.
# (And only, if there is just one.)
Work.find(reifications[0].work_id)
end
and it worked fine. Then I set up a case in the test fixtures where
one E is an abridgment of three Ws. I made this method in expression.rb:
def works
# For expression e, e.works returns an array of all works
# for which e is a reification.
works = []
reifications.each do |r|
works << Work.find(r.work_id)
end
works
end
But e.works only works once! Like so:
>> e=Expression.find(5)
=> #<Expression id: 5, created_at: "2008-08-27 17:09:44", updated_at: "2008-08-27 17:09:44", title: "Reader's Digest version of first three books", form: "text", date: "2000", language: "English", comment: "Test for one expression with many works.">
>> e.works
=> [#<Work id: 1, created_at: "2008-08-27 17:09:46", updated_at: "2008-08-27 17:09:46", title: "Harry Potter and the Philosopher's Stone", form: "novel", date: "1997", comment: "First book">, #<Work id: 2, created_at: "2008-08-27 17:09:46", updated_at: "2008-08-27 17:09:46", title: "Harry Potter and the Chamber of Secrets", form: "novel", date: "1998", comment: "Second book">, #<Work id: 3, created_at: "2008-08-27 17:09:46", updated_at: "2008-08-27 17:09:46", title: "Harry Potter and the Prisoner of Azkaban", form: "novel", date: "1999", comment: "Third book">]
>> e.works
=> []
What the hell?
I don't know what's going on here. For now I'll go back to just
assuming there is one W for each E. Have to ask about this.
----------
TODO: Add methods to all of MI so that for each I can easily get an
array of an M's Es, an M's Ws, and I's Ws, etc. Or some other simple
way of getting at all that.
----------
I will now merge this aboutness branch back into the master because
Aboutness works.
$ git checkout master
$ git merge aboutness
----------
Added a Subject controller and edited the routes and made an Ajaxy
thing to dynamically add a subject to a work without reloading the
page. The new subject disappears if you follow a link and then come
back, though. And if a WEMI is the subject of a work then there's a
problem if it has a non-unique title.
TODO: Add an intelligent autocompletion to the subject entry field
here so that it help you distinguish between two W or E with the same
title, tells you what the entity type is, etc.
----------
TODO: On every entity page, add the ability to make that entity the
subject of a work. (Use intelligent autocompletion for the works.)
----------
I have moved the TODOs into a TODO file and will keep this as just
narrative notes.
* 11 September 2008
Installing Phusion Passenger on anvil.lisforge.net:
See
http://www.modrails.com/
# gem install passenger
# passenger-install-apache2-module
This complained that some Apache development stuff was missing. It
told me what to do.
# apt-get install apache2-prefork-dev
Then I went back to:
# passenger-install-apache2-module
It finished up and told me what configuration stuff to add to
Apache. I put this in /etc/apache2/apache2.conf
# Passenger config stuff for Ruby on Rails
# Added 7 Sep 2008 by wtd
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3
PassengerRuby /usr/bin/ruby1.8
I set up a virtual host:
<VirtualHost *>
# wtd
ServerName openfrbr.org
ServerAlias www.openfrbr.org
DocumentRoot /var/www/openfrbr.org/public
CustomLog /var/log/apache2/openfrbr.org-access.log combined
ErrorLog /var/log/apache2/openfrbr.org-error.log
<Directory /var/www/openfrbr.org/>
Options -Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
I copied all of the files into place. I ran
$ rake db:schema:dump
and copied db/schema.db onto anvil and put it in place.
On anvil I ran
$ rake db:schema:load RAILS_ENV=production
but it complained that Rails 2.1.0 was missing. (I hadn't frozen
anything.) It also complained that has_many_polymorphs was missing.
$ sudo gem install -v=2.1.0 rails
$ sudo gem install has_many_polymorphs
$ rake db:schema:load RAILS_ENV=production
$ rake db:fixtures:load RAILS_ENV=production
And it all works.
Trying a git repository.
And another.
* 26 September 2008
Rolling this up again before I announce it. OpenFRBR went out as
version 1 in 2006, so this will have to be version 2.
$ git tag v2.0
$ git archive --prefix=openfrbr-2.0/ v2.0 | gzip > openfrbr-2.0.tar.gz
* 6 November 2008
Announced isbn2marc, which I've been working on for a few days. It's
a new and improved version of zmarc.rb. Given one or more ISBNs it
will look up MARC records in open Z39.50 servers and then pretty print
them on screen and save them in binary or MARCXML formats. I need
this to gather test MARC records to process with the LC FRBRization
tool, which I've been fooling around with the last couple of weeks.
* 10 November 2008
Working on lcloader, which will take the LC XML file and load it up
into OpenFRBR. Lots of attributes are missing. Some can be copied
from one entity to another, but we'll need an easy way to edit
attributes (perhaps en masse).
* 28 November 2008
Signed up for a WorldCat API key, and added some lines to isbn2marc so
that anyone with a key can use to get MARC records much more quickly.
It really speeds things up, talking to WorldCat.
This are the steps now to load a bunch of stuff into the system to
look at it:
- take an ISBN
- superdupe it
- run isbn2marc on the ISBNs
- lcclean the MARC file
- run the MARC file through the LC tool with marc2frbr.sh
- use script/runner lcloader on the LC's FRBR XML file to load it up
Works fine with the Harry Potter stuff I've been testing it on. Need
to document this, or wrap it in a script, or something.
I loaded up 160 manifestations just now. Having that much in the
system makes it more obvious where easy editing and reorganizing is
necessary, so I'm going to try some in-place editing. The
documentation on that is pretty hard to find. I wish Rails's
documentation was as rich as Perl's.
First, need to install this plugin, though you'd never know this from
the docs online:
ruby script/plugin install http://svn.rubyonrails.org/rails/plugins/in_place_editing
Applied this patch:
http://dev.rubyonrails.org/ticket/10055
Blog post about this:
http://craigjolicoeur.com/blog/2008/07/rails-2x-in-place-editor/
Docs:
http://rails.rubyonrails.com/classes/ActionView/Helpers/JavaScriptMacrosHelper.html#M000575
That says, "DEPRECATION WARNING: This method will become a separate
plugin when Rails 2.0 ships. Use this method in your view to generate
a return for the AJAX autocomplete requests."
I'm using Rails 2.1.1 and 2.2 is now out!
After a bit of poking around I found a section in BEGINNING RAILS:
FROM NOVICE TO PROFESSIONAL, by James Allan Hardy et al, that covered
editing in place, and I copied their example and then added to it a
bit. I arrived at this.
This method in works_controller.rb
def edit_in_place
@work = Work.find(params[:id])
@work.send "#{params[:field]}=", params[:value]
@work.save
render :text => params[:value]
end
And this in show.html.erb for works:
<p>Title</p>
<p id="edit_title"><%= @work.title %></p>
<%= in_place_editor 'edit_title',
:size => @work.title.length,
:url => {:action => 'edit_in_place', :id => @work.id, :field => 'title'} %>
<p>Date</p>
<p id="edit_date"><%= @work.date %><%- if @work.date.nil? %>-<%- end %></p>
<%= in_place_editor 'edit_date',
:url => {:action => 'edit_in_place', :id => @work.id, :field => 'date'} %>
The if line in the date is to put a dash there if the work field is
empty, because if it's empty then ir doesn't become clickable because
there's nothing to click on. There are other ways to handle
this--dealing with it is a common question--but this'll do for now.
What's going on is I set an ID on an element and then tell
in_place_editor which ID I want to make editable. I tell it to use
the edit_in_place method in the works controller, and to pass in
params[:id] = @work.id and params[:field] = 'title' or 'date', the
names of the fields being edited. The new text that I edit and save
will be params[:value].
In the edit_in_place method I find the right work, change the right
field, save it, and send the text back so it'll display in the page.
This took me a little while to get right:
@work.send "#{params[:field]}=", params[:value]
What's going on with the send method is explained here:
http://www.softiesonrails.com/2007/8/15/ruby-101-methods-and-messages
and demonstrated here:
>> w = Work.find(1)
=> #<Work id: 1, created_at: "2008-11-28 15:01:51", updated_at: "2008-11-28 22:26:59", title: "Fantastics Beasts and Where to Find them", form: "[novel?]", date: nil, comment: nil>
>> w.title
=> "Fantastics Beasts and Where to Find them"
>> w.send 'title'
=> "Fantastics Beasts and Where to Find them"
>> t="title"
=> "title"
>> w.t
NoMethodError: undefined method `t' for #<Work:0x99cf2a8>
>> w.send(t)
=> "Fantastics Beasts and Where to Find them"
>> w.send 'title=', 'Fantastic Beasts and Stuff'
=> "Fantastic Beasts and Stuff"
>> w.title
=> "Fantastic Beasts and Stuff"
By passing in the field I want to change, I can use the one method to
change any Work attribute.
You can send a string to an object and it'll know to deal with it as
though you were calling a method of that name, but you can also use
.to_sym.
* 3 December 2008
Inspired by in_place_editor I thought that in_place_collection editor
would be the best way to handle changing works for an expression,
expressions for a manifestation, or manifestations for an item. I
stayed up to 3:30 AM one night on this and was quite pleased when I
got it working. The next day I explaned it to a couple of people and
realized it was the wrong way to do that job, but that the Ajax
list-popper-upper could be really useful in other places. I'll
explain what I did so it's here for later.
First, docs on the Scriptaculous function:
http://github.com/madrobby/scriptaculous/wikis/ajax-inplacecollectioneditor
There's no plugin that does the collection editor business in Rails
automatically, so it's the usual situation of things here and there on
blogs and mailing lists.
http://whynotwiki.com/Rails_/_In-place_editing
This archive of a post on the Pragmatic Programmers web site got me on
to some good stuff:
When Your In-Place Editor Needs a Drop-Down
http://web.archive.org/web/*/http://fora.pragprog.com/rails-recipes/write-your-own/post/223
This has a bit of code:
Rails in place editing plugin w/ selection
http://thetacom.info/2008/03/21/rails-in-place-editing-plugin-w-selection/
And links to some code changes to the plug-in I installed a few days
ago:
http://pastie.org/169443
I added this to the bottom of
vendor/plugins/in_place_editing/lib/in_place_macros_helper.rb
# ----- Added to make in_place_collection_editor work
# See NOTES for more.
def in_place_collection_editor(field_id, options = {})
function = "new Ajax.InPlaceCollectionEditor("
function << "'#{field_id}', "
function << "'#{url_for(options[:url])}'"
if protect_against_forgery?
options[:with] ||= "Form.serialize(form)"
options[:with] += " + '&authenticity_token=' + encodeURIComponent('#{form_authenticity_token}')"
end
js_options = {}
js_options['collection'] = %(#{options[:collection]})
js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
js_options['loadingText'] = %('#{options[:loading_text]}') if options[:loading_text]
js_options['savingText'] = %('#{options[:saving_text]}') if options[:saving_text]
js_options['rows'] = options[:rows] if options[:rows]
js_options['cols'] = options[:cols] if options[:cols]
js_options['size'] = options[:size] if options[:size]
js_options['externalControl'] = "'#{options[:external_control]}'" if options[:external_control]
js_options['loadTextURL'] = "'#{url_for(options[:load_text_url])}'" if options[:load_text_url]
js_options['ajaxOptions'] = options[:options] if options[:options]
js_options['htmlResponse'] = !options[:script] if options[:script]
js_options['callback'] = "function(form) { return #{options[:with]} }" if options[:with]
js_options['clickToEditText'] = %('#{options[:click_to_edit_text]}') if options[:click_to_edit_text]
js_options['textBetweenControls'] = %('#{options[:text_between_controls]}') if options[:text_between_controls]
function << (', ' + options_for_javascript(js_options)) unless js_options.empty?
function << ')'
javascript_tag(function)
end
# Renders the value of the specified object and method with in-place
# select capabilities.
def in_place_editor_select_field(object, method, tag_options = {}, in_place_editor_options = {})
tag = ::ActionView::Helpers::InstanceTag.new(object, method, self)
tag_options = {:tag => "span",
:id => "#{object}_#{method}_#{tag.object.id}_in_place_editor",
:class => "in_place_editor_field"}.merge!(tag_options)
in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{object}_#{method}", :id => tag.object.id })
tag.to_content_tag(tag_options.delete(:tag), tag_options) + in_place_collection_editor(tag_options[:id], in_place_editor_options)
end
With that in place, I put this in app/views/expression/show.html.erb:
<% list = Work.all.collect {|w| [w.id, w.anchor_text]}.to_json %>
<span id="expression_reifications_<%= @expression.id %>_in_place_editor"><%= @expression.work.anchor_text %></span>
<%= in_place_editor_select_field :expression, 'reifications', {}, {:collection => list}
%>
</p>
This new (ugly but temporary) method went into
expressions_controller.rb:
def set_expression_reifications
e = Expression.find(params[:id])
work_id = params[:value]
e.reifications = []
r = Reification.new(:expression_id => e.id, :work_id => work_id, :relation => relation)
e.reifications << r
render :text => Work.find(work_id).anchor_text
end
And that was all that was necessary. A # appeared, caused by
#<Work:0xb707f410> appearing in the HTML, but I couldn't figure out
where it was coming from.
After getting all that working, and talking about it to a couple of
people, I realized it was the wrong way to go about it. I got the
task working in a RESTful way with a basic form instead, and it made
more sense and was easy to do.
I put this form in the expression show.html.erb:
<% @reification = @expression.reifications[0] %>
<% form_for @reification do |f| %>
<%= f.error_messages %>
<%= hidden_field_tag "expression_id", @expression.id %>
<%= hidden_field_tag "relation", @reification.relation %>
<%= submit_tag "Reassign to" %>
<%= select_tag "work_id",
Work.all.collect { |w| "<option value=\"#{w.id}\">#{w.anchor_text}</option>" }.to_s
%>
<% end %>
I made reifications_controller.rb and made an update method:
def update
@reification=Reification.find(params[:id])
@reification.work_id=params[:work_id]
@reification.expression_id=params[:expression_id]
@reification.relation = params[:relation] # TODO Unhide in form
respond_to do |format|
if @reification.save
flash[:notice] = 'Reification was successfully updated.'
format.html { redirect_to(Expression.find(@reification.expression_id)) }
format.xml { render :xml => @reification, :status => :created, :location => @reification }
else
flash[:notice] = 'Could not update reification.'
format.html { redirect_to(Expression.find(@reification.expression_id)) }
format.xml { render :xml => @reification.errors, :status => :unprocessable_entity }
end
end
end
I'm not sure all the XML stuff works, but you can reassign an
expression to a different work, and you're directed back to the
expression's page when you're done. The Reification model will
eventually get a delete method. When I get Embodiment going between
Expression and Manifestation it'll have all the methods because that's
many-to-many.
* 12 February 2009
Moved to GitHub. Easy to set up! I had a problem getting SSH configured
but their guides made it clear what I'd done wrong.
http://github.com/wdenton/openfrbr/tree/master
* 16 February 2009
User authentication is confusing in Rails, partly because of the
mish-mash of outdated stuff online, acts_as_authenticated and
restful_authentication are out, it seems, and these are in:
Clearance: http://github.com/thoughtbot/clearance/tree/master
Authlogic: http://github.com/binarylogic/authlogic/tree/master
All I want is something to prevent spam, and a one account system where
I tell the username and password would do that. Or a basic CAPTCHA or
one of those "What is 2+2?" tests. Something to look at later.
* 23 February 2009
Wanted to work on adding RDF representations of things, here at
Code4Lib. Ended up in a whole mess of problems.
First ran into some strange errors when I ran ./script/server, which
seemed to have to do with an upgrade to Rails 2.2. Decided to upgrade
OpenFRBR to Rails 2.2.
http://wiki.rubyonrails.org/rails/pages/HowtoUpgrade
$ sudo gem update rails
$ sudo gem update rake
$ sudo gem update activerecord
$ sudo gem update activesupport
$ sudo gem update actionpack
$ sudo gem update actionmailer
$ sudo gem update activeresource -y or possibly sudo gem install activeresource -y
I ran into some errors like this:
Installing ri documentation for actionmailer-2.2.2...
/usr/lib/ruby/1.8/rdoc/rdoc.rb:101:in `error': (RDoc::RDocError)
Directory /var/lib/gems/1.8/doc/actionmailer-2.2.2/ri already exists, but it looks like it
isn't an RDoc directory. Because RDoc doesn't want to risk
destroying any of your existing files, you'll need to
specify a different output directory name (using the
--op <dir> option).
I ran
# gem uninstall actionmailer
and told it I wanted to remove 2.2.2. Then I did the install again,
and it worked.
Edited config/environment.rb and set RAILS_GEM_VERSION = '2.2.2'
$ rake rails:update in the main directory.
$ ./script/server
Rails requires RubyGems >= 1.3.1 (you have 0.9.4). Please `gem update --system` and try again.
# gem update --system
Then /usr/bin/gem1.8 appeared and all of my previously installed gems
were gone. So I had to install Rails and the other gems again. My
quick "I'll add RDF formatting to OpenFRBR" had turned into a big
mess.
# rm /usr/bin/gem
# ln -s /usr/bin/gem1.8 /usr/bin/gem
# gem install rails
# gem install has_many_polymorphs
But then
$ ./script/server
=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
=> Rails 2.2.2 application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with development environment...
** Rails loaded.
** Loading any Rails specific GemPlugins
Exiting
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- /usr/lib/ruby/gems/1.8/gems/activerdf-1.6.11/lib/activerdf/init.rb (MissingSourceFile)
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:153:in `require'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:521:in `new_constants_in'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:153:in `require'
from /usr/lib/ruby/1.8/gem_plugin.rb:134:in `load'
from /usr/local/lib/site_ruby/1.8/rubygems/source_index.rb:203:in `each'
from /usr/local/lib/site_ruby/1.8/rubygems/source_index.rb:203:in `each'
from /usr/lib/ruby/1.8/gem_plugin.rb:112:in `load'
... 21 levels...
from /usr/lib/ruby/gems/1.8/gems/rails-2.2.2/lib/commands/server.rb:49
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from ./script/server:3
/usr/local/lib/site_ruby/1.8/rubygems/ doesn't exist any more.
Forced reinstallation of Mongrel but that didn't help.
(Interlude of confusion where I uninstalled and reinstalled lots of
stuff and had no idea what I was doing.)
Ended up ignoring the warning about gem going out of date in March and
reinstalling a ton of Ruby-related stuff. Had to add a line to the
gem script for it to work:
require 'rubygems/gem_runner'
Had to reinstall libopenssl_ruby. At some point Webrick would run.
Phew! Then had to reinstall all sqlite3 stuff. Reinstalled Mongrel.
rake db:drop; rake db:migrate; rake db:fixtures:load
It worked! Hours of frustration but somehow it works. I'm now
running Rails 2.2.2. Don't look forward to upgrading on my other box!
Ugh.
* 11 March 2009
Had Rails 2.2 already installed on jenkins, so that wasn't a problem.
When I ran ./script/server I got
/usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- iconv (LoadError)
Installing /usr/ports/converters/ruby-iconv/, which is
ruby18-iconv-1.8.7.72 right now, fixed that.
Then I got some error about activerdf! Cripes.
# gem uninstall activerdf
fixed that. I'll figure it out if I need to reinstall it.
* 26 April 2009
Added Capistrano support (config/deploy.rb) so I can run
$ cap deploy:update
after updating the source code, and a process on anvil will check out
the latest version and get it in place there.
Then I should be able to run
$ cap deploy:restart
and it'll run the script/spin script I made, except I get some sort of
error. Need to get that figured out. Anyway, now getting the source
in place is easy.
Capistrano
http://www.capify.org/
Capistrano: Basics
http://www.capify.org/getting-started/basics/
Capistrano: From the beginning
http://www.capify.org/getting-started/from-the-beginning/
The bad news is that because OpenFRBR was completely open to edits by
anyone, it was getting attacked by spammers constantly, to the point
where it was bringing anvil down to a crawl and Ross Singer had to
disable it. Before I get it back up it needs some kind of anti-spam
protection, with account logins or CAPTCHA or something.
Ross is interested in working on this and moving into a Semantic Web
design. We're going to try to get it working using the Talis Platform
as the place to store the triples, using the Pho gem that Talis's
Leigh Dodds wrote so Ruby people can talk to it. That will help, I
hope, work around the problem that there isn't great RDF support for
Ruby.
* 28 April 2009
Added AuthLogic support, which will mean the spam can be stopped.
http://github.com/binarylogic/authlogic/tree/master
I started a new branch:
$ git branch authlogic
$ git checkout authlogic
And then did the work set out here, in some great docs:
http://github.com/binarylogic/authlogic_example/tree/master
It all went fine, but then I got a bit confused about how to push the
branch to the remote repository at GitHub.
Some links:
http://www.zorched.net/2008/04/14/start-a-new-branch-on-your-remote-git-repository/
http://djwonk.com/blog/2009/04/18/tracking-remote-git-branches/
Turns out all I had to do was
$ git push origin authlogic
Tracking multiple branches would probably mean I'd want to edit my
config file, but I just want this there while I get this working; when
I know it works I'll merge it and remove the branch, so it's no big
thing.
* 22 September 2009
Trying things on my new laptop which has Rails 2.2.3.
Had to rename application.rb to application_controller.rb.
When running rake db:migrate I got
rake aborted!
undefined method `reenable' for <Rake::Task db:schema:dump => [environment]>:Rake::Task
$ sudo apt-get remove rake
$ sudo gem install rake
$ rake db:migrate
-bash: /usr/bin/rake: No such file or directory
$ # sigh
$ sudo ln -s /var/lib/gems/1.8/bin/rake /usr/bin/rake
Then all these worked:
$ rake db:drop
$ rake db:migrate
$ rake db:fixtures:load
and I was back in action.
... though not having looked at this in a while I had no idea where I
was at with the authlogic branch.
* 29 September 2009
Got confused about which branch I'm on. Renamed application.rb and
took out the RAILS_VERSION specification in config/environment.rb and
now the authlogic branch works OK. I'll get this finished off and
then merge it back, which will be a good test of my memory.
Fiddled with various things and watched Railscast #160 which was a
great help in getting all of this working:
http://railscasts.com/episodes/160-authlogic
Got it so that user can create an account, log in, edit the account,
and log out.
Then added lines this like to all the controllers:
before_filter :require_user, :only => [:create, :new, :edit, :update, :destroy]
and now it requires the user to be logged in before doing any of those
actions.
TODO: Redirect people back to the page they want to edit after they've
successfully logged in.
TODO: Add OpenID. See
http://railscasts.com/episodes/170-openid-with-authlogic
for example.
Note to self: installed Capistrano on my new Ubuntu box with
sudo gem install capistrano
and then had to add /var/lib/gems/1.8/bin to my $PATH
* 30 September 2009
At Hackfest I finally figured out how to use the Redland Ruby bindings
to grok RDF.
TODO: Get list out of that Google Doc.
TODO: Try Ross Singer's http://github.com/rsinger/RDFObjects
* 3 October 2009
Couldn't get the Capsitrano deployment working right last night so
I'll try doing it by hand.
$ git tag v2.2
$ git archive --prefix=openfrbr-2.2/ v2.2 | gzip > openfrbr-2.2.tar.gz
I had to symlink application_controller.rb to application.rb, because
Rails is older on anvil, but after that it worked. This isn't using
Capistrano, which I'll try to get working next.
Later, cleaned up and simplified all of the index.html.erb views so
that they have less on them, and made everything valid HTML. Changed
the doctype so it's HTML5, too, just for fun.
cap deploy was giving me this error:
* executing "sudo -p 'sudo password: ' -u wtd /var/www/openfrbr.org/current/script/process/reaper"
servers: ["anvil.lisforge.net"]
[anvil.lisforge.net] executing command
*** [err :: anvil.lisforge.net] /usr/local/lib/site_ruby/1.8/rubygems.rb:149:in `activate': can't activate rails (= 2.1.0, runtime), already activated rails-2.3.2 (
*** [err :: anvil.lisforge.net] Gem::Exception)
*** [err :: anvil.lisforge.net] from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:35:in `require'
*** [err :: anvil.lisforge.net] from /var/www/openfrbr.org/current/script/process/reaper:3
command finished
failed: "sh -c 'sudo -p '\\''sudo password: '\\'' -u wtd /var/www/openfrbr.org/current/script/process/reaper'" on anvil.lisforge.net
I like Rails but man, it's filled with annoying problems like this
when you upgrade or don't upgrade. Why is it filled with such small
annoyances that turn out to be so hard to find fixes for?
Ran
$ ./script/plugin install git://github.com/rails/irs_process_scripts.git
which was recommended on a couple of mailing lists I, but that didn't
do the job. Maybe it's because Rails on my box and anvil are out of
synch. I'll upgrade anvil later.
* 4 October 2009
Set up Embodiment model, which is a many-to-many relation between
Expression and Manifestation.
$ ./script/generate model embodiment
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/embodiment.rb
create test/unit/embodiment_test.rb
create test/fixtures/embodiments.yml
exists db/migrate
create db/migrate/20091004230419_create_embodiments.rb
Edited the models. Edited the migration. Added some test data.
Removed the expression_id line from manifestations.yml test data.
$ rake db:migrate
$ rake db:fixtures:load
Edited the Expression and Manifestation show methods and that about
did it.
Noticed that I have the Reification model in place but don't use the
many-to-manyness of it, so I added that to the TODO list.
* 26 January 2010
Made a new work. Tried to add an expression. Error! In
works/show.html.erb I have
<%= link_to "Add expression for this work",
new_expression_path(:work_id => @work.id) %>
But I don't want to make a new expression, I want to make a new
reification. I didn't notice this before because I'd been doing all
of this in text fixtures, by hand, and not using the web interface.
What is the best way to handle this RESTfully, when I have a Work and
I want to create an Expression and the Reification that connects it to
the Work? Should I get all the necessary information at once and then
do both creations and then redirect the user to the new Expression
page? I'm not sure what the theoretically ideal way of doing this is.
* 28 January 2010
On further investigation I found all this was actually set up right,
but I'd forgotten how I'd done it. There was a small bug, which I
fixed. I did @expression = Expression.new and then tried to read
@expression.id, but that didn't work because the new Expression hadn't
been saved. I moved a line of code to after I do the save and then it
worked. Got the tip from a guy in #rubyonrails---first time I asked a
question there and got an instant answer.
* 29 January 2010
Fixed up some bugs in the display of Manifestations and Items.
Added pluralize_without_count helper which is like pluralize but
doesn't insert the number before, it just gives the proper plural if
necessary.
Removed this from the Expression view, because an Expression can only
have one Work, so this reassignment isn't needed. Might be useful
when I make it so one can reassign a Manifesatation to different
Embodiments and Expressions, though.
<% @reification = @expression.reifications[0] %>
<% form_for @reification do |f| %>
<%= f.error_messages %>
<%= hidden_field_tag "expression_id", @expression.id %>
<%= hidden_field_tag "relation", @reification.relation %>
<%= submit_tag "Reassign to" %>
<%= select_tag "work_id",
Work.all.sort.collect { |w| "<option value=\"#{w.id}\">#{w.anchor_text}</option>" }.to_s
%>
<% end %>
$ ./script/generate model Exemplification
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/exemplification.rb
create test/unit/exemplification_test.rb
create test/fixtures/exemplifications.yml
exists db/migrate
create db/migrate/20100129204001_create_exemplifications.rb
Edited the models. Edited the migration, removed manifestation_id
from items. Edited test data to fit new model, added new test data.
Changed anchor_text method on manifestations. Couple of other things
here and there before it was all working, but now there's an
Exemplification model with a relation attribute, so that's ready to be
expanded when necessary. And Manifestation to Item is one to many.
* 18 February 2010
Been working on some small scripts to gather information from
LibraryThing, OCLC, and the Open Library. Then I'll assemble the bits
into an order so that I can:
- enter an ISBN
- get a list of ISBNs of related manifestations (thingISBN, xISBN)
- get information about each one:
- the work it embodies (LT What Work)
- subjects of the work
- characters, events, places in the work
- canonical title of work
- author
- publisher and publishing information
- language
- title of manifestation
- figure out what expression it might represent
- guess by language if not original language
- assume for now (since this is Harry Potter) that the original
language of all works is English
- construct the Work / Expression / Manifestation relations and inject
into the database
* 21 February 2010
Some notes on what's in Freebase that could be used.
Freebase URLs for ISBNs:
Page about an ISBN it knows about (for Deathly Hallows):
http://www.freebase.com/view/soft/isbn/9780545010221
JSON: http://www.freebase.com/experimental/topic/standard/soft/isbn/9780545010221
RDF: http://rdf.freebase.com/rdf/soft.isbn.9780545010221
This is a page about the Manifestation, which has information about
the title, binding, number of pages, much else possibly:
http://www.freebase.com/view/soft/isbn/9780545010221/best
JSON: http://www.freebase.com/experimental/topic/standard/soft/isbn/9780545010221/best
RDF: http://rdf.freebase.com/rdf/soft.isbn.9780545010221.best
It says it is an "Edition of"
http://www.freebase.com/view/en/harry_potter_7
Harry Potter and the Deathly Hallows:
http://www.freebase.com/view/en/harry_potter_7
This is a Book, with a lot of Editions, and it knows Characters,
Subjects, the Series, Genre, Author, and more.
Details in JSON:
http://www.freebase.com/experimental/topic/standard/en/harry_potter_7
Details in RDF:
http://rdf.freebase.com/rdf/en.harry_potter_7
* 22 February 2010
$ ./script/generate migration add_libraryThingId_to_works libraryThingId:int
exists db/migrate
create db/migrate/20100222205327_add_library_thing_id_to_works.rb
* 23 February 2010
OCLC work ID is available through the xOCLCNUM service, but not
xISBN. Would have to make two calls to get it and match up ISBN to
work ID, but that's OK. Given one I can get others and LCCN and OCLCnum. But
given a work ID I can't get back work-level information from these
APIs.
xOCLCNUM API: http://xisbn.worldcat.org/xisbnadmin/xoclcnum/api.htm
"When the request is about a known OCLC workid, the xOCLCNUM service
endpoint URL is: http://xisbn.worldcat.org/webservices/xid/owi/"
Example:
http://xisbn.worldcat.org/webservices/xid/owi/owi718389?method=getEditions&format=xml&fl=lccn,isbn,owi
* 1 May 2010
I want to make it so that on the Work view you can drag and drop Manifestations between Expressions to correct mistaken relationships. I can't get it completely working, though. I think it's because of the containers in the HTML lists, or the :onUpdate setting, or something.
I had this in the Work _tree.html.erb partial as a test (dragging
cities between provinces):
<ul id="works">
<li> Saskatchewan
<ul id="work_1">
<li id="e_1"> Regina</li>
<li id="e_2"> Saskatoon</li>
</ul>
</li>
<li> Manitoba
<ul id="work_2">
<li id="e_3"> Brandon</li>
<li id="e_4"> Winnipeg</li>
</ul>
</li>
<li> Ontario
<ul id="work_3">
<li id="e_5"> Toronto</li>
<li id="e_6"> Peterborough</li>
</ul>
</li>
<li> Quebec
<ul id="work_4">
<li id="e_7"> Montreal</li>
<li id="e_8"> Sherbrooke</li>
</ul>
</li>
</ul>
<% containment = [:work_1, :work_2, :work_3, :work_4] %>
<%= sortable_element("work_1",
# :tree => true,
:dropOnEmpty => true,
:containment => containment,
:url => { :action => "reorder_expression" })
%>
<%= sortable_element("work_2",
# :tree => true,
:dropOnEmpty => true,
:containment => containment,
:url => { :action => "reorder_expression" })
%>
<%= sortable_element("work_3",
# :tree => true,
:dropOnEmpty => true,
:containment => containment,
:url => { :action => "reorder_expression" })
%>
<%= sortable_element("work_4",
# :tree => true,
:dropOnEmpty => true,
:containment => containment,
:url => { :action => "reorder_expression" })
%>
In works_controller.rb I had
def reorder_expression
# Rails.logger.info("PARAMS: #{params.inspect}")
render :nothing => true
end
If you drag a city (Regina 1) to BETWEEN two other cities (Peterborough 5,
Toronto 6) in another province, you get this:
Parameters: {"action"=>"reorder_expression",
"authenticity_token"=>"f8hpA92uK6AuO4pwUMAxyeq2nxexXd99K8IzSuodExU=",
"work_3"=>[{"5"=>nil}, {"1"=>nil}, {"6"=>nil}], "controller"=>"works"}
That tells you that cities 5, 1, and 6 (in that order) are now in
province 3. That's fine. With that you know enough to reassociate
city 1 with province 3.
But if you drag city 1 to the top of the list in some other province, you get this:
Parameters: {"action"=>"reorder_expression",
"authenticity_token"=>"f8hpA92uK6AuO4pwUMAxyeq2nxexXd99K8IzSuodExU=",
"work_1"=>[{"2"=>nil}], "controller"=>"works"}
That tells you that city 2 is in province 1, from which you can deduce
that any other cities (1) you knew were in province 1 are gone, but
you don't know to where.
I fiddled with this for a while without much success and then decided
to leave it for now, and ask.
* 27 May 2010
Got Capistrano deployment working, thanks to
http://www.modrails.com/documentation/Users%20guide.html#capistrano
and by looking at Ross Singer's example here:
http://github.com/rsinger/openlcsh/blob/master/config/deploy.rb.tmp
Now I can just run
$ cap deploy
and it installs the latest version from Github onto anvil.
There was a problem, though: the production database kept being
recreated from scratch each time. To fix that, I put it in a shared
directory where it will remain unchanged over deployments. I edited
config/database.yml to say:
production:
adapter: sqlite3
database: /var/www/openfrbr.org/shared/db/production.sqlite3
timeout: 5000
After redeploying (and restarting Apache) it worked fine.
Trying out RDF.rb
$ sudo gem install rdf rdf-raptor rdf-json rdf-trix
* 12 July 2010
Left off with these in my browser tabs:
http://github.com/bendiken/rdf/blob/master/README.md
http://github.com/rsinger/RDFObjects
http://rdf.freebase.com/rdf/en.harry_potter_and_the_goblet_of_fire
http://dbpedia.org/data/The_Maltese_Falcon.rdf
http://openlibrary.org/books/OL6026352M.rdf
* 27 March 2011
Moved to Heroku, because I need to move off of anvil.lisforge.net
Set up an account and followed the instructions about setting up
another git remote repository and so on. Had some problems getting
the database uploaded but when I installed the taps gem then heroku
db:push worked nicely. As usual there was some stumbling through the
instructions and Stack Overflow posts, and now that I've done it all,
it works, but I can't remember everything I did.
One thing I had to do was configure database.yml with connection
details for the production database, but I didn't want these to end up
in Github. Here's what I did (from
http://devcenter.heroku.com/articles/database and some blog posts):
$ heroku console
>> ENV['DATABASE_URL']
=> "postgres://username:password@hostname/database"
This told me the information I needed. I picked out the details, then:
heroku add:config PROD_DB_USERNAME=username PROD_DB_HOST=hostname PROD_DB_DATABASE=database PROD_DB_PASSWORD=password
Then I edited database.yml to use those environment variables:
production:
encoding: unicode
adapter: postgresql
username: "#{ENV['PROD_DB_USERNAME']}"
port: 5432
host: "#{ENV['PROD_DB_HOST'}"
database: "#{ENV['PROD_DB_DATABASE']}"
password: "#{ENV['PROD_DB_PASSWORD']}"
And that did it. I had to use git rebase to hide a commit from the
world, and then git push -f it to get the rebased stuff up to Heroku
and Github.
But now it's at http://openfrbr.heroku.com/