hydra 10.0.0 → 10.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/doc/Dive-into-Hydra.md +60 -57
  3. data/doc/For-Developers.md +0 -1
  4. data/doc/Hackfest-ideas.md +18 -0
  5. data/doc/Home.md +3 -1
  6. data/doc/Hydra-Recipes.md +5 -1
  7. data/doc/LDP-Containers-for-the-perplexed.md +119 -0
  8. data/doc/Lesson---Adding-attached-files.md +83 -0
  9. data/doc/Lesson---Build-a-Codex-model-with-XML.md +272 -0
  10. data/doc/Lesson---Build-a-book-model-with-RDF.md +250 -0
  11. data/doc/Lesson---Define-Relationships-Between-Objects.md +125 -0
  12. data/doc/{Lesson:-Generate-Rails-Scaffolding-for-Creating-and-Editing-Books.md → Lesson---Generate-Rails-Scaffolding-for-Creating-and-Editing-Books.md} +57 -50
  13. data/doc/Lesson---Start-FCRepo-and-Solr.md +76 -0
  14. data/doc/{Lesson:-add-the-Hydra-dependencies.md → Lesson---add-the-Hydra-dependencies.md} +3 -3
  15. data/doc/{Lesson:-generate-a-rails-application.md → Lesson---generate-a-rails-application.md} +78 -91
  16. data/doc/Lesson---make-blacklight-return-search-results.md +71 -0
  17. data/doc/Lesson---start-the-application-&-search-for-results.md +55 -0
  18. data/doc/Recipe:-Add-full-text-indexing-to-your-app.md +27 -0
  19. data/doc/Using-rdf:resource-within-your-models.md +43 -0
  20. data/hydra.gemspec +1 -1
  21. data/lib/hydra/version.rb +1 -1
  22. data/script/grant_revoke_gem_authority.rb +1 -1
  23. metadata +18 -17
  24. data/doc/Lesson:-Define-Relationships-Between-Objects.md +0 -131
  25. data/doc/Lesson:-Reading-Hydra-rightsMetadata-XML.md +0 -87
  26. data/doc/Lesson:-Use-Hydra-Access-Controls-to-Control-Access-to-Blacklight-show-Pages.md +0 -37
  27. data/doc/Lesson:-Using-Hydra-Access-Controls-and-CanCan-to-conditionally-render-part-of-a-page.md +0 -29
  28. data/doc/Lesson:-adding-content-datastreams.md +0 -57
  29. data/doc/Lesson:-build-a-book-model.md +0 -265
  30. data/doc/Lesson:-install-hydra-jetty.md +0 -148
  31. data/doc/Lesson:-make-blacklight-return-search-results.md +0 -69
  32. data/doc/Lesson:-set-up-your-Rails-Application-to-use-rspec.md +0 -41
  33. data/doc/Lesson:-start-the-application-&-search-for-results.md +0 -29
@@ -0,0 +1,250 @@
1
+ # Goals
2
+ * Start the Rails console and run code interactively in the console
3
+ * Define an ActiveFedora model class for Book objects
4
+ * Create Book objects that use your Book model class
5
+ * See how an object has been indexed into Solr
6
+ * See how and where objects and metadata are stored in Fedora
7
+ * Set indexers to manage how your metadata is indexed into Solr
8
+ * Re-index objects into Solr (update the Solr index based on any changes to an object)
9
+
10
+ # Explanation
11
+ We are going to create a 'Book' object. This object will have a set of RDF statements which will describe the properties of the book.
12
+
13
+ Once you've created an object and saved it in Fedora, you also want to be able to search for it in Solr. ActiveFedora makes it easy to get your metadata into Solr and manage if/when/how your metadata is indexed.
14
+
15
+ # Steps
16
+
17
+ ### Step 1: Create a Book model
18
+ Create a new file at `app/models/book.rb`. We'll paste in this code:
19
+
20
+ ```ruby
21
+ class Book < ActiveFedora::Base
22
+ property :title, predicate: ::RDF::Vocab::DC.title, multiple: false
23
+ property :author, predicate: ::RDF::Vocab::DC.creator, multiple: false
24
+ end
25
+ ```
26
+
27
+ This class extends from ActiveFedora::Base the abstract class which all ActiveFedora models should descend. The code says, create one property called title, which will be expressed in RDF using the Dublin Core RDF title predicate. Since multiple is set to false, there may only be one title on each Book. Author is similar, except it uses the Dublin Core RDF 'creator' predicate.
28
+
29
+ ### Step 2: Start the Rails console
30
+
31
+ Let's take a look at how this class works. We'll start the rails console by typing
32
+
33
+ ```bash
34
+ rails console
35
+ ```
36
+
37
+ (Or you can abbreviate this as ```rails c```.)
38
+
39
+ You should see something like `Loading development environment (Rails 4.2.0)`. Now you're in a "[REPL](http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)", or *interactive ruby console* that has all of your Rails application's code and configuration loaded.
40
+
41
+ ### Step 3: In the console, create a Book object
42
+
43
+ Let's create a new Book instance. I've shown the expected output after each command:
44
+
45
+ ```text
46
+ b = Book.new(id: 'test-1')
47
+ ActiveFedora: loading fedora config from /Users/justin/workspace/hydra-demo/config/fedora.yml
48
+ ActiveFedora: loading solr config from /Users/justin/workspace/hydra-demo/config/solr.yml
49
+ Attempted to init base path `dev`, but it already exists
50
+ => #<Book id: "test-1", title: nil, author: nil>
51
+ b.title = "Anna Karenina"
52
+ => "Anna Karenina"
53
+ b.author = "Tolstoy, Leo"
54
+ => "Tolstoy, Leo"
55
+ b.save
56
+ => true
57
+ puts b.resource.dump(:ttl)
58
+
59
+ <http://127.0.0.1:8984/rest/dev/test-1> a <http://www.w3.org/ns/ldp#RDFSource>,
60
+ <http://www.w3.org/ns/ldp#Container>,
61
+ <http://www.jcp.org/jcr/nt/1.0folder>,
62
+ <http://www.jcp.org/jcr/nt/1.0hierarchyNode>,
63
+ <http://www.jcp.org/jcr/nt/1.0base>,
64
+ <http://www.jcp.org/jcr/mix/1.0created>,
65
+ <http://fedora.info/definitions/v4/repository#Container>,
66
+ <http://fedora.info/definitions/v4/repository#Resource>,
67
+ <http://www.jcp.org/jcr/mix/1.0lastModified>,
68
+ <http://www.jcp.org/jcr/mix/1.0referenceable>;
69
+ <http://purl.org/dc/terms/title> "Anna Karenina";
70
+ <http://fedora.info/definitions/v4/repository#created> "2015-02-04T20:51:20.337Z"^^<http://www.w3.org/2001/XMLSchema#dateTime>;
71
+ <http://fedora.info/definitions/v4/repository#createdBy> "bypassAdmin";
72
+ <http://fedora.info/definitions/v4/repository#exportsAs> <http://127.0.0.1:8984/rest/dev/test-1/fcr:export?format=jcr/xml>;
73
+ <http://fedora.info/definitions/v4/repository#hasParent> <http://127.0.0.1:8984/rest/dev>;
74
+ <http://fedora.info/definitions/v4/repository#lastModified> "2015-02-04T20:51:20.337Z"^^<http://www.w3.org/2001/XMLSchema#dateTime>;
75
+ <http://fedora.info/definitions/v4/repository#lastModifiedBy> "bypassAdmin";
76
+ <http://fedora.info/definitions/v4/repository#mixinTypes> "fedora:Container",
77
+ "fedora:Resource";
78
+ <http://fedora.info/definitions/v4/repository#primaryType> "nt:folder";
79
+ <http://fedora.info/definitions/v4/repository#uuid> "3ed7ee9d-68c7-4b8e-b703-d8a547842aa3";
80
+ <http://fedora.info/definitions/v4/repository#writable> true;
81
+ <http://purl.org/dc/terms/creator> "Tolstoy, Leo";
82
+ <info:fedora/fedora-system:def/model#hasModel> "Book" .
83
+ => nil
84
+ ```
85
+
86
+ We've created a new Book object in the repository. You can see that it has a URI (Fedora Commons persistence identifier) of 'http://127.0.0.1:8984/rest/dev/test-1' (default username/password `fedoraAdmin/fedoraAdmin`). Because you set title and author you can see they are also stored in the RDF graph. We can use those values by calling their accessors.
87
+
88
+ ```text
89
+ b.author
90
+ => "Tolstoy, Leo"
91
+ b.title
92
+ => "Anna Karenina"
93
+ ```
94
+
95
+ Finally we'll get the Fedora URI and the abbreviated version (`id`) of the object:
96
+
97
+ ```text
98
+ b.uri
99
+ => "http://127.0.0.1:8984/rest/dev/test-1"
100
+ b.id
101
+ => "test-1"
102
+ ```
103
+ >
104
+ **Note:**
105
+ In the last command of the sequence, we printed the RDF graph for the Book object using Turtle (:ttl) syntax `b.resource.dump(:ttl)`. You can also print the graph using other RDF serialization formats such as N-Triples and JSON-LD
106
+ * `b.resource.dump(:ntriples)`
107
+ * `b.resource.dump(:jsonld)`
108
+ >
109
+
110
+ ### Step 4: See what your Book objects look like in Fedora and Solr
111
+
112
+ If we go to the URI of your object in a browser; i.e. [http://127.0.0.1:8984/rest/dev/test-1](http://127.0.0.1:8984/rest/dev/test-1), we should see what it looks like in Fedora. In the properties section you should see the attributes we set:
113
+
114
+ ![screen shot 2015-02-04 at 3 00 07 pm](https://cloud.githubusercontent.com/assets/92044/6049601/9962014c-ac7e-11e4-873e-2c01014ee699.png)
115
+
116
+ Let's also see that this book has been ingested into the Solr search index. If you followed the example and used test-1 for your book's ID, the solr page will be [[http://localhost:8983/solr/hydra-development/select?q=test-1]] - the generic pattern looks like this: [[http://localhost:8983/solr/hydra-development/select?q=XXX]] and replace the `XXX` with the `id` from your console session. The page should look like the sample below. Note that, at this point, the title and author have not been indexed in solr. You only get fields like `system_create_dtsi`, `system_modified_dtsi`, `id`, `object_profile_ssm`, and `has_model_ssim`. In the next step we will modify our Book model to add the book metadata to the solr document.
117
+
118
+ ```xml
119
+ <?xml version="1.0"?>
120
+ <response>
121
+ <lst name="responseHeader">
122
+ <int name="status">0</int>
123
+ <int name="QTime">27</int>
124
+ <lst name="params">
125
+ <str name="q">test-1</str>
126
+ </lst>
127
+ </lst>
128
+ <result name="response" numFound="1" start="0" maxScore="0.0010456265">
129
+ <doc>
130
+ <date name="system_create_dtsi">2015-02-04T20:51:20Z</date>
131
+ <date name="system_modified_dtsi">2015-02-04T20:59:59Z</date>
132
+ <str name="active_fedora_model_ssi">Book</str>
133
+ <arr name="has_model_ssim">
134
+ <str>Book</str>
135
+ </arr>
136
+ <str name="id">test-1</str>
137
+ <arr name="object_profile_ssm">
138
+ <str>
139
+ {"id":"test-1","title":"Anna Karenina","author":"Tolstoy, Leo"}
140
+ </str>
141
+ </arr>
142
+ <long name="_version_">1492211308347523072</long>
143
+ <date name="timestamp">2015-02-04T20:59:59.422Z</date>
144
+ <float name="score">0.0010456265</float>
145
+ </doc>
146
+ </result>
147
+ <lst name="facet_counts">
148
+ <lst name="facet_queries"/>
149
+ <lst name="facet_fields">
150
+ <lst name="active_fedora_model_ssi">
151
+ <int name="Book">1</int>
152
+ </lst>
153
+ <lst name="object_type_si"/>
154
+ </lst>
155
+ <lst name="facet_dates"/>
156
+ <lst name="facet_ranges"/>
157
+ <lst name="facet_intervals"/>
158
+ </lst>
159
+ <lst name="spellcheck">
160
+ <lst name="suggestions">
161
+ <bool name="correctlySpelled">true</bool>
162
+ </lst>
163
+ </lst>
164
+ </response>
165
+ ```
166
+
167
+ ### Step 5: See how your Book metadata are indexed into Solr
168
+
169
+ The to_solr method is what generates the solr document for your objects. To see the full solr document for the book we created, call
170
+
171
+ ```text
172
+ b.to_solr
173
+ => {"system_create_dtsi"=>"2015-02-04T20:51:20Z", "system_modified_dtsi"=>"2015-02-04T20:59:59Z", "active_fedora_model_ssi"=>"Book", "has_model_ssim"=>["Book"], :id=>"test-1", "object_profile_ssm"=>"{\"id\":\"test-1\",\"title\":\"Anna Karenina\",\"author\":\"Tolstoy, Leo\"}"}
174
+ ```
175
+
176
+ As you can see, the the author and title values are not indexed (excluding `object_profile_ssm`).
177
+
178
+ Once you're done, exit the console by typing ```exit```
179
+
180
+ ### Step 6: Change how your Book metadata are indexed into Solr
181
+
182
+ To make the Book model index the author and title fields, you need to reopen `app/models/book.rb` and change the class to look like this:
183
+
184
+ ```ruby
185
+ class Book < ActiveFedora::Base
186
+ property :title, predicate: ::RDF::Vocab::DC.title, multiple: false do |index|
187
+ index.as :stored_searchable
188
+ end
189
+ property :author, predicate: ::RDF::Vocab::DC.creator, multiple: false do |index|
190
+ index.as :stored_searchable
191
+ end
192
+ end
193
+ ```
194
+
195
+ **Note:** Because we have made changes to our Ruby code that we want to use, we need to restart the Rails console so that it will reload all of the code, including our latest changes.
196
+
197
+ Now, **restart the rails console** and we can load the object we previously created:
198
+
199
+ ```text
200
+ b = Book.find("test-1")
201
+ => #<Book id: "test-1", title: "Anna Karenina", author: "Tolstoy, Leo">
202
+ ```
203
+
204
+ Check and see that to_solr includes the title and author fields.
205
+
206
+ ```text
207
+ b.to_solr
208
+ => {"system_create_dtsi"=>"2015-02-04T20:51:20Z", "system_modified_dtsi"=>"2015-02-04T20:59:59Z", "active_fedora_model_ssi"=>"Book", "has_model_ssim"=>["Book"], :id=>"test-1", "object_profile_ssm"=>"{\"id\":\"test-1\",\"title\":\"Anna Karenina\",\"author\":\"Tolstoy, Leo\"}", "title_tesim"=>["Anna Karenina"], "author_tesim"=>["Tolstoy, Leo"]}
209
+ ```
210
+
211
+ Now when you call `.to_solr` on a Book it returns a solr document with fields named `title_tesim` and `author_tesim` that contain your title and author values. Those are the field names that we will add to Blacklight's queries in [[Lesson - Make Blacklight Return Search Results]].
212
+
213
+
214
+ ### Step 7: Re-index an object in Solr
215
+
216
+ Now we'll call the ```update_index``` method, which republishes the Solr document using the changes we've made.
217
+
218
+ ```text
219
+ b.update_index
220
+ => {"responseHeader"=>{"status"=>0, "QTime"=>25}}
221
+ ```
222
+
223
+ If you refresh the document result from solr ([[http://localhost:8983/solr/hydra-development/select?q=test-1]]) you should see that these fields have been added to the solr_document:
224
+
225
+ ```xml
226
+ <arr name="title_tesim">
227
+ <str>Anna Karenina</str>
228
+ </arr>
229
+ <arr name="author_tesim">
230
+ <str>Tolstoy, Leo</str>
231
+ </arr>
232
+ ```
233
+
234
+ **Aside:** The strange suffixes on the field names are provided by [solrizer](http://github.com/projecthydra/solrizer). You can read about them in the [solrizer documentaton](https://github.com/projecthydra/hydra-head/wiki/Solr-Schema). In short, the **_tesim** suffix tells Solr to treat the values as _**t**ext_ in the _**e**nglish_ language that should be _**s**tored_, _**i**ndexed_ and allowed to be _**m**ultivalued_. This _tesim suffix is a useful catch-all that gets your searches working predictably with minimal fuss. As you encounter cases where you need to index your content in more nuanced ways, there are ways to change these suffixes in order to achieve different results in Solr.
235
+
236
+ #### Why doesn't the Book show up in Blacklight?
237
+
238
+ Now your object is indexed properly, but it **won't show up in Blacklight's search results** until you've turned off access controls and added the appropriate fields to Blacklight's queries. We cover those in a subsequent lesson, but first we'll take a look at modeling a similar object using XML instead of RDF to store our metadata.
239
+
240
+ ### Step 8: Commit your changes
241
+
242
+ Now that we've got our model working, it's a great time to commit to git:
243
+
244
+ ```text
245
+ git add .
246
+ git commit -m "Create a book model using RDF"
247
+ ```
248
+
249
+ # Next Step
250
+ If you want to learn about modeling similar data in XML, proceed to [[Lesson - Build a codex model with XML]]; otherwise, go on directly to [[Lesson - Make Blacklight Return Search Results]] or return to the [[Dive into Hydra]] page.
@@ -0,0 +1,125 @@
1
+ # Goals
2
+ * Set up Models to represent relationships between different types of objects
3
+ * Create and modify relationships between objects
4
+
5
+ # Explanation
6
+ Now that we have created a model for Books, we will create a separate model for Pages and set a relationship between the two models indicating that any Book can have many Pages and Pages know what Book they belong to.
7
+
8
+ The syntax for declaring relationships between ActiveFedora models is the same syntax used by ActiveRecord. It's called the ActiveModel syntax. Underneath the hood, the two libraries implement relationships in very different ways. ActiveRecord uses relational database techniques like foreign keys and join tables while ActiveFedora puts this information into RDF relationships in the RELS-EXT datastreams of your Fedora objects. In both cases you can use the ActiveRecord methods to handle relationships.
9
+
10
+ # Steps
11
+
12
+ ### Step 1: Define a Page object model
13
+
14
+ Next we're going to add a Page model. We'll start by creating another simple model. This time open ```app/models/page.rb``` and add this content:
15
+
16
+ ```ruby
17
+ class Page < ActiveFedora::Base
18
+
19
+ property :number, predicate: ::RDF::URI.new('http://opaquenamespace.org/hydra/pageNumber'), multiple: false do |index|
20
+ index.as :stored_searchable
21
+ index.type :integer
22
+ end
23
+
24
+ property :text, predicate: ::RDF::URI.new('http://opaquenamespace.org/hydra/pageText'), multiple: false do |index|
25
+ index.as :stored_searchable
26
+ end
27
+
28
+ belongs_to :book, predicate: ActiveFedora::RDF::Fcrepo::RelsExt.isPartOf
29
+
30
+ end
31
+
32
+ ```
33
+
34
+ This is very similar to how our RDF-based Book class looks, we're just adding different attributes defined by different predicates. I.E. a Book has an _author_ and a _title_, while a Page has a _number_ and _text_.
35
+
36
+ The final line in the page example establishes a relationship between the page and book models. This works very similarly to the way you would define the relationship with ActiveRecord. In ActiveFedora, we have to add the :predicate attribute as well that describes the type of relationship.
37
+
38
+ If you completed the XML Codex lesson but not the Book lesson, change the next to last line to read `belongs_to :codex, ...` instead of `belongs_to :book, ...`. It's totally OK to mix models with both XML and RDF in this way! If you defined a Codex object model, but not a Book object model in previous lessons, you can complete the rest of this lesson by substituting `codex` anywhere the lesson says `book` (be sure to match capitalization, etc.). Hydra, especially Active Fedora, is designed to let you manipulate your digital repository objects as much like standard ruby objects as possible; this means that we try to abstract away the specific mechanisms used to persist metadata. So, once your objects are defined, the code you write to manipulate object with RDF metadata looks very much like the code you write to manipulate objects with XML metadata.
39
+
40
+ > ASIDE #1: It's generally best practice to use well-known RDF predicates when defining metadata terms, e.g. using DC.title for your title term. In some cases, however, RDF ontologies don't exist to express the concepts we want to track. Surprisingly, page numbers are in this group; therefore, we have used a predicate registry created by the community to define our page number predicate: <span>http:/</span>/opaquenamespace.org/hydra/pageNumber.
41
+
42
+ > ASIDE #2: We are explicitly assigning a type for the page number index. This lets us control our indexing and searching to use integer sorting (e.g. 1,2,3,4,...9,10,11) instead of the default lexical sorting use for strings (e.g. '1','10','11','2','21','27','5','6','60'). By default, Hydra and Active Fedora assume all metadata are text values. You can find additional information on the available indexing options in the [Solrizer ReadME](https://github.com/projecthydra/solrizer) and in the [solrizer code that defines the default indexing strategies](https://github.com/projecthydra/solrizer/blob/master/lib/solrizer/default_descriptors.rb)
43
+
44
+ ### Step 2: Make Books aware of their Pages
45
+
46
+ Let's edit the Book class in ```app/models/book.rb``` and add the other half of the relationship:
47
+
48
+ ```ruby
49
+ # within app/models/book.rb
50
+ has_many :pages
51
+ ```
52
+
53
+ ### Step 3: In the Console, manipulate relationships between Book and Page objects
54
+
55
+ Save your changes and then reopen the rails console. Now we ought to be able to create some associations.
56
+
57
+ ```ruby
58
+ b = Book.find("test-1")
59
+ => #<Book id: "test-1", title: "Anna Karenina", author: "Tolstoy, Leo">
60
+ p = Page.new(id: "test-3", number: 1, text: "Happy families are all alike; every unhappy family is unhappy in its own way.")
61
+ => #<Page id: "test-3", number: 1, text:"Happy families are all alike; every unhappy family is unhappy in its own way.">
62
+ p.book = b
63
+ => #<Book id: "test-1", title: "Anna Karenina", author: "Tolstoy, Leo">
64
+ p.save
65
+ => true
66
+ b.pages
67
+ => [#<Page id: "test-3", number: 1, text: "Happy families are all alike; every unhappy family is unhappy in its own way.", book_id: "test-1">]
68
+ ```
69
+
70
+ ### Step 4: Look at the RDF (if you want to)
71
+
72
+ **Note:** If you don't know what RDF is and don't care to know, you can skip this step.
73
+
74
+ Let's look at the RDF that active-fedora uses to represent these relationships. To see that content, either output it on the command line like this:
75
+
76
+ ```ruby
77
+ puts p.resource.dump(:ttl)
78
+ ```
79
+ Alternatively, look at the resource in your browser at [http://127.0.0.1:8984/rest/dev/test-3](http://127.0.0.1:8984/rest/dev/test-3) (You might need to change the pid in the URL if your page's id isn't test-3)
80
+
81
+ Either way, you should see RDF that looks like this:
82
+
83
+ ```text
84
+ <http://127.0.0.1:8984/rest/dev/test-3> a <http://www.w3.org/ns/ldp#RDFSource>,
85
+ <http://www.w3.org/ns/ldp#Container>,
86
+ <http://www.jcp.org/jcr/nt/1.0folder>,
87
+ <http://www.jcp.org/jcr/nt/1.0hierarchyNode>,
88
+ <http://www.jcp.org/jcr/nt/1.0base>,
89
+ <http://www.jcp.org/jcr/mix/1.0created>,
90
+ <http://fedora.info/definitions/v4/repository#Container>,
91
+ <http://fedora.info/definitions/v4/repository#Resource>,
92
+ <http://www.jcp.org/jcr/mix/1.0lastModified>,
93
+ <http://www.jcp.org/jcr/mix/1.0referenceable>;
94
+ <http://fedora.info/definitions/v4/repository#created> "2015-02-04T23:09:20.012Z"^^<http://www.w3.org/2001/XMLSchema#dateTime>;
95
+ <http://fedora.info/definitions/v4/repository#createdBy> "bypassAdmin";
96
+ <http://fedora.info/definitions/v4/repository#exportsAs> <http://127.0.0.1:8984/rest/dev/test-3/fcr:export?format=jcr/xml>;
97
+ <http://fedora.info/definitions/v4/repository#hasParent> <http://127.0.0.1:8984/rest/dev>;
98
+ <http://fedora.info/definitions/v4/repository#lastModified> "2015-02-04T23:09:20.012Z"^^<http://www.w3.org/2001/XMLSchema#dateTime>;
99
+ <http://fedora.info/definitions/v4/repository#lastModifiedBy> "bypassAdmin";
100
+ <http://fedora.info/definitions/v4/repository#mixinTypes> "fedora:Container",
101
+ "fedora:Resource";
102
+ <http://fedora.info/definitions/v4/repository#primaryType> "nt:folder";
103
+ <http://fedora.info/definitions/v4/repository#uuid> "6c2b0f28-f211-4e91-a1e6-59b86d916ad6";
104
+ <http://fedora.info/definitions/v4/repository#writable> true;
105
+ <http://opaquenamespace.org/hydra/pageNumber> 1;
106
+ <http://opaquenamespace.org/hydra/pageText> "Happy families are all alike; every unhappy family is unhappy in its own way.";
107
+ <info:fedora/fedora-system:def/model#hasModel> "Page";
108
+ <info:fedora/fedora-system:def/relations-external#isPartOf> <http://127.0.0.1:8984/rest/dev/test-1> .
109
+ => nil
110
+ ```
111
+
112
+ As you can see, it is creating rdf assertions of `info:fedora/fedora-system:def/relations-external#isPartOf` `http://127.0.0.1:8984/rest/dev/test-1` and `info:fedora/fedora-system:def/model#hasModel` `Page`. The model assertion is created automatically based on the name of your ActiveFedora model (in this case, Page)
113
+
114
+ ### Step 5: Commit your changes
115
+
116
+ Now that we've added page relationships, it's a great time to commit to git:
117
+
118
+ ```text
119
+ git add .
120
+ git commit -m "Created a page model with relationship to the book model"
121
+ ```
122
+
123
+ # Next Step
124
+ Go on to **BONUS** [[Lesson - Adding attached files]] or
125
+ explore other [Dive into Hydra](Dive into Hydra#Bonus) tutorial bonus lessons.
@@ -5,7 +5,7 @@
5
5
 
6
6
  # Explanation
7
7
 
8
- This lesson walks you through modifying the "Title" field in your book 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.
8
+ This lesson walks you through modifying the "Author" field in your book 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.
9
9
 
10
10
  # Steps
11
11
 
@@ -16,25 +16,23 @@ We will use the Rails scaffold generator to set up the routes, Controller and Vi
16
16
 
17
17
  **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](http://curriculum.railsbridge.org/curriculum/curriculum) and then come back to this lesson. You might also want to read the [Getting Started with Rails](http://guides.rubyonrails.org/getting_started.html) guide.
18
18
 
19
- Tell the generator to build scaffolding for the Book model and make it assume books have `title` and `author` attributes like the ones we set up earlier in [[Lesson: build a book model]].
19
+ Tell the generator to build scaffolding for the Book model and make it assume books have `title` and `author` attributes like the ones we set up earlier in [Lesson Build a book model with RDF](https://github.com/projecthydra/hydra/wiki/Lesson---Build-a-book-model-with-RDF).
20
20
  ```text
21
21
  rails generate scaffold Book title:string author:string
22
22
  ```
23
23
 
24
+ >NOTE: If you completed the XML based Codex example instead of the RDF based Book example earlier, all you need to do is replace `Book` with `Codex` in the line above.
25
+
24
26
  When it asks you whether to overwrite `app/models/book.rb`, enter `n` and hit enter.
25
27
 
26
- Depending on the specific version of rails you are running the generator creates a few things that we don't want in the `test` and `db` directories. Delete them with the `git clean` command.
28
+ 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 books. We're using Fedora, not SQL, to persist our book objects, so you don't need this database migration. You can delete it with the `git clean` command.
27
29
 
28
30
  ```bash
29
- $ git clean -df test db
31
+ git clean -df db
30
32
  ```
31
33
  You'll see output something like this:
32
34
  ```text
33
35
  Removing db/migrate/20130417230046_create_books.rb
34
- Removing test/fixtures/books.yml
35
- Removing test/functional/books_controller_test.rb
36
- Removing test/unit/book_test.rb
37
- Removing test/unit/helpers/
38
36
  ```
39
37
 
40
38
  ### Step 2: Run the server & Explore
@@ -43,20 +41,6 @@ Run the `rails server` and visit [[http://localhost:3000/books]]
43
41
 
44
42
  Explore the pages for creating, editing and showing Books.
45
43
 
46
- > ####Rails 3-troubleshooting:
47
- > If you see 'uninitialized constant Book::BookMetadata' you'll need to edit two files.
48
- > Open `app/models/datastreams/book_metadata.rb` and replace `BookMetadata` in the class definition with `Datastreams::BookMetadata` i.e.
49
- >
50
- > ```ruby
51
- > class Datastreams::BookMetadata < ActiveFedora::OmDatastream
52
- > ```
53
- >
54
- > You will also need to edit `app/models/book.rb` and replace `BookMetadata` with `Datastreams::BookMetadata`:
55
- >
56
- > ```ruby
57
- > has_metadata 'descMetadata', type: Datastreams::BookMetadata
58
- > ```
59
-
60
44
  ### Step 3: Commit your work
61
45
 
62
46
  ```text
@@ -69,9 +53,11 @@ git commit -m "Ran Book scaffold generator"
69
53
  Open `app/models/book.rb` and edit the multiple setting to be 'true':
70
54
 
71
55
  ```ruby
72
- has_attributes :author, datastream: 'descMetadata', multiple: true
56
+ property :author, predicate: ::RDF::DC.creator, multiple: true
73
57
  ```
74
58
 
59
+ >NOTE: you might be editing the file `app/models/codex.rb` if you completed the XML step of the tutorial, just make sure the value for `multiple:` in the author property is set to `true`
60
+
75
61
  Now you need to tell your hydra application how to display multivalued fields in the 'show' (Display) view for this model.
76
62
  In `app/views/books/show.html.erb` find the lines that display the author field.
77
63
  ```erb
@@ -86,14 +72,14 @@ We want to make these lines iterate over the values returned by `@book.author` a
86
72
  <p>
87
73
  <b>Author(s):</b>
88
74
  <ul>
89
- <%- @book.author.each do |author|%>
75
+ <% @book.author.each do |author|%>
90
76
  <li><%= author %></li>
91
- <%- end %>
77
+ <% end %>
92
78
  </ul>
93
79
  </p>
94
80
  ```
95
81
 
96
- Save the file and refresh the Show view for a book. Now authors show up as a list of values.
82
+ Save the file and refresh the Show view for a book. Now authors show up as a list of values. Note that the display of results of a search is **not** affected, only the view which you can reach from [http://localhost:3000/books](http://localhost:3000/books).
97
83
 
98
84
  ### Step 5: Allow Create and Update views to display Authors as a multi-valued field
99
85
 
@@ -118,7 +104,7 @@ Replace those lines with something that iterates over the values from `@book.aut
118
104
  <% end %>
119
105
  ```
120
106
 
121
- 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 when the array is empty.
107
+ 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.
122
108
 
123
109
  ```erb
124
110
  <%= f.label :author, "Authors" %>
@@ -127,36 +113,55 @@ This handles displaying existing author values, but what about setting the autho
127
113
  <%= text_field_tag "book[author][]", author %>
128
114
  </div>
129
115
  <% end %>
130
- <%- if @book.author.empty? %>
131
- <div class="field">
132
- <%= text_field_tag "book[author][]", nil %>
133
- </div>
134
- <%- end %>
116
+ <div class="field">
117
+ <%= text_field_tag "book[author][]", nil %>
118
+ </div>
119
+ ```
120
+
121
+ Update the `book_params` method in `app/controllers/books_controller.rb` from
122
+ ```ruby
123
+ def book_params
124
+ params.require(:book).permit(:title, :author)
125
+ end
126
+ ```
127
+ to
128
+ ```ruby
129
+ def book_params
130
+ params.require(:book).permit(:title, :author=>[])
131
+ end
135
132
  ```
136
133
 
137
- *Note:* This still doesn't cover the case where you want to _add_ more than one Author to a Book. That goes beyond the scope of this tutorial because it requires javascript (or a multi-page workflow).
138
-
139
- > #### Rails 4-only step
140
- >
141
- > Update the `book_params` method in `app/controllers/books_controller.rb` from
142
- > ```ruby
143
- > def book_params
144
- > params.require(:book).permit(:title, :author)
145
- > end
146
- > ```
147
- > to
148
- > ```ruby
149
- > def book_params
150
- > params.require(:book).permit(:title, :author=>[])
151
- > end
152
- > ```
134
+ 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 BooksController to not save authors that don't have names.
135
+
136
+
137
+ ```ruby
138
+ # app/controllers/books_controller.rb
139
+
140
+ def update
141
+ @book.attributes = book_params
142
+ @book.author = params[:book][:author].select { |a| a.present? }
143
+ respond_to do |format|
144
+ if @book.save
145
+ format.html { redirect_to @book, notice: 'Book was successfully updated.' }
146
+ format.json { render :show, status: :ok, location: @book }
147
+ else
148
+ format.html { render :edit }
149
+ format.json { render json: @book.errors, status: :unprocessable_entity }
150
+ end
151
+ end
152
+ end
153
+
154
+ ```
155
+
156
+ *Note:* This still doesn't cover the case where you want to _add_ more than one additional Author to a Book. That goes beyond the scope of this tutorial because it requires javascript (or a multi-page workflow).
157
+
153
158
 
154
159
  ### Step 6: Try out multiple authors
155
160
 
156
161
  Start up the rails console and run the following commands to add a second author to our book.
157
162
 
158
163
  ```ruby
159
- b = Book.find('changeme:1')
164
+ b = Book.find('test-1')
160
165
  b.author += ['Some other author']
161
166
  b.save
162
167
  ```
@@ -174,5 +179,7 @@ git commit -m "Handling multivalued author fields"
174
179
 
175
180
  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.
176
181
 
182
+ In general, you might not want to build all the views to edit your metadata by hand. The [hydra-editor](https://github.com/projecthydra/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.
183
+
177
184
  # Next Step
178
- Proceed to additional hydra tutorials including [Tame Your XML With OM](https://github.com/projecthydra/om/wiki/Tame-your-XML-with-OM) and [Access Controls with Hydra](https://github.com/projecthydra/hydra-head/wiki/Access-Controls-with-Hydra) or go back to explore other [Dive into Hydra](Dive into Hydra#Bonus) tutorial bonus lessons.
185
+ Proceed to additional hydra tutorials including [Tame your RDF Metadata with ActiveFedora](https://github.com/projecthydra/active_fedora/wiki/Tame-your-RDF-Metadata-with-ActiveFedora) [Tame Your XML With OM](https://github.com/projecthydra/om/wiki/Tame-your-XML-with-OM) and [Access Controls with Hydra](https://github.com/projecthydra/hydra-head/wiki/Access-Controls-with-Hydra) or go back to explore other [Dive into Hydra](Dive into Hydra#bonus) tutorial bonus lessons.