Lesson: Generate Rails Scaffolding for Creating and Editing - samvera/hydra-works GitHub Wiki
- Understand the difference between unique (single-value) and multi-valued metadata fields
- Learn how to make modifications to the views which support CRUD (Create, Read, Update, Delete) on objects in your repo
This lesson walks you through modifying setting up scaffolding for the models. Next it shows you how to the modify the "Author" field in your bibliographic_work model to make it either single- or multi-valued. The lesson then walks through the changes necessary to modify views to read, create, and edit your updated metadata model.
We will use the Rails scaffold generator to set up the routes, Controller and Views we need in order to CRUD for the models we created.
NOTE: If you are not familiar with the ideas of Controllers, Views and routes, or aren't familiar with the Rails scaffold generator, go through the Railsbridge Curriculum and then come back to this lesson. You might also want to read the Getting Started with Rails guide.
Tell the generator to build scaffolding for the each of the models we created in Lesson: Define models with Hydra-Works and make them have attributes for each property we defined in the models. Execute the following commands in the terminal window.
rails generate scaffold Collection title:string
rails generate scaffold BibliographicWork title:string author:string abstract
rails generate scaffold BibliographicFileSet title:string
rails generate scaffold PageFileSet page_number:integer
Warning: Type n
when asked whether you want to overwrite app/models/collection.rb
, app/models/bibliographic_work.rb
, app/models/bibliographic_file_set.rb
, and app/models/page_file_set.rb
. If you simply hit return, the default is Y
. If you accidentally hit return, you can copy the model code back from the final version of it in Lesson: Explore Objects in Fedora and Solr (Step 6) or Lesson: Add pages to a bibliographic work.
Rails assumes that you're using an ActiveRecord based model stored in a SQL databases and creates the necessary database migrations to setup a table to store bibliographic_works. We're using Fedora, not SQL, to persist our bibliographic_work objects, so you don't need this database migration. You can delete it with the git clean
command.
git clean -df db
You'll see output something like this:
Removing db/migrate/20150925124032_create_collections.rb
Removing db/migrate/20150925124045_create_bibliographic_works.rb
Removing db/migrate/20150925124124_create_bibliographic_file_sets.rb
Removing db/migrate/20150925124153_create_page_file_sets.rb
Run the rails server
and visit pages...
- http://localhost:3000/collections
- http://localhost:3000/bibliographic_works
- http://localhost:3000/bibliographic_file_sets
- http://localhost:3000/page_file_sets
Explore the pages for creating, editing and showing for each model. You may want to do the next step before doing a lot of exploring.
The default stylesheet has all the fields and the action buttons at the end of the set of fields run together. Not very pretty. Let's make a quick, albeit inelegant, css update just to make things readable.
Edit app/assets/stylesheets/application.css
and append the following to the end of the file.
td {
padding: 10px;
border: gray 1px solid;
}
NOTE: Blacklight uses Bootstrap for controlling its styles. If you want to learn how to customize styles and code for Blacklight, see the Customizing Blacklight Tutorial presented at Hydra Connect 2015.
Start the rails server
up and take another peak at one of the index views listed at the start of this previous step. At least things are readable now if not pretty.
Commit your work...
git add .
git commit -m "Ran model scaffold generators"
Open app/models/bibliographic_work.rb
and edit the multiple setting to be 'true':
property :author, predicate: ::RDF::DC.creator, multiple: true do |index|
Now you need to tell your hydra application how to display multivalued fields in the 'show' (Display) view for this model.
In app/views/bibliographic_works/show.html.erb
find the lines that display the author field.
<p>
<strong>Author:</strong>
<%= @bibliographic_work.author %>
</p>
Change these lines to match below. This will iterate over the values returned by @bibliographic_work.author
and put them in a list
<p>
<strong>Author(s):</strong>
<ul>
<% @bibliographic_work.author.each do |author|%>
<li><%= author %></li>
<% end %>
</ul>
</p>
Save the file and refresh the Show view for a bibliographic_work. Now authors show up as a list of values.
NOTE: The display of results of a search is not affected, only the view which you can reach from http://localhost:3000/bibliographic_works.
The _form
partial defines the guts of the form that is used in both the new
(Create) view and the edit
(Update) view. That makes our lives simpler because we only have to update that one file to fix both pages!
In app/views/bibliographic_works/_form.html.erb
find the lines that display the author field.
<div class="field">
<%= f.label :author %><br />
<%= f.text_field :author %>
</div>
Replace those lines with something that iterates over the values from @bibliographic_work.author
and displays a text_field tag for each of them.
NOTE: The @name attribute will be set to "bibliographic_work[author][]". The trailing []
in the name tells Rails that this is a multivalued field that should be parsed as an Array.
<%= f.label :author, "Authors" %>
<% @bibliographic_work.author.each do |author| %>
<div class="field">
<%= text_field_tag "bibliographic_work[author][]", author %>
</div>
<% end %>
This handles displaying existing author values, but what about setting the author value in the first place? If there are no values in the array, no fields are going to be displayed. As a stop-gap, we can add a conditional clause that displays an empty text_field after the existing authors are displayed.
<%= f.label :author, "Authors" %>
<% @bibliographic_work.author.each do |author| %>
<div class="field">
<%= text_field_tag "bibliographic_work[author][]", author %>
</div>
<% end %>
<div class="field">
<%= text_field_tag "bibliographic_work[author][]", nil %>
</div>
Update the bibliographic_work_params
method in app/controllers/bibliographic_works_controller.rb
from
def bibliographic_work_params
params.require(:bibliographic_work).permit(:title, :author, :abstract)
end
to
def bibliographic_work_params
params.require(:bibliographic_work).permit(:title, :author=>[], :abstract=>'')
end
Now every time you save the form you'll get one more additional author. However you get this author even if you haven't filled any value in. Let's update the BibliographWorksController to not save authors that don't have names.
Edit app/controllers/bibliographic_works_controller.rb
and modify the #update
method to match the following.
def update
@bibliographic_work.attributes = bibliographic_work_params
@bibliographic_work.author = params[:bibliographic_work][:author].select { |a| a.present? }
respond_to do |format|
if @bibliographic_work.save
format.html { redirect_to @bibliographic_work, notice: 'Bibliographic Work was successfully updated.' }
format.json { render :show, status: :ok, location: @bibliographic_work }
else
format.html { render :edit }
format.json { render json: @bibliographic_work.errors, status: :unprocessable_entity }
end
end
end
NOTE: This still doesn't cover the case where you want to add more than one additional Author to a Bibliographic Work. That goes beyond the scope of this tutorial because it requires javascript (or a multi-page workflow).
Start up the rails console
and run the following commands to add a second author to our bibliographic_work.
bw = BibliographicWork.find('work-1')
bw.author += ['Some other author']
bw.save
Start up the rails server
and visit http://localhost:3000/bibliographic_works again and see that multiple authors show up and that you can edit them.
Commit your work...
git add .
git commit -m "Handling multivalued author fields"
Based on the concepts in steps 1-7, determine whether you want 'Title' to display as a single or multi-valued field and make appropriate edits to the 'show' view and '_form' partial on your own.
In general, you might not want to build all the views to edit your metadata by hand. The hydra-editor gem is used by many hydra adopters as a way to handle providing metadata editing forms without having to hand-code for each field. It also provides javascript support for repeating fields like our author field above so you can add multiple values to a single term without having to save each time.
Edit (or create) app/views/catalog/_home_text.html.erb
and set it to...
NOTE: If creating, you may need to create the catalog directory if it doesn't exist.
<h4>Browse...</h4>
<ul>
<li><%= link_to 'Collections', collections_path %></li>
<li><%= link_to 'Works', bibliographic_works_path %></li>
</ul>
NOTE: I'm choosing to only allow browsing at the collection and work level. The only way to access files is through the work. We'll set that up in a few steps. We could have decided that users always have to start at collections and drill down to works, in which case, we would only have Browse Collections.
NOTE: You might be interested in checking out the hydra-collections gem which provides utilities and controllers for working with collections.
Start up the rails server
and visit http://localhost:3000/ and see that you now have choices for browsing from the home page.
Commit your work...
git add .
git commit -m "Browsing from home page"
Let's update the show page for collections to list works in the displayed collection. Edit app/views/collections/show.html.erb
.
Add the following at the top of the file, just under the notice.
<h1>Collection</h1>
Add the following to the end to get the listing of works in the collection.
<hr>
<h5>Listing Bibliographic Works in this Collection</h5>
<table>
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Abstract</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @collection.works.each do |bibliographic_work| %>
<tr>
<td><%= bibliographic_work.title %></td>
<td><%= bibliographic_work.author %></td>
<td><%= bibliographic_work.abstract %></td>
<td><%= link_to 'Show', bibliographic_work %></td>
<td><%= link_to 'Edit', edit_bibliographic_work_path(bibliographic_work) %></td>
<td><%= link_to 'Destroy', bibliographic_work, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
NOTE: The code listed here is the same code in app/views/bibliographic_works/index.html.erb
with some slight wording and style changes. There are only two changes, 1) change the 'Listing' heading to a smaller heading tag and add that the works are 'in this Collection', 2) get the list of bibliographic_works from @collection.generic_works
.
Try it out...
- Start
rails server
- Goto
http://localhost:3000/
. You should see Browse... followed by Collections and Works. - Click
Collections
link under Browse... - Click
show
link next to Works by Edgar Allan Poe. You should see the title of the collection and the list of works in that collection.
Let's update the show page for works to list files in the displayed work. Edit app/views/bibliographic_works/show.html.erb
.
Add the following at the top of the file, just under the notice.
<h1>Bibliographic Work</h1>
Add the following to the end to get the listing of files in the work.
<hr>
<h5>Listing Files in this Bibliographic Work</h5>
<table>
<thead>
<tr>
<th>Title/Page Number</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @bibliographic_work.file_sets.each do |fs| %>
<% if fs.is_a? PageFileSet %>
<tr>
<td>page <%= fs.page_number %></td>
<td><%= link_to 'Show', fs %></td>
<td><%= link_to 'Edit', edit_page_file_set_path(fs) %></td>
<td><%= link_to 'Destroy', fs, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% else %>
<tr>
<td><%= fs.title %></td>
<td><%= link_to 'Show', fs %></td>
<td><%= link_to 'Edit', edit_bibliographic_file_set_path(fs) %></td>
<td><%= link_to 'Destroy', fs, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
NOTE: The code listed here is the basically the same code in app/views/bibliographic_file_sets/index.html.erb
merged with app/views/page_file_sets/index.html.erb
and including some slight wording and style changes. There are only two changes, 1) change the 'Listing' heading to a smaller heading tag and add that the files are 'in this Bibliographic Work', 2) get the list of bibliographic_file_sets and page_file_sets from @collection.generic_files
.
You may also want to add headers to the top of the show pages for the file models. Edit app/views/bibliographic_file_sets/show.html.erb
and add <h1>Bibliographic File</h1>
just under the notice. Edit app/views/page_file_sets/show.html.erb
and add <h1>Page File</h1>
just under the notice.
Try it out...
- Goto
http://localhost:3000/
. You should see Browse... followed by Collections and Works. - Click
Collections
link under Browse... - Click
show
link next to Works by by Edgar Allan Poe. You should see the title of the collection and the list of works in that collection. - Click the
show
link next to The Raven to see the list of files in that work. You should see the PDF file for the entire book and several page files. - Click the
show
link next to any of the files to see the show page for that file.
NOTE: We didn't make the Back
buttons smarter, so using the back button (on the page, not the browser's back button) from a work will show all works instead of returning to the collection's show page. Fixing this is beyond the scope of this tutorial.
Commit your work...
git add .
git commit -m "Drilling down from Collection to Work to File"
This completes the basic and bonus lessons for Dive into Hydra-Works. Great job! Go back to [Dive into Hydra-Works](Dive into Hydra#Next Steps) tutorial to learn about more steps you can take to expand your knowledge of Hydra-Works.