active-fedora 3.1.5 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- active-fedora (3.1.5)
4
+ active-fedora (3.1.6)
5
5
  activeresource (~> 3.0.0)
6
6
  activesupport (~> 3.0.0)
7
7
  equivalent-xml
@@ -10,7 +10,7 @@ PATH
10
10
  mime-types (>= 1.16)
11
11
  multipart-post (= 1.1.2)
12
12
  nokogiri
13
- om (>= 1.4.3)
13
+ om (>= 1.4.4)
14
14
  rdf
15
15
  rdf-rdfxml
16
16
  rubydora (~> 0.2.6)
@@ -69,7 +69,7 @@ GEM
69
69
  multipart-post (1.1.2)
70
70
  nokogiri (1.5.0)
71
71
  nori (1.0.2)
72
- om (1.4.3)
72
+ om (1.4.4)
73
73
  mediashelf-loggable
74
74
  nokogiri (>= 1.4.2)
75
75
  rack (1.3.5)
@@ -1,3 +1,7 @@
1
+ 3.1.6
2
+
3
+ HYDRA-733 When serializing, MetadataDatastream does not load the existing content, so it can overwrite with blank
4
+
1
5
  3.1.5
2
6
  HYDRA-722 updating AF::Base#label= and then saving doesn't persist the change
3
7
  JettyWrapper to 1.2.0
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.add_dependency('mime-types', '>= 1.16')
21
21
  s.add_dependency('multipart-post', "= 1.1.2")
22
22
  s.add_dependency('nokogiri')
23
- s.add_dependency('om', '>= 1.4.3')
23
+ s.add_dependency('om', '>= 1.4.4')
24
24
  s.add_dependency('solrizer', '>1.0.0')
25
25
  s.add_dependency("activeresource", '~> 3.0.0')
26
26
  s.add_dependency("activesupport", '~> 3.0.0')
@@ -32,6 +32,7 @@ module ActiveFedora #:nodoc:
32
32
  autoload :SemanticNode
33
33
  autoload :NestedAttributes
34
34
  autoload :FixtureLoader
35
+ autoload :DatastreamCollections
35
36
 
36
37
  end
37
38
 
@@ -40,6 +40,39 @@ module ActiveFedora
40
40
  end
41
41
 
42
42
 
43
+ # Specifies a one-to-one association with another class. This method should only be used
44
+ # if this class contains the foreign key.
45
+ #
46
+ # Methods will be added for retrieval and query for a single associated object, for which
47
+ # this object holds an id:
48
+ #
49
+ # [association()]
50
+ # Returns the associated object. +nil+ is returned if none is found.
51
+ # [association=(associate)]
52
+ # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
53
+ #
54
+ # (+association+ is replaced with the symbol passed as the first argument, so
55
+ # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
56
+ #
57
+ # === Example
58
+ #
59
+ # A Post class declares <tt>belongs_to :author</tt>, which will add:
60
+ # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
61
+ # * <tt>Post#author=(author)</tt>
62
+ # The declaration can also include an options hash to specialize the behavior of the association.
63
+ #
64
+ # === Options
65
+ #
66
+ # [:property]
67
+ # the association predicate to use when storing the association +REQUIRED+
68
+ # [:class_name]
69
+ # Specify the class name of the association. Use it only if that name can't be inferred
70
+ # from the association name. So <tt>has_one :author</tt> will by default be linked to the Author class, but
71
+ # if the real class name is Person, you'll have to specify it with this option.
72
+ #
73
+ # Option examples:
74
+ # belongs_to :firm, :property => :client_of
75
+ # belongs_to :author, :class_name => "Person", :property => :author_of
43
76
  def belongs_to(association_id, options = {})
44
77
  raise "You must specify a property name for #{name}" if !options[:property]
45
78
  has_relationship association_id.to_s, options[:property]
@@ -32,12 +32,10 @@ module ActiveFedora
32
32
  class Base
33
33
  include RelationshipsHelper
34
34
  include SemanticNode
35
- class_inheritable_accessor :ds_specs, :class_named_datastreams_desc
36
- self.class_named_datastreams_desc = {}
35
+ class_inheritable_accessor :ds_specs
37
36
 
38
37
  self.ds_specs = {'RELS-EXT'=> {:type=> ActiveFedora::RelsExtDatastream, :label=>"", :block=>nil}}
39
38
 
40
- attr_accessor :named_datastreams_desc
41
39
 
42
40
 
43
41
  has_relationship "collection_members", :has_collection_member
@@ -110,21 +108,29 @@ module ActiveFedora
110
108
  ds_specs[args[:name]]= {:type => args[:type], :label => args.fetch(:label,""), :control_group => args.fetch(:control_group,"X"), :disseminator => args.fetch(:disseminator,""), :url => args.fetch(:url,""),:block => block}
111
109
  end
112
110
 
111
+
113
112
  def method_missing(name, *args)
114
- if datastreams.has_key? name.to_s
113
+ dsid = corresponding_datastream_name(name)
114
+ if dsid
115
115
  ### Create and invoke a proxy method
116
- self.class.class_eval <<-end_eval, __FILE__, __LINE__
117
- def #{name.to_s}()
118
- datastreams["#{name.to_s}"]
119
- end
120
- end_eval
121
-
116
+ self.class.send :define_method, name do
117
+ datastreams[dsid]
118
+ end
122
119
  self.send(name)
123
120
  else
124
121
  super
125
122
  end
126
123
  end
127
124
 
125
+ ## Given a method name, return the best-guess dsid
126
+ def corresponding_datastream_name(method_name)
127
+ dsid = method_name.to_s
128
+ return dsid if datastreams.has_key? dsid
129
+ unescaped_name = method_name.to_s.gsub('_', '-')
130
+ return unescaped_name if datastreams.has_key? unescaped_name
131
+ nil
132
+ end
133
+
128
134
  #Saves a Base object, and any dirty datastreams, then updates
129
135
  #the Solr index for this object.
130
136
  def save
@@ -224,8 +230,9 @@ module ActiveFedora
224
230
  datastreams[datastream.dsid] = datastream
225
231
  return datastream.dsid
226
232
  end
233
+
227
234
  def add(datastream) # :nodoc:
228
- warn "Warning: ActiveFedora::Base.add has been deprected. Use add_datastream"
235
+ warn "Warning: ActiveFedora::Base.add has been deprecated. Use add_datastream"
229
236
  add_datastream(datastream)
230
237
  end
231
238
 
@@ -258,21 +265,16 @@ module ActiveFedora
258
265
  # return a valid dsid that is not currently in use. Uses a prefix (default "DS") and an auto-incrementing integer
259
266
  # Example: if there are already datastreams with IDs DS1 and DS2, this method will return DS3. If you specify FOO as the prefix, it will return FOO1.
260
267
  def generate_dsid(prefix="DS")
261
- keys = datastreams.keys
262
- next_index = keys.select {|v| v =~ /(#{prefix}\d*$)/}.length + 1
263
- new_dsid = prefix.to_s + next_index.to_s
264
- while keys.include?(new_dsid)
265
- next_index += 1
266
- new_dsid = prefix.to_s + next_index.to_s
267
- end
268
- new_dsid
269
-
270
- # while keys.include?(new_dsid)
271
- # next_index += 1
272
- # new_dsid = prefix.to_s + rand(range).to_s
273
- # end
268
+ matches = datastreams.keys.map {|d| data = /^#{prefix}(\d+)$/.match(d); data && data[1].to_i}.compact
269
+ val = matches.empty? ? 1 : matches.max + 1
270
+ format_dsid(prefix, val)
274
271
  end
275
272
 
273
+ ### Provided so that an application can override how generated pids are formatted (e.g DS01 instead of DS1)
274
+ def format_dsid(prefix, suffix)
275
+ sprintf("%s%i", prefix,suffix)
276
+ end
277
+
276
278
  # Return the Dublin Core (DC) Datastream. You can also get at this via
277
279
  # the +datastreams["DC"]+.
278
280
  def dc
@@ -375,192 +377,6 @@ module ActiveFedora
375
377
  # will rely on SemanticNode.remove_relationship once it is implemented
376
378
  end
377
379
 
378
- # ** EXPERIMENTAL **
379
- #
380
- # Returns array of datastream names defined for this object
381
- def datastream_names
382
- named_datastreams_desc.keys
383
- end
384
-
385
- # ** EXPERIMENTAL **
386
- #
387
- # Calls add_named_datastream while assuming it will be managed content and sets :blob and :controlGroup values accordingly
388
- # ====Parameters
389
- # name: Datastream name
390
- # file: The file to add for this datastream
391
- # opts: Options hash. See +add_named_datastream+ for expected keys and values
392
- def add_named_file_datastream(name, file, opts={})
393
- opts.merge!({:blob=>file,:controlGroup=>'M'})
394
- add_named_datastream(name,opts)
395
- end
396
-
397
- # ** EXPERIMENTAL **
398
- #
399
- # This object is used by [datastream_name]_append helper to add a named datastream
400
- # but can also be called directly.
401
- # ====Parameters
402
- # name: name of datastream to add
403
- # opts: hash defining datastream attributes
404
- # The following are expected keys in opts hash:
405
- # :label => Defaults to the file name
406
- # :blob or :file => Required to point to the datastream file being added if managed content
407
- # :controlGroup => defaults to 'M' for managed, can also be 'E' external and 'R' for redirected
408
- # :content_type => required if the file does not respond to 'content_type' and should match :mimeType in has_datastream definition if defined
409
- # :dsLocation => holds uri location of datastream. Required only if :controlGroup is type 'E' or 'R'.
410
- # :dsid or :dsId => Optional, and used to update an existing datastream with dsid supplied. Will throw an error if dsid does not exist and does not match prefix pattern for datastream name
411
- def add_named_datastream(name,opts={})
412
- unless named_datastreams_desc.has_key?(name) && named_datastreams_desc[name].has_key?(:type)
413
- raise "Failed to add datastream. Named datastream #{name} not defined for object #{pid}."
414
- end
415
-
416
- if opts.has_key?(:mime_type)
417
- opts.merge!({:content_type=>opts[:mime_type]})
418
- elsif opts.has_key?(:mimeType)
419
- opts.merge!({:content_type=>opts[:mimeType]})
420
- end
421
- opts.merge!(named_datastreams_desc[name])
422
-
423
- label = opts.has_key?(:label) ? opts[:label] : ""
424
-
425
- #only do these steps for managed datastreams
426
- unless (opts.has_key?(:controlGroup)&&opts[:controlGroup]!="M")
427
- if opts.has_key?(:file)
428
- opts.merge!({:blob => opts[:file]})
429
- opts.delete(:file)
430
- end
431
-
432
- raise "You must define parameter blob for this managed datastream to load for #{pid}" unless opts.has_key?(:blob)
433
-
434
- #if no explicit label and is a file use original file name for label
435
- if !opts.has_key?(:label)&&opts[:blob].respond_to?(:original_filename)
436
- label = opts[:blob].original_filename
437
- end
438
-
439
- if opts[:blob].respond_to?(:content_type)&&!opts[:blob].content_type.nil? && !opts.has_key?(:content_type)
440
- opts.merge!({:content_type=>opts[:blob].content_type})
441
- end
442
-
443
- raise "The blob must respond to content_type or the hash must have :content_type or :mime_type property set" unless opts.has_key?(:content_type)
444
-
445
- #throw error for mimeType mismatch
446
- if named_datastreams_desc[name].has_key?(:mimeType) && !named_datastreams_desc[name][:mimeType].eql?(opts[:content_type])
447
- raise "Content type mismatch for add datastream #{name} to #{pid}. Expected: #{named_datastreams_desc[name][:mimeType]}, Actual: #{opts[:content_type]}"
448
- end
449
- else
450
- label = opts[:dsLocation] if (opts.has_key?(:dsLocation))
451
- end
452
-
453
- opts.merge!(:dsLabel => label)
454
-
455
- ds = create_datastream(named_datastreams_desc[name][:type], opts[:dsid], opts)
456
- #Must be of type datastream
457
- assert_kind_of 'datastream', ds, ActiveFedora::Datastream
458
- #make sure dsid is nil so that it uses the prefix for mapping purposes
459
- #check dsid works for the prefix if it is set
460
- if !ds.dsid.nil? && opts.has_key?(:prefix)
461
- raise "dsid supplied does not conform to pattern #{opts[:prefix]}[number]" unless ds.dsid =~ /#{opts[:prefix]}[0-9]/
462
- end
463
-
464
- add_datastream(ds,opts)
465
- end
466
-
467
- # ** EXPERIMENTAL **
468
- #
469
- # Update an existing named datastream. It has same parameters as add_named_datastream
470
- # except the :dsid key is now required.
471
- #
472
- # ====TODO
473
- # Currently requires you to update file if a managed datastream
474
- # but could change to allow metadata only updates as well
475
- def update_named_datastream(name, opts={})
476
- #check that dsid provided matches existing datastream with that name
477
- raise "You must define parameter dsid for datastream to update for #{pid}" unless opts.include?(:dsid)
478
- raise "Datastream with name #{name} and dsid #{opts[:dsid]} does not exist for #{pid}" unless self.send("#{name}_ids").include?(opts[:dsid])
479
- add_named_datastream(name,opts)
480
- end
481
-
482
- # ** EXPERIMENTAL **
483
- #
484
- # Throws an assertion failure unless the object 'o' is kind_of? class 't'
485
- # ====Parameters
486
- # n: Name of object
487
- # o: The object to test
488
- # t: The class type to check is kind_of?
489
- def assert_kind_of(n, o,t)
490
- raise "Assertion failure: #{n}: #{o} is not of type #{t}" unless o.kind_of?(t)
491
- end
492
-
493
- # ** EXPERIMENTAL **
494
- #
495
- # Returns true if the name is a defined named datastream
496
- def is_named_datastream?(name)
497
- named_datastreams_desc.has_key?(name)
498
- end
499
-
500
- # ** EXPERIMENTAL **
501
- #
502
- # Returns hash of datastream names defined by has_datastream calls mapped to
503
- # array of datastream objects that have been added
504
- # ====Example
505
- # For the following has_datastream entries and a datastream defined for minivan only would be
506
- # has_datastream :name=>"minivan", :prefix => "VAN", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'M'
507
- # has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'
508
- #
509
- # Returns
510
- # {"external_images"=>[],"thumbnails"=>{#<ActiveFedora::Datastream:0x7ffd6512daf8 ...}}
511
- def named_datastreams
512
- ds_values = {}
513
- self.class.named_datastreams_desc.keys.each do |name|
514
- ds_values.merge!({name=>self.send("#{name}")})
515
- end
516
- return ds_values
517
- end
518
-
519
- # ** EXPERIMENTAL **
520
- #
521
- # Returns hash of datastream names mapped to an array
522
- # of dsid's for named datastream objects
523
- # === Example
524
- # For the following has_datastream call, assume we have added two datastreams.
525
- #
526
- # has_datastream :name=>"thumbnails",:prefix => "THUMB",:type=>ActiveFedora::Datastream, :mimeType=>"image/jpeg", :controlGroup=>'M'
527
- #
528
- # It would then return
529
- # {"thumbnails=>["THUMB1", "THUMB2"]}
530
- def named_datastreams_ids
531
- dsids = {}
532
- self.class.named_datastreams_desc.keys.each do |name|
533
- dsid_array = self.send("#{name}_ids")
534
- dsids[name] = dsid_array
535
- end
536
- return dsids
537
- end
538
-
539
- # ** EXPERIMENTAL **
540
- #
541
- # Returns the hash that stores arguments passed to has_datastream calls within an
542
- # ActiveFedora::Base child class.
543
- #
544
- # has_datastream :name=>"audio_file", :prefix=>"AUDIO", :type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav"
545
- # has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'
546
- #
547
- # The above examples result in the following hash
548
- # {"audio_file"=>{:prefix=>"AUDIO",:type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav", :controlGroup=>'M'},
549
- # "external_images=>{:prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'}}
550
- #
551
- # This hash is later used when adding a named datastream such as an "audio_file" as defined above.
552
- def named_datastreams_desc
553
- @named_datastreams_desc ||= named_datastreams_desc_from_class
554
- end
555
-
556
- # ** EXPERIMENTAL **
557
- #
558
- # Get class variable hash that stores has_datastream arguments.
559
- # It is used to initialize the value returned by public named_datastreams_desc method
560
- def named_datastreams_desc_from_class
561
- self.class.named_datastreams_desc
562
- end
563
-
564
380
  def create_datastream(type, dsid, opts={})
565
381
  dsid = generate_dsid(opts[:prefix] || "DS") if dsid == nil
566
382
  klass = type.kind_of?(Class) ? type : type.constantize
@@ -587,128 +403,6 @@ module ActiveFedora
587
403
  ds
588
404
  end
589
405
 
590
- # ** EXPERIMENTAL **
591
- #
592
- # Allows for a datastream to be treated like any other attribute of a model class
593
- # while enforcing mimeType and/or datastream type (ie. external, managed, etc.) if defined.
594
- # ====Examples
595
- #
596
- # has_datastream :name=>"thumbnails",:prefix => "THUMB",:type=>ActiveFedora::Datastream, :mimeType=>"image/jpeg", :controlGroup=>'M'
597
- # has_datastream :name=>"EADs", :type=>ActiveFedora::Datastream, :mimeType=>"application/xml", :controlGroup=>'M'
598
- # has_datastream :name=>"external_images", :type=>ActiveFedora::Datastream, :controlGroup=>'E'
599
- #
600
- # Required Keys in args
601
- # :name - name to give this datastream (must be unique)
602
- #
603
- # Optional Keys in args
604
- # :prefix - used to create the DSID plus an index ie. THUMB1, THUMB2. If :prefix is not specified, defaults to :name value in all uppercase
605
- # :type - defaults to ActiveFedora::Datastream if you would like content specific class to be used supply it here
606
- # :mimeType - if supplied it will ensure any datastreams added are of this type, if not supplied any mimeType is acceptabl e
607
- # :controlGroup - possible values "X", "M", "R", or "E" (InlineXML, Managed Content, Redirect, or External Referenced) If controlGroup is 'E' or 'R' it expects a dsLocation be defined when adding the datastream.
608
- #
609
- # You use the datastream attribute using helper methods created for each datastream name:
610
- #
611
- # ====Helper Method Examples
612
- # thumbnails_append - Append a thumbnail datastream
613
- # thumbnails - Get array of thumbnail datastreams
614
- # thumbnails_ids - Get array of dsid's for thumbnail datastreams
615
- #
616
- # When loading the list of datastreams for a name from Fedora it uses the DSID prefix to find them in Fedora
617
- def self.has_datastream(args)
618
- unless args.has_key?(:name)
619
- return false
620
- end
621
- unless args.has_key?(:prefix)
622
- args.merge!({:prefix=>args[:name].to_s.upcase})
623
- end
624
- unless named_datastreams_desc.has_key?(args[:name])
625
- named_datastreams_desc[args[:name]] = {}
626
- end
627
-
628
- args.merge!({:mimeType=>args[:mime_type]}) if args.has_key?(:mime_type)
629
-
630
- unless named_datastreams_desc[args[:name]].has_key?(:type)
631
- #default to type ActiveFedora::Datastream
632
- args.merge!({:type => "ActiveFedora::Datastream"})
633
- end
634
- named_datastreams_desc[args[:name]]= args
635
- create_named_datastream_finders(args[:name],args[:prefix])
636
- create_named_datastream_update_methods(args[:name])
637
- end
638
-
639
- # ** EXPERIMENTAL **
640
- #
641
- # Creates the following helper methods for a datastream name
642
- # [datastream_name]_append - Add a named datastream
643
- #
644
- # ==== Examples for "thumbnails" datastream
645
- # thumbnails_append - Append a thumbnail datastream
646
- # TODO: Add [datastream_name]_remove
647
- def self.create_named_datastream_update_methods(name)
648
- append_file_method_name = "#{name.to_s.downcase}_file_append"
649
- append_method_name = "#{name.to_s.downcase}_append"
650
- #remove_method_name = "#{name.to_s.downcase}_remove"
651
- self.send(:define_method,:"#{append_file_method_name}") do |*args|
652
- file,opts = *args
653
- opts ||= {}
654
- add_named_file_datastream(name,file,opts)
655
- end
656
-
657
- self.send(:define_method,:"#{append_method_name}") do |*args|
658
- #call add_named_datastream instead of add_file_named_datastream in case not managed datastream
659
- add_named_datastream(name,*args)
660
- end
661
- end
662
-
663
- # ** EXPERIMENTAL **
664
- #
665
- # Creates the following helper methods for a datastream name
666
- # [datastream_name] - Returns array of named datastreams
667
- # [datastream_name]_ids - Returns array of named datastream dsids
668
- #
669
- # ==== Examples for "thumbnails" datastream
670
- # thumbnails - Get array of thumbnail datastreams
671
- # thumbnails_ids - Get array of dsid's for thumbnail datastreams
672
- def self.create_named_datastream_finders(name, prefix)
673
- class_eval <<-END, __FILE__, __LINE__
674
- def #{name}(opts={})
675
- id_array = []
676
- keys = datastreams.keys
677
- id_array = keys.select {|v| v =~ /#{prefix}\d*/}
678
- if opts[:response_format] == :id_array
679
- return id_array
680
- else
681
- named_ds = []
682
- id_array.each do |name|
683
- if datastreams.has_key?(name)
684
- named_ds.push(datastreams[name])
685
- end
686
- end
687
- return named_ds
688
- end
689
- end
690
- def #{name}_ids
691
- #{name}(:response_format => :id_array)
692
- end
693
- END
694
- end
695
-
696
- # ** EXPERIMENTAL **
697
- #
698
- # Accessor for class variable for hash that stores arguments passed to has_datastream calls within an
699
- # ActiveFedora::Base child class.
700
- #
701
- # has_datastream :name=>"audio_file", :prefix=>"AUDIO", :type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav"
702
- # has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'
703
- #
704
- # The above examples result in the following hash
705
- # {"audio_file"=>{:prefix=>"AUDIO",:type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav", :controlGroup=>'M'},
706
- # "external_images=>{:prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'}}
707
- #
708
- # This hash is later used when adding a named datastream such as an "audio_file" as defined above.
709
- def self.named_datastreams_desc
710
- self.class_named_datastreams_desc ||= {}
711
- end
712
406
 
713
407
  #
714
408
  # Relationships Management
@@ -1073,6 +767,7 @@ module ActiveFedora
1073
767
  include Associations
1074
768
  include NestedAttributes
1075
769
  include Reflection
770
+ include DatastreamCollections
1076
771
  end
1077
772
 
1078
773
  end
@@ -28,7 +28,6 @@ module ActiveFedora
28
28
  end
29
29
 
30
30
  # Test whether this datastream been modified since it was last saved?
31
- # TODO deprecate this, just use changed?
32
31
  def dirty?
33
32
  @dirty || changed?
34
33
  end
@@ -0,0 +1,326 @@
1
+ module DatastreamCollections
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ class_inheritable_accessor :class_named_datastreams_desc
6
+ self.class_named_datastreams_desc = {}
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ # ** EXPERIMENTAL **
12
+ #
13
+ # Allows for a datastream to be treated like any other attribute of a model class
14
+ # while enforcing mimeType and/or datastream type (ie. external, managed, etc.) if defined.
15
+ # ====Examples
16
+ #
17
+ # has_datastream :name=>"thumbnails",:prefix => "THUMB",:type=>ActiveFedora::Datastream, :mimeType=>"image/jpeg", :controlGroup=>'M'
18
+ # has_datastream :name=>"EADs", :type=>ActiveFedora::Datastream, :mimeType=>"application/xml", :controlGroup=>'M'
19
+ # has_datastream :name=>"external_images", :type=>ActiveFedora::Datastream, :controlGroup=>'E'
20
+ #
21
+ # Required Keys in args
22
+ # :name - name to give this datastream (must be unique)
23
+ #
24
+ # Optional Keys in args
25
+ # :prefix - used to create the DSID plus an index ie. THUMB1, THUMB2. If :prefix is not specified, defaults to :name value in all uppercase
26
+ # :type - defaults to ActiveFedora::Datastream if you would like content specific class to be used supply it here
27
+ # :mimeType - if supplied it will ensure any datastreams added are of this type, if not supplied any mimeType is acceptabl e
28
+ # :controlGroup - possible values "X", "M", "R", or "E" (InlineXML, Managed Content, Redirect, or External Referenced) If controlGroup is 'E' or 'R' it expects a dsLocation be defined when adding the datastream.
29
+ #
30
+ # You use the datastream attribute using helper methods created for each datastream name:
31
+ #
32
+ # ====Helper Method Examples
33
+ # thumbnails_append - Append a thumbnail datastream
34
+ # thumbnails - Get array of thumbnail datastreams
35
+ # thumbnails_ids - Get array of dsid's for thumbnail datastreams
36
+ #
37
+ # When loading the list of datastreams for a name from Fedora it uses the DSID prefix to find them in Fedora
38
+ def has_datastream(args)
39
+ unless args.has_key?(:name)
40
+ return false
41
+ end
42
+ unless args.has_key?(:prefix)
43
+ args.merge!({:prefix=>args[:name].to_s.upcase})
44
+ end
45
+ unless class_named_datastreams_desc.has_key?(args[:name])
46
+ class_named_datastreams_desc[args[:name]] = {}
47
+ end
48
+
49
+ args.merge!({:mimeType=>args[:mime_type]}) if args.has_key?(:mime_type)
50
+
51
+ unless class_named_datastreams_desc[args[:name]].has_key?(:type)
52
+ #default to type ActiveFedora::Datastream
53
+ args.merge!({:type => "ActiveFedora::Datastream"})
54
+ end
55
+ class_named_datastreams_desc[args[:name]]= args
56
+ create_named_datastream_finders(args[:name],args[:prefix])
57
+ create_named_datastream_update_methods(args[:name])
58
+ end
59
+
60
+ # ** EXPERIMENTAL **
61
+ #
62
+ # Creates the following helper methods for a datastream name
63
+ # [datastream_name]_append - Add a named datastream
64
+ #
65
+ # ==== Examples for "thumbnails" datastream
66
+ # thumbnails_append - Append a thumbnail datastream
67
+ # TODO: Add [datastream_name]_remove
68
+ def create_named_datastream_update_methods(name)
69
+ append_file_method_name = "#{name.to_s.downcase}_file_append"
70
+ append_method_name = "#{name.to_s.downcase}_append"
71
+ #remove_method_name = "#{name.to_s.downcase}_remove"
72
+ self.send(:define_method,:"#{append_file_method_name}") do |*args|
73
+ file,opts = *args
74
+ opts ||= {}
75
+ add_named_file_datastream(name,file,opts)
76
+ end
77
+
78
+ self.send(:define_method,:"#{append_method_name}") do |*args|
79
+ #call add_named_datastream instead of add_file_named_datastream in case not managed datastream
80
+ add_named_datastream(name,*args)
81
+ end
82
+ end
83
+
84
+ # ** EXPERIMENTAL **
85
+ #
86
+ # Creates the following helper methods for a datastream name
87
+ # [datastream_name] - Returns array of named datastreams
88
+ # [datastream_name]_ids - Returns array of named datastream dsids
89
+ #
90
+ # ==== Examples for "thumbnails" datastream
91
+ # thumbnails - Get array of thumbnail datastreams
92
+ # thumbnails_ids - Get array of dsid's for thumbnail datastreams
93
+ def create_named_datastream_finders(name, prefix)
94
+ class_eval <<-END, __FILE__, __LINE__
95
+ def #{name}(opts={})
96
+ id_array = []
97
+ keys = datastreams.keys
98
+ id_array = keys.select {|v| v =~ /#{prefix}\d*/}
99
+ if opts[:response_format] == :id_array
100
+ return id_array
101
+ else
102
+ named_ds = []
103
+ id_array.each do |name|
104
+ if datastreams.has_key?(name)
105
+ named_ds.push(datastreams[name])
106
+ end
107
+ end
108
+ return named_ds
109
+ end
110
+ end
111
+ def #{name}_ids
112
+ #{name}(:response_format => :id_array)
113
+ end
114
+ END
115
+ end
116
+
117
+
118
+
119
+ end
120
+
121
+ # ** EXPERIMENTAL **
122
+ #
123
+ # Accessor for class variable for hash that stores arguments passed to has_datastream calls within an
124
+ # ActiveFedora::Base child class.
125
+ #
126
+ # has_datastream :name=>"audio_file", :prefix=>"AUDIO", :type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav"
127
+ # has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'
128
+ #
129
+ # The above examples result in the following hash
130
+ # {"audio_file"=>{:prefix=>"AUDIO",:type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav", :controlGroup=>'M'},
131
+ # "external_images=>{:prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'}}
132
+ #
133
+ # This hash is later used when adding a named datastream such as an "audio_file" as defined above.
134
+ def named_datastreams_desc
135
+ self.class_named_datastreams_desc ||= {}
136
+ end
137
+
138
+ # ** EXPERIMENTAL **
139
+ #
140
+ # Returns array of datastream names defined for this object
141
+ def datastream_names
142
+ named_datastreams_desc.keys
143
+ end
144
+
145
+ # ** EXPERIMENTAL **
146
+ #
147
+ # Calls add_named_datastream while assuming it will be managed content and sets :blob and :controlGroup values accordingly
148
+ # ====Parameters
149
+ # name: Datastream name
150
+ # file: The file to add for this datastream
151
+ # opts: Options hash. See +add_named_datastream+ for expected keys and values
152
+ def add_named_file_datastream(name, file, opts={})
153
+ opts.merge!({:blob=>file,:controlGroup=>'M'})
154
+ add_named_datastream(name,opts)
155
+ end
156
+
157
+ # ** EXPERIMENTAL **
158
+ #
159
+ # This object is used by [datastream_name]_append helper to add a named datastream
160
+ # but can also be called directly.
161
+ # ====Parameters
162
+ # name: name of datastream to add
163
+ # opts: hash defining datastream attributes
164
+ # The following are expected keys in opts hash:
165
+ # :label => Defaults to the file name
166
+ # :blob or :file => Required to point to the datastream file being added if managed content
167
+ # :controlGroup => defaults to 'M' for managed, can also be 'E' external and 'R' for redirected
168
+ # :content_type => required if the file does not respond to 'content_type' and should match :mimeType in has_datastream definition if defined
169
+ # :dsLocation => holds uri location of datastream. Required only if :controlGroup is type 'E' or 'R'.
170
+ # :dsid or :dsId => Optional, and used to update an existing datastream with dsid supplied. Will throw an error if dsid does not exist and does not match prefix pattern for datastream name
171
+ def add_named_datastream(name,opts={})
172
+ unless named_datastreams_desc.has_key?(name) && named_datastreams_desc[name].has_key?(:type)
173
+ raise "Failed to add datastream. Named datastream #{name} not defined for object #{pid}."
174
+ end
175
+
176
+ if opts.has_key?(:mime_type)
177
+ opts.merge!({:content_type=>opts[:mime_type]})
178
+ elsif opts.has_key?(:mimeType)
179
+ opts.merge!({:content_type=>opts[:mimeType]})
180
+ end
181
+ opts.merge!(named_datastreams_desc[name])
182
+
183
+ label = opts.has_key?(:label) ? opts[:label] : ""
184
+
185
+ #only do these steps for managed datastreams
186
+ unless (opts.has_key?(:controlGroup)&&opts[:controlGroup]!="M")
187
+ if opts.has_key?(:file)
188
+ opts.merge!({:blob => opts[:file]})
189
+ opts.delete(:file)
190
+ end
191
+
192
+ raise "You must define parameter blob for this managed datastream to load for #{pid}" unless opts.has_key?(:blob)
193
+
194
+ #if no explicit label and is a file use original file name for label
195
+ if !opts.has_key?(:label)&&opts[:blob].respond_to?(:original_filename)
196
+ label = opts[:blob].original_filename
197
+ end
198
+
199
+ if opts[:blob].respond_to?(:content_type)&&!opts[:blob].content_type.nil? && !opts.has_key?(:content_type)
200
+ opts.merge!({:content_type=>opts[:blob].content_type})
201
+ end
202
+
203
+ raise "The blob must respond to content_type or the hash must have :content_type or :mime_type property set" unless opts.has_key?(:content_type)
204
+
205
+ #throw error for mimeType mismatch
206
+ if named_datastreams_desc[name].has_key?(:mimeType) && !named_datastreams_desc[name][:mimeType].eql?(opts[:content_type])
207
+ raise "Content type mismatch for add datastream #{name} to #{pid}. Expected: #{named_datastreams_desc[name][:mimeType]}, Actual: #{opts[:content_type]}"
208
+ end
209
+ else
210
+ label = opts[:dsLocation] if (opts.has_key?(:dsLocation))
211
+ end
212
+
213
+ opts.merge!(:dsLabel => label)
214
+
215
+ ds = create_datastream(named_datastreams_desc[name][:type], opts[:dsid], opts)
216
+ #Must be of type datastream
217
+ assert_kind_of 'datastream', ds, ActiveFedora::Datastream
218
+ #make sure dsid is nil so that it uses the prefix for mapping purposes
219
+ #check dsid works for the prefix if it is set
220
+ if !ds.dsid.nil? && opts.has_key?(:prefix)
221
+ raise "dsid supplied does not conform to pattern #{opts[:prefix]}[number]" unless ds.dsid =~ /#{opts[:prefix]}[0-9]/
222
+ end
223
+
224
+ add_datastream(ds,opts)
225
+ end
226
+
227
+ # ** EXPERIMENTAL **
228
+ #
229
+ # Update an existing named datastream. It has same parameters as add_named_datastream
230
+ # except the :dsid key is now required.
231
+ #
232
+ # ====TODO
233
+ # Currently requires you to update file if a managed datastream
234
+ # but could change to allow metadata only updates as well
235
+ def update_named_datastream(name, opts={})
236
+ #check that dsid provided matches existing datastream with that name
237
+ raise "You must define parameter dsid for datastream to update for #{pid}" unless opts.include?(:dsid)
238
+ raise "Datastream with name #{name} and dsid #{opts[:dsid]} does not exist for #{pid}" unless self.send("#{name}_ids").include?(opts[:dsid])
239
+ add_named_datastream(name,opts)
240
+ end
241
+
242
+ # ** EXPERIMENTAL **
243
+ #
244
+ # Throws an assertion failure unless the object 'o' is kind_of? class 't'
245
+ # ====Parameters
246
+ # n: Name of object
247
+ # o: The object to test
248
+ # t: The class type to check is kind_of?
249
+ def assert_kind_of(n, o,t)
250
+ raise "Assertion failure: #{n}: #{o} is not of type #{t}" unless o.kind_of?(t)
251
+ end
252
+
253
+ # ** EXPERIMENTAL **
254
+ #
255
+ # Returns true if the name is a defined named datastream
256
+ def is_named_datastream?(name)
257
+ named_datastreams_desc.has_key?(name)
258
+ end
259
+
260
+ # ** EXPERIMENTAL **
261
+ #
262
+ # Returns hash of datastream names defined by has_datastream calls mapped to
263
+ # array of datastream objects that have been added
264
+ # ====Example
265
+ # For the following has_datastream entries and a datastream defined for minivan only would be
266
+ # has_datastream :name=>"minivan", :prefix => "VAN", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'M'
267
+ # has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'
268
+ #
269
+ # Returns
270
+ # {"external_images"=>[],"thumbnails"=>{#<ActiveFedora::Datastream:0x7ffd6512daf8 ...}}
271
+ def named_datastreams
272
+ ds_values = {}
273
+ self.class.class_named_datastreams_desc.keys.each do |name|
274
+ ds_values.merge!({name=>self.send("#{name}")})
275
+ end
276
+ return ds_values
277
+ end
278
+
279
+ # ** EXPERIMENTAL **
280
+ #
281
+ # Returns hash of datastream names mapped to an array
282
+ # of dsid's for named datastream objects
283
+ # === Example
284
+ # For the following has_datastream call, assume we have added two datastreams.
285
+ #
286
+ # has_datastream :name=>"thumbnails",:prefix => "THUMB",:type=>ActiveFedora::Datastream, :mimeType=>"image/jpeg", :controlGroup=>'M'
287
+ #
288
+ # It would then return
289
+ # {"thumbnails=>["THUMB1", "THUMB2"]}
290
+ def named_datastreams_ids
291
+ dsids = {}
292
+ self.class.class_named_datastreams_desc.keys.each do |name|
293
+ dsid_array = self.send("#{name}_ids")
294
+ dsids[name] = dsid_array
295
+ end
296
+ return dsids
297
+ end
298
+
299
+ # ** EXPERIMENTAL **
300
+ #
301
+ # Returns the hash that stores arguments passed to has_datastream calls within an
302
+ # ActiveFedora::Base child class.
303
+ #
304
+ # has_datastream :name=>"audio_file", :prefix=>"AUDIO", :type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav"
305
+ # has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'
306
+ #
307
+ # The above examples result in the following hash
308
+ # {"audio_file"=>{:prefix=>"AUDIO",:type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav", :controlGroup=>'M'},
309
+ # "external_images=>{:prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'}}
310
+ #
311
+ # This hash is later used when adding a named datastream such as an "audio_file" as defined above.
312
+ def named_datastreams_desc
313
+ @named_datastreams_desc ||= self.class.class_named_datastreams_desc
314
+ end
315
+
316
+ # ** EXPERIMENTAL **
317
+ #
318
+ # Get class variable hash that stores has_datastream arguments.
319
+ # It is used to initialize the value returned by public named_datastreams_desc method
320
+ # Deprecated
321
+ def named_datastreams_desc_from_class
322
+ logger.warn "named_datastreams_desc_from_class is deprecated and will be removed in the next version"
323
+ self.class.class_named_datastreams_desc
324
+ end
325
+
326
+ end
@@ -8,10 +8,9 @@ module ActiveFedora
8
8
  # </fields>
9
9
  class MetadataDatastream < Datastream
10
10
 
11
+ # .to_solr (among other things) is provided by ActiveFedora::MetadataDatastreamHelper
11
12
  include ActiveFedora::MetadataDatastreamHelper
12
13
 
13
- # .to_solr and .to_xml (among other things) are provided by ActiveFedora::MetadataDatastreamHelper
14
- self.xml_model = ActiveFedora::MetadataDatastream
15
14
 
16
15
  def update_attributes(params={},opts={})
17
16
  result = params.dup
@@ -144,6 +143,29 @@ module ActiveFedora
144
143
  tmpl.send(:dirty=, false)
145
144
  tmpl
146
145
  end
146
+
147
+ def to_xml(xml = Nokogiri::XML::Document.parse("<fields />")) #:nodoc:
148
+ if xml.instance_of?(Nokogiri::XML::Builder)
149
+ xml_insertion_point = xml.doc.root
150
+ elsif xml.instance_of?(Nokogiri::XML::Document)
151
+ xml_insertion_point = xml.root
152
+ else
153
+ xml_insertion_point = xml
154
+ end
155
+
156
+ builder = Nokogiri::XML::Builder.with(xml_insertion_point) do |xml|
157
+ fields.each_pair do |field,field_info|
158
+ element_attrs = field_info[:element_attrs].nil? ? {} : field_info[:element_attrs]
159
+ values = field_info[:values]
160
+ values = [values] unless values.respond_to? :each
161
+ values.each do |val|
162
+ builder_arg = "xml.#{field}(val, element_attrs)"
163
+ eval(builder_arg)
164
+ end
165
+ end
166
+ end
167
+ return builder.to_xml
168
+ end
147
169
 
148
170
  # This method generates the various accessor and mutator methods on self for the datastream metadata attributes.
149
171
  # each field will have the 3 magic methods:
@@ -7,8 +7,6 @@ module ActiveFedora::MetadataDatastreamHelper
7
7
 
8
8
  module ClassMethods
9
9
 
10
- attr_accessor :xml_model
11
-
12
10
  #get the Class's field list
13
11
  def fields
14
12
  @@classFields
@@ -28,7 +26,9 @@ module ActiveFedora::MetadataDatastreamHelper
28
26
  end
29
27
 
30
28
  def serialize! # :nodoc:
31
- self.content = self.to_xml ##TODO only do this when the xml will have changed to avoid a load of the datastream content.
29
+ if dirty?
30
+ self.content = self.to_xml
31
+ end
32
32
  end
33
33
 
34
34
  def to_solr(solr_doc = Hash.new) # :nodoc:
@@ -70,27 +70,5 @@ module ActiveFedora::MetadataDatastreamHelper
70
70
  end
71
71
  end
72
72
 
73
- def to_xml(xml = Nokogiri::XML::Document.parse("<fields />")) #:nodoc:
74
- if xml.instance_of?(Nokogiri::XML::Builder)
75
- xml_insertion_point = xml.doc.root
76
- elsif xml.instance_of?(Nokogiri::XML::Document)
77
- xml_insertion_point = xml.root
78
- else
79
- xml_insertion_point = xml
80
- end
81
-
82
- builder = Nokogiri::XML::Builder.with(xml_insertion_point) do |xml|
83
- fields.each_pair do |field,field_info|
84
- element_attrs = field_info[:element_attrs].nil? ? {} : field_info[:element_attrs]
85
- values = field_info[:values]
86
- values = [values] unless values.respond_to? :each
87
- values.each do |val|
88
- builder_arg = "xml.#{field}(val, element_attrs)"
89
- eval(builder_arg)
90
- end
91
- end
92
- end
93
- return builder.to_xml
94
- end
95
73
 
96
74
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveFedora
2
- VERSION = "3.1.5"
2
+ VERSION = "3.1.6"
3
3
  end
@@ -6,9 +6,13 @@ end
6
6
 
7
7
  class Book < ActiveFedora::Base
8
8
  belongs_to :library, :property=>:has_constituent
9
+ belongs_to :author, :property=>:has_member, :class_name=>'Person'
9
10
  has_and_belongs_to_many :topics, :property=>:is_topic_of
10
11
  end
11
12
 
13
+ class Person < ActiveFedora::Base
14
+ end
15
+
12
16
  class Topic < ActiveFedora::Base
13
17
  has_and_belongs_to_many :books, :property=>:is_topic_of
14
18
  end
@@ -112,6 +116,8 @@ describe ActiveFedora::Base do
112
116
  @library.save()
113
117
  @book = Book.new
114
118
  @book.save
119
+ @person = Person.new
120
+ @person.save
115
121
  end
116
122
  it "should have many books once it has been saved" do
117
123
  @library.books << @book
@@ -124,6 +130,14 @@ describe ActiveFedora::Base do
124
130
  @library2 = Library.find(@library.pid)
125
131
  @library2.books.map(&:pid).should == [@book.pid]
126
132
  end
133
+
134
+ it "should respect the :class_name parameter" do
135
+ @book.author = @person
136
+ @book.save
137
+ Book.find(@book.id).author_id.should == @person.pid
138
+ Book.find(@book.id).author.send(:find_target).should be_kind_of Person
139
+ end
140
+
127
141
  after do
128
142
  @library.delete
129
143
  @book.delete
@@ -1,4 +1,3 @@
1
- # encoding: WINDOWS-1252
2
1
  require 'spec_helper'
3
2
  require 'rexml/document'
4
3
  require "active_fedora/samples"
@@ -69,7 +68,7 @@ describe ActiveFedora::Base do
69
68
 
70
69
  end
71
70
  sample_location = "Boston, Massachusetts"
72
- sample_notes = 'Addelson, Frances. (1973?) “The Induced Abortion American Journal of Ortho-Psychiatry, Addelson, Frances. “Abortion: Source of Guilt or Growth National Journal of Gynecology and Obstetrics., Addelson, Frances. “First Zionist Novel Jewish Frontier.'
71
+ sample_notes = 'Addelson, Frances. (1973?) "The Induced Abortion," American Journal of Ortho-Psychiatry, Addelson, Frances. "Abortion: Source of Guilt or Growth," National Journal of Gynecology and Obstetrics., Addelson, Frances. "First Zionist Novel," Jewish Frontier.'
73
72
  sample_other_contributor = 'any other contributors, people or corporate names (eg. Temple Israel)'
74
73
  sample_transcript_editor = 'Siegel, Cheryl'
75
74
  sample_hard_copy_availability = <<-END
@@ -77,7 +76,7 @@ describe ActiveFedora::Base do
77
76
  END
78
77
  sample_narrator = 'Addelson, Frances'
79
78
  sample_bio = <<-END
80
- Rochelle Ruthchild interviewed Frances Addleson on October 18, November 14, and December 10, 1997. The interview thoroughly examined the trajectory of Frances’ life from birth until the time of the interview. As a young child, Frances’ father died during the influenza epidemic, and her mother was not equipped to care for her and her siblings. Consequently, they were placed in a Jewish foster home. Although her experience was mostly positive, this experience would leave life-long effects. Frances attended Radcliffe upon the urging of a mentor and later obtained her Master’s degree in social work from Simmons College in 1954. In the 1940’s, she returned to work while her children were still young; a rather unusual event for that time period. While working as a social worker at Beth Israel Hospital in the early 1970’s, she helped counsel countless women who came to the hospital seeking abortions before the procedure was officially legalized during the landmark Roe vs. Wade decision in 1973. Frances would later write two articles that were published in medical journals about her experience during this time. Although not a very religious person, Frances felt connected to the Jewish notion of social justice and remained very active until an accident in the late 1990’s.
79
+ Rochelle Ruthchild interviewed Frances Addleson on October 18, November 14, and December 10, 1997. The interview thoroughly examined the trajectory of Frances\' life from birth until the time of the interview. As a young child, Frances\' father died during the influenza epidemic, and her mother was not equipped to care for her and her siblings. Consequently, they were placed in a Jewish foster home. Although her experience was mostly positive, this experience would leave life-long effects. Frances attended Radcliffe upon the urging of a mentor and later obtained her Master\'s degree in social work from Simmons College in 1954. In the 1940\'s, she returned to work while her children were still young; a rather unusual event for that time period. While working as a social worker at Beth Israel Hospital in the early 1970\'s, she helped counsel countless women who came to the hospital seeking abortions before the procedure was officially legalized during the landmark Roe vs. Wade decision in 1973. Frances would later write two articles that were published in medical journals about her experience during this time. Although not a very religious person, Frances felt connected to the Jewish notion of social justice and remained very active until an accident in the late 1990\'s.
81
80
  END
82
81
  sample_interviewer = "Ruthchild, & Rochelle"
83
82
 
@@ -59,82 +59,84 @@ describe ActiveFedora::Base do
59
59
  has_datastream :name=>"anymime", :type=>ActiveFedora::Datastream, :controlGroup=>'M'
60
60
  has_datastream :name=>"external", :type=>ActiveFedora::Datastream, :controlGroup=>'E'
61
61
  end
62
-
63
- it 'should add a named datastream to a fedora object' do
62
+
63
+ before do
64
64
  @test_object2 = MockAddNamedDatastream.new
65
- f = File.new(File.join( File.dirname(__FILE__), "../fixtures/minivan.jpg"))
66
- f2 = File.new(File.join( File.dirname(__FILE__), "../fixtures/dino.jpg" ))
67
- f2.stubs(:original_filename).returns("dino.jpg")
68
- f.stubs(:content_type).returns("image/jpeg")
69
- #check cannot add a datastream with name that does not exist
70
- had_exception = false
71
- begin
72
- @test_object2.add_named_datastream("thumb",{:content_type=>"image/jpeg",:blob=>f})
73
- rescue
74
- had_exception = true
75
- end
76
- raise "Did not raise exception with datastream name that does not exist" unless had_exception
77
- #check that either blob or file opt must be set
78
- had_exception = false
79
- begin
80
- @test_object2.add_named_datastream("thumbnail",{:content_type=>"image/jpeg"})
81
- rescue
82
- had_exception = true
83
- end
84
- raise "Did not raise exception with blob not set" unless had_exception
65
+ @f = File.new(File.join( File.dirname(__FILE__), "../fixtures/minivan.jpg"))
66
+ @f2 = File.new(File.join( File.dirname(__FILE__), "../fixtures/dino.jpg" ))
67
+ @f.stubs(:content_type).returns("image/jpeg")
68
+ @f2.stubs(:original_filename).returns("dino.jpg")
69
+ end
85
70
 
86
- @test_object2.add_named_datastream("thumbnail",{:content_type=>"image/jpeg",:blob=>f})
87
- @test_object2.add_named_datastream("thumbnail",{:content_type=>"image/jpeg",:file=>f})
88
- #check dslabel set from either opt[label] or opt[blob].original_file_name
89
- @test_object2.add_named_datastream("high",{:content_type=>"image/jpeg",:blob=>f2, :label=>"my_image"})
71
+ it 'cannot add a datastream with name that does not exist' do
72
+ expect { @test_object2.add_named_datastream("thumb",{:content_type=>"image/jpeg",:blob=>f}) }.to raise_error
73
+ end
74
+
75
+ it "should accept a file blob" do
76
+ @test_object2.add_named_datastream("thumbnail",{:content_type=>"image/jpeg",:blob=>@f})
77
+ end
78
+
79
+ it "should accept a file handle" do
80
+ @test_object2.add_named_datastream("thumbnail",{:content_type=>"image/jpeg",:file=>@f})
81
+ end
82
+
83
+ it "should raise an error if neither a blob nor file is set" do
84
+ expect { @test_object2.add_named_datastream("thumbnail",{:content_type=>"image/jpeg"}) }.to raise_error
85
+ end
86
+
87
+ it "should use the given label for the dsLabel" do
88
+ @test_object2.add_named_datastream("high",{:content_type=>"image/jpeg",:blob=>@f2, :label=>"my_image"})
90
89
  @test_object2.high.first.dsLabel.should == "my_image"
91
- @test_object2.add_named_datastream("high",{:content_type=>"image/jpeg",:blob=>f2})
90
+ end
91
+
92
+ it "should fallback on using the file name" do
93
+ @test_object2.add_named_datastream("high",{:content_type=>"image/jpeg",:blob=>@f2})
92
94
  @test_object2.high.first.dsLabel.should == "dino.jpg"
93
- #check opt[content_type] must be set or opt[blob] responds to content_type, already checking if content_type option set above
94
- f.expects(:content_type).returns("image/jpeg")
95
- @test_object2.add_named_datastream("thumbnail",{:file=>f})
96
- had_exception = false
97
- begin
98
- @test_object2.add_named_datastream("thumbnail",{:file=>f2})
99
- rescue
100
- had_exception = true
101
- end
102
- raise "Did not raise exception if content_type not set" unless had_exception
103
- #check mimetype and content type must match if mimetype is specified (if not specified can be anything)
104
- f.stubs(:content_type).returns("image/tiff")
105
- had_exception = false
106
- begin
107
- @test_object2.add_named_datastream("thumbnail",{:file=>f})
108
- rescue
109
- had_exception = true
110
- end
111
- raise "Did not raise exception on content type and mime type mismatch" unless had_exception
95
+ end
96
+
97
+ it "should check the file for a content type" do
98
+ @f.expects(:content_type).returns("image/jpeg")
99
+ @test_object2.add_named_datastream("thumbnail",{:file=>@f})
100
+ end
101
+
102
+ it "should raise an error if no content type is avialable" do
103
+ expect { @test_object2.add_named_datastream("thumbnail",{:file=>@f2}) }.to raise_error
104
+ end
105
+
106
+ it "should encsure mimetype and content type match" do
107
+ @f.stubs(:content_type).returns("image/tiff")
108
+ expect { @test_object2.add_named_datastream("thumbnail",{:file=>f}) }.to raise_error
109
+ end
112
110
 
111
+ it "should allow any mime type" do
113
112
  #check for if any mime type allowed
114
- @test_object2.add_named_datastream("anymime",{:file=>f})
113
+ @test_object2.add_named_datastream("anymime",{:file=>@f})
115
114
  #check datastream created is of type ActiveFedora::Datastream
116
115
  @test_object2.anymime.first.class.should == ActiveFedora::Datastream
116
+ end
117
+
118
+ it "should cgecj that a dsid forms to the prefix" do
117
119
  #if dsid supplied check that conforms to prefix
118
- f.stubs(:content_type).returns("image/jpeg")
119
- had_exception = false
120
- begin
121
- @test_object2.add_named_datastream("thumbnail",{:file=>f,:dsid=>"DS1"})
122
- rescue
123
- had_exception = true
124
- end
125
- raise "Did not raise exception with dsid that does not conform to prefix" unless had_exception
120
+ @f.stubs(:content_type).returns("image/jpeg")
121
+ expect { @test_object2.add_named_datastream("thumbnail",{:file=>@f,:dsid=>"DS1"}) }.to raise_error
122
+ end
123
+
124
+ it "should have the right properties" do
125
+ @test_object2.add_named_datastream("high",{:content_type=>"image/jpeg",:blob=>@f2})
126
126
  #if prefix not set check uses name in CAPS and dsid uses prefix
127
127
  #@test_object2.high.first.attributes[:prefix].should == "HIGH"
128
128
  @test_object2.high.first.dsid.match(/HIGH[0-9]/)
129
129
  #check datastreams added with other right properties
130
130
  @test_object2.high.first.controlGroup.should == "M"
131
+ end
132
+
133
+ it "should work with external datastreams" do
131
134
 
132
135
  #check external datastream
133
136
  @test_object2.add_named_datastream("external",{:dsLocation=>"http://myreasource.com"})
134
137
  #check dslocation goes to dslabel
135
138
  @test_object2.external.first.dsLabel.should == "http://myreasource.com"
136
139
  #check datastreams added to fedora (may want to stub this at first)
137
-
138
140
  end
139
141
  end
140
142
 
@@ -240,6 +240,23 @@ describe ActiveFedora::Base do
240
240
  end
241
241
 
242
242
 
243
+ describe ".datastreams" do
244
+ it "should create dynamic accessors" do
245
+ @test_history.withText.should == @test_history.datastreams['withText']
246
+ end
247
+ it "dynamic accessors should convert dashes to underscores" do
248
+ ds = stub('datastream', :dsid=>'eac-cpf')
249
+ @test_history.add_datastream(ds)
250
+ @test_history.eac_cpf.should == ds
251
+ end
252
+ it "dynamic accessors should not convert datastreams named with underscore" do
253
+ ds = stub('datastream', :dsid=>'foo_bar')
254
+ @test_history.add_datastream(ds)
255
+ @test_history.foo_bar.should == ds
256
+ end
257
+ end
258
+
259
+
243
260
 
244
261
  describe ".fields" do
245
262
  it "should provide fields" do
@@ -44,6 +44,7 @@ describe ActiveFedora::MetadataDatastream do
44
44
  @mock_repo.expects(:datastream).with(:pid => nil, :dsid => 'mdDs')
45
45
  @mock_repo.expects(:add_datastream).with(:pid => nil, :dsid => 'mdDs', :checksumType => 'DISABLED', :versionable => true, :content => 'fake xml', :controlGroup => 'M', :dsState => 'A', :mimeType=>'text/xml')
46
46
  @test_ds.expects(:to_xml).returns("fake xml")
47
+ @test_ds.expects(:dirty?).returns(true)
47
48
  @test_ds.serialize!
48
49
  @test_ds.save
49
50
  @test_ds.mimeType.should == 'text/xml'
@@ -135,6 +135,7 @@ describe ActiveFedora::QualifiedDublinCoreDatastream do
135
135
  it "should call .content= with to_dc_xml" do
136
136
  result = @test_ds.to_dc_xml
137
137
  @test_ds.expects(:content=).with(result)
138
+ @test_ds.expects(:dirty?).returns(true)
138
139
  @test_ds.serialize!
139
140
  end
140
141
  end
@@ -81,9 +81,6 @@ describe ActiveFedora do
81
81
  it "should prevent Base.save from calling update_index if false" do
82
82
  dirty_ds = ActiveFedora::MetadataDatastream.new(@test_object.inner_object, 'ds1')
83
83
  @test_object.datastreams['ds1'] = dirty_ds
84
- repo = @test_object.inner_object.repository
85
- repo.expects(:add_datastream)
86
- repo.expects(:ingest)
87
84
  @test_object.stubs(:datastreams).returns({:ds1 => dirty_ds})
88
85
  @test_object.expects(:update_index).never
89
86
  @test_object.expects(:refresh)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active-fedora
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 3
8
8
  - 1
9
- - 5
10
- version: 3.1.5
9
+ - 6
10
+ version: 3.1.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt Zumwalt
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-12-14 00:00:00 -06:00
19
+ date: 2012-01-05 00:00:00 -06:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -105,12 +105,12 @@ dependencies:
105
105
  requirements:
106
106
  - - ">="
107
107
  - !ruby/object:Gem::Version
108
- hash: 1
108
+ hash: 15
109
109
  segments:
110
110
  - 1
111
111
  - 4
112
- - 3
113
- version: 1.4.3
112
+ - 4
113
+ version: 1.4.4
114
114
  requirement: *id006
115
115
  - !ruby/object:Gem::Dependency
116
116
  name: solrizer
@@ -561,6 +561,7 @@ files:
561
561
  - lib/active_fedora/base.rb
562
562
  - lib/active_fedora/content_model.rb
563
563
  - lib/active_fedora/datastream.rb
564
+ - lib/active_fedora/datastream_collections.rb
564
565
  - lib/active_fedora/datastream_hash.rb
565
566
  - lib/active_fedora/delegating.rb
566
567
  - lib/active_fedora/digital_object.rb