active-fedora 5.3.1 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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