active-fedora 5.3.1 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,9 @@
1
+ 5.4.0
2
+ HYDRA-850 Added finder methods like Base.where().limit().order().first
3
+ Added Base.delete_all and Base.destroy_all
4
+ Fixed RDF proxy delegates that weren't delegating 'is_a?'
5
+ Better looking output for the Base#inspect method
6
+
1
7
  5.3.1
2
8
  Fixed delegating rdf terms as_json
3
9
 
data/LICENSE CHANGED
@@ -1,20 +1,14 @@
1
- Copyright (c) 2009 Matt Zumwalt
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ ##########################################################################
2
+ # Copyright 2011 Stanford University Libraries (SULAIR) and MediaShelf, LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
@@ -50,8 +50,10 @@ module ActiveFedora #:nodoc:
50
50
  autoload :Property
51
51
  autoload :Persistence
52
52
  autoload :QualifiedDublinCoreDatastream
53
+ autoload :Querying
53
54
  autoload :RDFDatastream
54
55
  autoload :RdfxmlRDFDatastream
56
+ autoload :Relation
55
57
  autoload :RelsExtDatastream
56
58
  autoload :ServiceDefinitions
57
59
  autoload :SemanticNode
@@ -29,6 +29,7 @@ module ActiveFedora
29
29
  self.fedora_connection = {}
30
30
  self.profile_solr_name = ActiveFedora::SolrService.solr_name("object_profile", :string, :displayable)
31
31
 
32
+
32
33
  def method_missing(name, *args)
33
34
  dsid = corresponding_datastream_name(name)
34
35
  if dsid
@@ -285,9 +286,12 @@ module ActiveFedora
285
286
  end
286
287
 
287
288
 
288
-
289
- def inspect
290
- "#<#{self.class}:#{self.hash} @pid=\"#{pid}\" >"
289
+ def pretty_pid
290
+ if self.pid == UnsavedDigitalObject::PLACEHOLDER
291
+ nil
292
+ else
293
+ self.pid
294
+ end
291
295
  end
292
296
 
293
297
  # Return a Hash representation of this object where keys in the hash are appropriate Solr field names.
@@ -430,7 +434,6 @@ module ActiveFedora
430
434
  return arr
431
435
  end
432
436
  end
433
-
434
437
  end
435
438
 
436
439
  Base.class_eval do
@@ -448,6 +451,7 @@ module ActiveFedora
448
451
  include Associations
449
452
  include NestedAttributes
450
453
  include Reflection
454
+ extend Querying
451
455
  end
452
456
 
453
457
  end
@@ -13,7 +13,7 @@ module ActiveFedora
13
13
  end
14
14
 
15
15
  def inspect
16
- "#<#{self.class}:#{self.hash} @pid=\"#{digital_object ? pid : nil}\" @dsid=\"#{dsid}\" @controlGroup=\"#{controlGroup}\" changed=\"#{changed?}\" @mimeType=\"#{mimeType}\" >"
16
+ "#<#{self.class} @pid=\"#{digital_object ? pid : nil}\" @dsid=\"#{dsid}\" @controlGroup=\"#{controlGroup}\" changed=\"#{changed?}\" @mimeType=\"#{mimeType}\" >"
17
17
  end
18
18
 
19
19
  #compatibility method for rails' url generators. This method will
@@ -206,9 +206,10 @@ module ActiveFedora
206
206
 
207
207
  module ClassMethods
208
208
  #This method is used to specify the details of a datastream.
209
- #args must include :name. Note that this method doesn't actually
210
- #execute the block, but stores it at the class level, to be executed
211
- #by any future instantiations.
209
+ # You can pass the name as the first argument and a hash of options as the second argument
210
+ # or you can pass the :name as a value in the args hash. Either way, name is required.
211
+ # Note that this method doesn't actually execute the block, but stores it , to be executed
212
+ # by any the implementation of the datastream(specified as :type)
212
213
  #
213
214
  # @param [Hash] args
214
215
  # @option args [Class] :type The class that will represent this datastream, should extend ``Datastream''
@@ -220,7 +221,17 @@ module ActiveFedora
220
221
  # @option args [Boolean] :autocreate Always create this datastream on new objects
221
222
  # @option args [Boolean] :versionable Should versioned datastreams be stored
222
223
  # @yield block executed by some kinds of datastreams
223
- def has_metadata(args, &block)
224
+ def has_metadata(*args, &block)
225
+
226
+ if args.first.is_a? String
227
+ name = args.first
228
+ args = args[1] || {}
229
+ args[:name] = name
230
+ else
231
+ args = args.first
232
+ end
233
+
234
+
224
235
  spec = {:autocreate => args.fetch(:autocreate, false), :type => args[:type], :label => args.fetch(:label,""), :control_group => args[:control_group], :disseminator => args.fetch(:disseminator,""), :url => args.fetch(:url,""),:block => block}
225
236
  spec[:versionable] = args[:versionable] if args.has_key? :versionable
226
237
  ds_specs[args[:name]]= spec
@@ -235,7 +246,14 @@ module ActiveFedora
235
246
  # @option args :control_group ("M") The type of controlGroup to store the datastream as. Defaults to M
236
247
  # @option args [Boolean] :autocreate Always create this datastream on new objects
237
248
  # @option args [Boolean] :versionable Should versioned datastreams be stored
238
- def has_file_datastream(args = {})
249
+ def has_file_datastream(*args)
250
+ if args.first.is_a? String
251
+ name = args.first
252
+ args = args[1] || {}
253
+ args[:name] = name
254
+ else
255
+ args = args.first || {}
256
+ end
239
257
  spec = {:autocreate => args.fetch(:autocreate, false), :type => args.fetch(:type,ActiveFedora::Datastream),
240
258
  :label => args.fetch(:label,"File Datastream"), :control_group => args.fetch(:control_group,"M")}
241
259
  spec[:versionable] = args[:versionable] if args.has_key? :versionable
@@ -2,6 +2,17 @@ module ActiveFedora
2
2
  module Delegating
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ included do
6
+ class_attribute :delegate_registry
7
+ self.delegate_registry = []
8
+ end
9
+
10
+ # Calling inspect may trigger a bunch of loads, but it's mainly for debugging, so no worries.
11
+ def inspect
12
+ values = delegate_registry.map {|r| "#{r}:#{send(r).inspect}"}
13
+ "#<#{self.class} pid:\"#{pretty_pid}\", #{values.join(', ')}>"
14
+ end
15
+
5
16
  module ClassMethods
6
17
  # Provides a delegate class method to expose methods in metadata streams
7
18
  # as member of the base object. Pass the target datastream via the
@@ -56,6 +67,7 @@ module ActiveFedora
56
67
 
57
68
  private
58
69
  def create_delegate_accessor(field, args)
70
+ self.delegate_registry += [field]
59
71
  define_method field do
60
72
  ds = self.send(args[:to])
61
73
  val = if ds.kind_of?(ActiveFedora::RDFDatastream)
@@ -45,25 +45,6 @@ module ActiveFedora
45
45
  result
46
46
  end
47
47
 
48
-
49
- #
50
- # =Class Methods
51
- # These methods are mixed into the inheriting class.
52
- #
53
- # Accessor and mutator methods are dynamically generated based
54
- # on the contents of the @@field_spec hash, which stores the
55
- # field specifications recorded during invocation of has_metadata.
56
- #
57
- # Each metadata field will generate 3 methods:
58
- #
59
- # fieldname_values
60
- # *returns the current values array for this field
61
- # fieldname_values=(val)
62
- # *store val as the values array. val
63
- # may be a single string, or an array of strings
64
- # (single items become single element arrays).
65
- # fieldname_append(val)
66
- # *appends val to the values array.
67
48
  module ClassMethods
68
49
  # Returns a suitable uri object for :has_model
69
50
  # Should reverse Model#from_class_uri
@@ -81,164 +62,6 @@ module ActiveFedora
81
62
  "info:fedora/#{namespace}:#{ContentModel.sanitized_class_name(self)}#{pid_suffix}"
82
63
  end
83
64
 
84
- # Returns an Array of objects of the Class that +find+ is being
85
- # called on
86
- #
87
- # @param[String,Symbol,Hash] args either a pid or :all or a hash of conditions
88
- # @param [Hash] opts the options to create a message with.
89
- # @option opts [Integer] :rows when :all is passed, the maximum number of rows to load from solr
90
- # @option opts [Boolean] :cast when true, examine the model and cast it to the first known cModel
91
- def find(args, opts={}, &block)
92
- return find_one(args, opts[:cast]) if args.class == String
93
- return to_enum(:find, args, opts).to_a unless block_given?
94
-
95
- args = {} if args == :all
96
- find_each(args, opts) do |obj|
97
- yield obj
98
- end
99
- end
100
-
101
- def all(opts = {}, &block)
102
- find(:all, opts, &block)
103
- end
104
-
105
-
106
- # Yields each batch of solr records that was found by the find +options+ as
107
- # an array. The size of each batch is set by the <tt>:batch_size</tt>
108
- # option; the default is 1000.
109
- #
110
- # Returns a solr result matching the supplied conditions
111
- # @param[Hash] conditions solr conditions to match
112
- # @param[Hash] options
113
- # @option opts [Array] :sort a list of fields to sort by
114
- # @option opts [Array] :rows number of rows to return
115
- #
116
- # @example
117
- # Person.find_in_batches('age_t'=>'21', {:batch_size=>50}) do |group|
118
- # group.each { |person| puts person['name_t'] }
119
- # end
120
-
121
- def find_in_batches conditions, opts={}
122
- opts[:q] = create_query(conditions)
123
- opts[:qt] = solr_query_handler
124
- #set default sort to created date ascending
125
- unless opts.include?(:sort)
126
- opts[:sort]=[ActiveFedora::SolrService.solr_name(:system_create,:date)+' asc']
127
- end
128
-
129
- batch_size = opts.delete(:batch_size) || 1000
130
-
131
- counter = 0
132
- begin
133
- counter += 1
134
- response = ActiveFedora::SolrService.instance.conn.paginate counter, batch_size, "select", :params => opts
135
- docs = response["response"]["docs"]
136
- yield docs
137
- end while docs.has_next?
138
- end
139
-
140
- # Yields the found ActiveFedora::Base object to the passed block
141
- #
142
- # @param [Hash] conditions the conditions for the solr search to match
143
- # @param [Hash] opts
144
- # @option opts [Boolean] :cast when true, examine the model and cast it to the first known cModel
145
- def find_each( conditions={}, opts={})
146
- find_in_batches(conditions, opts.merge({:fl=>SOLR_DOCUMENT_ID})) do |group|
147
- group.each do |hit|
148
- yield(find_one(hit[SOLR_DOCUMENT_ID], opts[:cast]))
149
- end
150
- end
151
- end
152
-
153
-
154
- # Returns true if the pid exists in the repository
155
- # @param[String] pid
156
- # @return[boolean]
157
- def exists?(pid)
158
- inner = DigitalObject.find_or_initialize(self, pid)
159
- !inner.new?
160
- end
161
-
162
- # Get a count of the number of objects from solr
163
- # Takes :conditions as an argument
164
- def count(args = {})
165
- q = search_model_clause ? [search_model_clause] : []
166
- q << "#{args[:conditions]}" if args[:conditions]
167
- SolrService.query(q.join(' AND '), :raw=>true, :rows=>0)['response']['numFound']
168
- end
169
-
170
- # Returns a solr result matching the supplied conditions
171
- # @param[Hash,String] conditions can either be specified as a string, or
172
- # hash representing the query part of an solr statement. If a hash is
173
- # provided, this method will generate conditions based simple equality
174
- # combined using the boolean AND operator.
175
- # @param[Hash] options
176
- # @option opts [Array] :sort a list of fields to sort by
177
- # @option opts [Array] :rows number of rows to return
178
- def find_with_conditions(conditions, opts={})
179
- #set default sort to created date ascending
180
- unless opts.include?(:sort)
181
- opts[:sort]=[ActiveFedora::SolrService.solr_name(:system_create,:date)+' asc']
182
- end
183
- SolrService.query(create_query(conditions), opts)
184
- end
185
-
186
- def quote_for_solr(value)
187
- '"' + value.gsub(/(:)/, '\\:').gsub(/(\/)/, '\\/').gsub(/"/, '\\"') + '"'
188
- end
189
-
190
- private
191
-
192
- # Returns a solr query for the supplied conditions
193
- # @param[Hash] conditions solr conditions to match
194
- def create_query(conditions)
195
- conditions.kind_of?(Hash) ? create_query_from_hash(conditions) : create_query_from_string(conditions)
196
- end
197
-
198
- def create_query_from_hash(conditions)
199
- clauses = search_model_clause ? [search_model_clause] : []
200
- conditions.each_pair do |key,value|
201
- unless value.nil?
202
- if value.is_a? Array
203
- value.each do |val|
204
- clauses << "#{key}:#{quote_for_solr(val)}"
205
- end
206
- else
207
- key = SOLR_DOCUMENT_ID if (key === :id || key === :pid)
208
- escaped_value = quote_for_solr(value)
209
- clauses << (key.to_s.eql?(SOLR_DOCUMENT_ID) ? "#{key}:#{escaped_value}" : "#{key}:#{escaped_value}")
210
- end
211
- end
212
- end
213
- return "*:*" if clauses.empty?
214
- clauses.compact.join(" AND ")
215
- end
216
-
217
- def create_query_from_string(conditions)
218
- model_clause = search_model_clause
219
- model_clause ? "#{model_clause} AND (#{conditions})" : conditions
220
- end
221
-
222
- # Return the solr clause that queries for this type of class
223
- def search_model_clause
224
- unless self == ActiveFedora::Base
225
- return ActiveFedora::SolrService.construct_query_for_rel(:has_model, self.to_class_uri)
226
- end
227
- end
228
-
229
- # Retrieve the Fedora object with the given pid, explore the returned object, determine its model
230
- # using #{ActiveFedora::ContentModel.known_models_for} and cast to that class.
231
- # Raises a ObjectNotFoundError if the object is not found.
232
- # @param [String] pid of the object to load
233
- # @param [Boolean] cast when true, cast the found object to the class of the first known model defined in it's RELS-EXT
234
- #
235
- # @example because the object hydra:dataset1 asserts it is a Dataset (hasModel info:fedora/afmodel:Dataset), return a Dataset object (not a Book).
236
- # Book.find_one("hydra:dataset1")
237
- def find_one(pid, cast=false)
238
- inner = DigitalObject.find(self, pid)
239
- af_base = self.allocate.init_with(inner)
240
- cast ? af_base.adapt_to_cmodel : af_base
241
- end
242
65
  end
243
66
 
244
67
  private
@@ -0,0 +1,146 @@
1
+ module ActiveFedora
2
+ module Querying
3
+ delegate :find, :first, :where, :limit, :order, :all, :delete_all, :destroy_all, :to=>:relation
4
+ def relation
5
+ Relation.new(self)
6
+ end
7
+
8
+ # Yields each batch of solr records that was found by the find +options+ as
9
+ # an array. The size of each batch is set by the <tt>:batch_size</tt>
10
+ # option; the default is 1000.
11
+ #
12
+ # Returns a solr result matching the supplied conditions
13
+ # @param[Hash] conditions solr conditions to match
14
+ # @param[Hash] options
15
+ # @option opts [Array] :sort a list of fields to sort by
16
+ # @option opts [Array] :rows number of rows to return
17
+ #
18
+ # @example
19
+ # Person.find_in_batches('age_t'=>'21', {:batch_size=>50}) do |group|
20
+ # group.each { |person| puts person['name_t'] }
21
+ # end
22
+
23
+ def find_in_batches conditions, opts={}
24
+ opts[:q] = create_query(conditions)
25
+ opts[:qt] = solr_query_handler
26
+ #set default sort to created date ascending
27
+ unless opts[:sort].present?
28
+ opts[:sort]=[ActiveFedora::SolrService.solr_name(:system_create,:date)+' asc']
29
+ end
30
+
31
+ batch_size = opts.delete(:batch_size) || 1000
32
+
33
+ counter = 0
34
+ begin
35
+ counter += 1
36
+ response = ActiveFedora::SolrService.instance.conn.paginate counter, batch_size, "select", :params => opts
37
+ docs = response["response"]["docs"]
38
+ yield docs
39
+ end while docs.has_next?
40
+ end
41
+
42
+ # Yields the found ActiveFedora::Base object to the passed block
43
+ #
44
+ # @param [Hash] conditions the conditions for the solr search to match
45
+ # @param [Hash] opts
46
+ # @option opts [Boolean] :cast when true, examine the model and cast it to the first known cModel
47
+ def find_each( conditions={}, opts={})
48
+ find_in_batches(conditions, opts.merge({:fl=>SOLR_DOCUMENT_ID})) do |group|
49
+ group.each do |hit|
50
+ yield(find_one(hit[SOLR_DOCUMENT_ID], opts[:cast]))
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+ # Returns true if the pid exists in the repository
57
+ # @param[String] pid
58
+ # @return[boolean]
59
+ def exists?(pid)
60
+ inner = DigitalObject.find_or_initialize(self, pid)
61
+ !inner.new?
62
+ end
63
+
64
+ # Get a count of the number of objects from solr
65
+ # Takes :conditions as an argument
66
+ def count(args = {})
67
+ q = search_model_clause ? [search_model_clause] : []
68
+ q << "#{args[:conditions]}" if args[:conditions]
69
+ SolrService.query(q.join(' AND '), :raw=>true, :rows=>0)['response']['numFound']
70
+ end
71
+
72
+ # Returns a solr result matching the supplied conditions
73
+ # @param[Hash,String] conditions can either be specified as a string, or
74
+ # hash representing the query part of an solr statement. If a hash is
75
+ # provided, this method will generate conditions based simple equality
76
+ # combined using the boolean AND operator.
77
+ # @param[Hash] options
78
+ # @option opts [Array] :sort a list of fields to sort by
79
+ # @option opts [Array] :rows number of rows to return
80
+ def find_with_conditions(conditions, opts={})
81
+ #set default sort to created date ascending
82
+ unless opts.include?(:sort)
83
+ opts[:sort]=[ActiveFedora::SolrService.solr_name(:system_create,:date)+' asc']
84
+ end
85
+ SolrService.query(create_query(conditions), opts)
86
+ end
87
+
88
+ def quote_for_solr(value)
89
+ '"' + value.gsub(/(:)/, '\\:').gsub(/(\/)/, '\\/').gsub(/"/, '\\"') + '"'
90
+ end
91
+
92
+ # Retrieve the Fedora object with the given pid, explore the returned object, determine its model
93
+ # using #{ActiveFedora::ContentModel.known_models_for} and cast to that class.
94
+ # Raises a ObjectNotFoundError if the object is not found.
95
+ # @param [String] pid of the object to load
96
+ # @param [Boolean] cast when true, cast the found object to the class of the first known model defined in it's RELS-EXT
97
+ #
98
+ # @example because the object hydra:dataset1 asserts it is a Dataset (hasModel info:fedora/afmodel:Dataset), return a Dataset object (not a Book).
99
+ # Book.find_one("hydra:dataset1")
100
+ def find_one(pid, cast=false)
101
+ inner = DigitalObject.find(self, pid)
102
+ af_base = self.allocate.init_with(inner)
103
+ cast ? af_base.adapt_to_cmodel : af_base
104
+ end
105
+
106
+
107
+ private
108
+
109
+ # Returns a solr query for the supplied conditions
110
+ # @param[Hash] conditions solr conditions to match
111
+ def create_query(conditions)
112
+ conditions.kind_of?(Hash) ? create_query_from_hash(conditions) : create_query_from_string(conditions)
113
+ end
114
+
115
+ def create_query_from_hash(conditions)
116
+ clauses = search_model_clause ? [search_model_clause] : []
117
+ conditions.each_pair do |key,value|
118
+ unless value.nil?
119
+ if value.is_a? Array
120
+ value.each do |val|
121
+ clauses << "#{key}:#{quote_for_solr(val)}"
122
+ end
123
+ else
124
+ key = SOLR_DOCUMENT_ID if (key === :id || key === :pid)
125
+ escaped_value = quote_for_solr(value)
126
+ clauses << (key.to_s.eql?(SOLR_DOCUMENT_ID) ? "#{key}:#{escaped_value}" : "#{key}:#{escaped_value}")
127
+ end
128
+ end
129
+ end
130
+ return "*:*" if clauses.empty?
131
+ clauses.compact.join(" AND ")
132
+ end
133
+
134
+ def create_query_from_string(conditions)
135
+ model_clause = search_model_clause
136
+ model_clause ? "#{model_clause} AND (#{conditions})" : conditions
137
+ end
138
+
139
+ # Return the solr clause that queries for this type of class
140
+ def search_model_clause
141
+ unless self == ActiveFedora::Base
142
+ return ActiveFedora::SolrService.construct_query_for_rel(:has_model, self.to_class_uri)
143
+ end
144
+ end
145
+ end
146
+ end