cul_scv_hydra 0.11.0 → 0.12.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,17 +1,7 @@
1
- require "active-fedora"
2
- require "active_fedora_finders"
3
- require "hydra-core"
4
- class BagAggregator < ::ActiveFedora::Base
5
- extend ActiveModel::Callbacks
6
- include ::ActiveFedora::Finders
7
- include ::ActiveFedora::DatastreamCollections
8
- include ::Hydra::ModelMethods
9
- include Cul::Scv::Hydra::Models::Common
10
- include Cul::Scv::Hydra::Models::Aggregator
11
-
12
- alias :file_objects :resources
1
+ class BagAggregator < GenericAggregator
13
2
 
14
3
  def route_as
15
4
  "collection"
16
5
  end
6
+
17
7
  end
@@ -8,9 +8,12 @@ module Hydra
8
8
  module Models
9
9
  AGGREGATOR_TYPE = (URI.parse("http://purl.oclc.org/NET/CUL/Aggregator"))
10
10
  RESOURCE_TYPE = (URI.parse("http://purl.oclc.org/NET/CUL/Resource"))
11
- MEMBER_QUERY = <<-SPARQL
11
+ MEMBER_SPARQL = <<-SPARQL
12
12
  SELECT ?pid WHERE { ?pid <http://purl.oclc.org/NET/CUL/memberOf> <info:fedora/%PID%> }
13
13
  SPARQL
14
+ MEMBER_ITQL = <<-ITQL
15
+ select $pid from <#ri> where $pid <http://purl.oclc.org/NET/CUL/memberOf> <info:fedora/%PID%>
16
+ ITQL
14
17
  end
15
18
  end
16
19
  end
@@ -3,8 +3,8 @@ module Aggregator
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
+ has_and_belongs_to_many :containers, :property=>:cul_member_of, :class_name=>'ActiveFedora::Base'
6
7
  has_metadata :name => "structMetadata", :type=>Cul::Scv::Hydra::Datastreams::StructMetadata, :versionable => true, :controlGroup => 'M'
7
- has_many :parts, :property => :cul_member_of, :class_name=>'ActiveFedora::Base'
8
8
  after_create :aggregator!
9
9
  end
10
10
 
@@ -30,7 +30,98 @@ module Aggregator
30
30
  member.save
31
31
  end
32
32
 
33
+ def solr_members(opts={})
34
+ opts = {:rows=>25,:response_format=>:solr}.merge(opts)
35
+ self.parts(opts)
36
+ #opts = {:rows=>25,:response_format=>:solr}.merge(opts)
37
+ #query = self.class.inbound_relationship_query(self.pid, "parts")
38
+ #solr_result = ::ActiveFedora::SolrService.instance.conn.query(query, :rows=>opts[:rows])
39
+ #if opts[:response_format] == :solr
40
+ # return solr_result
41
+ #else
42
+ # if opts[:response_format] == :id_array
43
+ # id_array = []
44
+ # solr_result.hits.each do |hit|
45
+ # id_array << hit[SOLR_DOCUMENT_ID]
46
+ # end
47
+ # return id_array
48
+ # elsif opts[:response_format] == :load_from_solr || self.load_from_solr
49
+ # return ::ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
50
+ # else
51
+ # return ::ActiveFedora::SolrService.reify_solr_results(solr_result)
52
+ # end
53
+ #end
54
+ end
55
+
56
+ def members(opts={})
57
+ solr_members(opts)
58
+ end
59
+
60
+ def members_ids(opts={})
61
+ opts = opts.merge({:response_format=>:id_array})
62
+ solr_members(opts)
63
+ end
64
+
65
+ def thumbnail_info
66
+ r = self.parts.reader(:response_format => :solr)
67
+ members = []
68
+ r.collect {|hit| members << SolrDocument.new(hit) } unless r.blank?
69
+ if members.length == 0
70
+ return {:asset=>"cul_scv_hydra/crystal/file.png",:mime=>'image/png'}
71
+ else
72
+ thumb = nil
73
+ unless datastreams['structMetadata'].new?
74
+ thumb = thumb_from_struct(members)
75
+ else
76
+ thumb = thumb_from_members(members)
77
+ end
78
+ end
79
+ return thumb || {:asset=>"cul_scv_hydra/crystal/file.png",:mime=>'image/png'}
80
+ end
81
+
33
82
  private
83
+ def thumb_from_struct(members)
84
+ sm = datastreams['structMetadata']
85
+ first = sm.divs_with_attribute(false,'ORDER','1').first
86
+ if first
87
+ members.each do |member|
88
+ if member["identifier_ssim"].include? first["CONTENTIDS"]
89
+ return thumb_from_solr_doc(member)
90
+ end
91
+ end
92
+ return nil
93
+ else
94
+ return nil
95
+ end
96
+ end
97
+
98
+ def thumb_from_members(members)
99
+ sorted = members.sort do |a,b|
100
+ c = a['title_si'] <=> b['title_si']
101
+ if c == 0 && a['identifier_ssim']
102
+ if b['identifier_ssim']
103
+ a['identifier_ssim'].delete(a.id) unless a['identifier_ssim'].length == 1
104
+ b['identifier_ssim'].delete(a.id) unless b['identifier_ssim'].length == 1
105
+ a['identifier_ssim'][0] <=> b['identifier_ssim'][0]
106
+ else
107
+ -1
108
+ end
109
+ else
110
+ c
111
+ end
112
+ end
113
+ thumb_from_solr_doc(sorted[0])
114
+ end
115
+
116
+ def thumb_from_solr_doc(solr_doc)
117
+ if solr_doc and (member = ActiveFedora::Base.find(solr_doc.id, :cast=>true)).respond_to? :thumbnail_info
118
+ puts "thumb: " + (thumb = member.thumbnail_info).inspect
119
+ thumb
120
+ else
121
+ return nil
122
+ end
123
+ end
124
+
34
125
  def to_uri(obj)
35
126
  if obj.respond_to? :internal_uri
36
127
  return obj.internal_uri
@@ -3,15 +3,10 @@ require 'uri'
3
3
  module Cul::Scv::Hydra::Models
4
4
  module Common
5
5
  extend ActiveSupport::Concern
6
- DC_SPEC = {
7
- :name => "DC", :type=>Cul::Scv::Hydra::Datastreams::DCMetadata,
8
- :versionable => true, :label => 'Dublin Core Record for this object'
9
- }
6
+
10
7
  included do
11
8
  define_model_callbacks :create
12
- has_and_belongs_to_many :containers, :property=>:cul_member_of, :class_name=>'ActiveFedora::Base'
13
9
  has_metadata :name => "DC", :type=>Cul::Scv::Hydra::Datastreams::DCMetadata, :versionable => true
14
- #self.ds_specs['DC'] = DC_SPEC
15
10
  has_metadata :name => "descMetadata", :type=>Cul::Scv::Hydra::Datastreams::ModsDocument, :versionable => true
16
11
  has_metadata :name => "rightsMetadata", :type=>::Hydra::Datastream::RightsMetadata, :versionable => true
17
12
 
@@ -22,30 +17,6 @@ module Common
22
17
  'ldpd'
23
18
  end
24
19
 
25
- # override a buggy impl in AF
26
- def has_datastream(args)
27
- unless args.has_key?(:name)
28
- return false
29
- end
30
- unless args.has_key?(:prefix)
31
- args.merge!({:prefix=>args[:name].to_s.upcase})
32
- end
33
- unless class_named_datastreams_desc.has_key?(args[:name])
34
- class_named_datastreams_desc[args[:name]] = {}
35
- end
36
-
37
- args.merge!({:mimeType=>args[:mime_type]}) if args.has_key?(:mime_type)
38
-
39
- unless class_named_datastreams_desc[args[:name]].has_key?(:type)
40
- #default to type ActiveFedora::Datastream
41
- args[:type] ||= ActiveFedora::Datastream
42
- end
43
- args[:type] = args[:type].constantize if args[:type].is_a? String
44
- raise args[:type] unless args[:type].name
45
- class_named_datastreams_desc[args[:name]]= args
46
- create_named_datastream_finders(args[:name],args[:prefix])
47
- create_named_datastream_update_methods(args[:name])
48
- end
49
20
  end
50
21
 
51
22
  def rdf_type
@@ -63,44 +34,6 @@ module Common
63
34
  super
64
35
  end
65
36
  end
66
-
67
- def resources(opts={})
68
- if self.respond_to? :parts # aggregator
69
- opts = {:rows=>25,:response_format=>:solr}.merge(opts)
70
- self.parts(opts)
71
- #opts = {:rows=>25,:response_format=>:solr}.merge(opts)
72
- #query = self.class.inbound_relationship_query(self.pid, "parts")
73
- #solr_result = ::ActiveFedora::SolrService.instance.conn.query(query, :rows=>opts[:rows])
74
- #if opts[:response_format] == :solr
75
- # return solr_result
76
- #else
77
- # if opts[:response_format] == :id_array
78
- # id_array = []
79
- # solr_result.hits.each do |hit|
80
- # id_array << hit[SOLR_DOCUMENT_ID]
81
- # end
82
- # return id_array
83
- # elsif opts[:response_format] == :load_from_solr || self.load_from_solr
84
- # return ::ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
85
- # else
86
- # return ::ActiveFedora::SolrService.reify_solr_results(solr_result)
87
- # end
88
- #end
89
- else
90
- logger.warn 'parts not defined; was this an Aggregator?'
91
- []
92
- end
93
- end
94
-
95
- def members(opts={})
96
- resources(opts)
97
- end
98
-
99
- def members_ids(opts={})
100
- opts = opts.merge({:response_format=>:id_array})
101
- resources(opts)
102
- end
103
-
104
37
 
105
38
  def cmodel_pid(klass)
106
39
  klass.pid_namespace + ":" + klass.name.split("::")[-1]
@@ -129,23 +62,6 @@ module Common
129
62
  "default"
130
63
  end
131
64
 
132
- def index_type_label
133
- riquery = Cul::Scv::Hydra::Models::MEMBER_QUERY.gsub(/%PID%/, self.pid)
134
- begin
135
- docs = ::ActiveFedora::Base.connection_for_pid(self.pid).find_by_sparql riquery
136
- rescue Exception=>e
137
- docs = self.parts
138
- end
139
- if docs.size == 0
140
- label = "EMPTY"
141
- elsif docs.size == 1
142
- label = "SINGLE PART"
143
- else
144
- label = "MULTIPART"
145
- end
146
- label
147
- end
148
-
149
65
  def has_desc?
150
66
  has_desc = false
151
67
  begin
@@ -159,7 +75,7 @@ module Common
159
75
  end
160
76
 
161
77
  def to_solr(solr_doc = Hash.new, opts={})
162
- super
78
+ solr_doc = super(solr_doc, opts)
163
79
  if has_desc?
164
80
  solr_doc["descriptor_ssi"] = ["mods"]
165
81
  else
@@ -0,0 +1,80 @@
1
+ module Cul::Scv::Hydra::Models
2
+ module ImageResource
3
+ IMAGE_EXT = {"image/bmp" => 'bmp', "image/gif" => 'gif', "image/jpeg" => 'jpg', "image/png" => 'png', "image/tiff" => 'tif', "image/x-windows-bmp" => 'bmp', 'image/jp2' => 'jp2'}
4
+ WIDTH = RDF::URI(ActiveFedora::Predicates.find_graph_predicate(:image_width))
5
+ LENGTH = RDF::URI(ActiveFedora::Predicates.find_graph_predicate(:image_length))
6
+ WIDTH_PREDICATE = ActiveFedora::Predicates.short_predicate("http://www.w3.org/2003/12/exif/ns#imageWidth").to_s
7
+ LENGTH_PREDICATE = ActiveFedora::Predicates.short_predicate("http://www.w3.org/2003/12/exif/ns#imageLength").to_s
8
+ EXTENT_PREDICATE = ActiveFedora::Predicates.short_predicate("http://purl.org/dc/terms/extent").to_s
9
+ FORMAT_OF_PREDICATE = ActiveFedora::Predicates.short_predicate("http://purl.org/dc/terms/isFormatOf").to_s
10
+ FORMAT_URI = RDF::URI("http://purl.org/dc/terms/format")
11
+
12
+ DJATOKA_THUMBNAIL_PARMS = {
13
+ "url_ver" => "Z39.88-2004",
14
+ "svc_id" => "info:lanl-repo/svc/getRegion",
15
+ "svc_val_fmt" => "info:ofi/fmt:kev:mtx:jpeg2000",
16
+ "svc.format" => "image/jpeg",
17
+ "svc.level" => "",
18
+ "svc.rotate" => "0",
19
+ "svc.scale" => "200",
20
+ "svc.clayers" => ""
21
+ }
22
+
23
+ DJATOKA_BASE_URL = "http://iris.cul.columbia.edu:8888/resolve"
24
+
25
+ def long
26
+ @long_side ||= max(width(), length())
27
+ end
28
+
29
+ def width
30
+ @width ||= begin
31
+ ds = datastreams["content"]
32
+ width = 0
33
+ unless ds.nil? or rels_int.relationships(ds,:exif_image_width).blank?
34
+ width = rels_int.relationships(ds,:exif_image_width).first.object.to_s.to_i
35
+ end
36
+ width = relationships(:image_width).first.to_s.to_i if width == 0
37
+ width
38
+ end
39
+ end
40
+
41
+ def length
42
+ @length ||= begin
43
+ ds = datastreams["content"]
44
+ length = 0
45
+ unless ds.nil? or rels_int.relationships(ds,:exif_image_length).blank?
46
+ length = rels_int.relationships(ds,:exif_image_length).first.object.to_s.to_i
47
+ end
48
+ length = relationships(:image_length).first.to_s.to_i if length == 0
49
+ length
50
+ end
51
+ end
52
+
53
+ def zooming?
54
+ zoom = rels_int.relationships(datastreams['content'],:foaf_zooming).first
55
+ return zoom.object.to_s if zoom
56
+ datastreams.each do |k,v|
57
+ if v.mimeType =~ /image\/jp2$/i
58
+ zoom = "info:fedora/#{k.dsid}"
59
+ end
60
+ end
61
+ return zoom
62
+ end
63
+
64
+ def thumbnail_info
65
+ thumb = rels_int.relationships(datastreams['content'],:foaf_thumbnail).first
66
+ if thumb
67
+ t_dsid = thumb.object.to_s.split('/')[-1]
68
+ t_url = "#{ActiveFedora.fedora_config.credentials[:url]}/objects/#{pid}/datastreams/#{t_dsid}/content"
69
+ return {:url=>t_url,:mime=>datastreams[t_dsid].mimeType}
70
+ elsif (zoom = self.zooming?)
71
+ t_dsid = zoom.split('/')[-1]
72
+ t_parms = DJATOKA_THUMBNAIL_PARMS.merge({"rft_id" => datastreams[t_dsid].dsLocation})
73
+ url = "#{DJATOKA_BASE_URL}?#{options.map { |key, value| "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"}.join("&") }"
74
+ {:url => url, :mime => t_parms["svc.format"]}
75
+ else
76
+ return {:asset=>"cul_scv_hydra/crystal/file.png",:mime=>'image/png'}
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,111 @@
1
+ module Cul::Scv::Hydra::Models
2
+ module LinkableResources
3
+ include Cul::Scv::Fedora::UrlHelperBehavior
4
+ # so as to avoid including all the url hepers via:
5
+ ## include Rails.application.routes.url_helpers
6
+ # we are just going to delegate
7
+ delegate :fedora_content_path, :to => 'Rails.application.routes.url_helpers'
8
+ delegate :cache_path, :to => 'Rails.application.routes.url_helpers'
9
+
10
+ def http_client
11
+ unless @http_client
12
+ @http_client ||= HTTPClient.new
13
+ @http_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
14
+ uname = Cul::Scv::Fedora.repository.config[:user]
15
+ pwd = Cul::Scv::Fedora.repository.config[:password]
16
+ @http_client.set_auth(nil, uname, pwd)
17
+ end
18
+ @http_client
19
+ end
20
+
21
+ def linkable_resources
22
+ r = self.parts(:response_format => :solr)
23
+ return [] if r.blank?
24
+ members = r.collect {|hit|
25
+ SolrDocument.new(hit)
26
+ }
27
+ members.delete_if { |sd| (sd[:has_model_ssim] & ["info:fedora/ldpd:Resource"]).blank? }
28
+ case self.route_as
29
+ when "zoomingimage"
30
+ results = members.collect {|doc| image_resource(doc)}
31
+ base_id = self.pid
32
+ url = fedora_ds_url(base_id, 'SOURCE') + '/content'
33
+ head_req = http_client().head(url)
34
+ file_size = head_req.header["Content-Length"].first.to_i
35
+ results << {
36
+ :dimensions => "Original",
37
+ :mime_type => "image/jp2",
38
+ :uri=>base_id, :block=>"SOURCE", :filename=>base_id + "_source.jp2",
39
+ :content_models=>[]}
40
+ when "audio"
41
+ results = members.collect {|doc| audio_resource(doc)}
42
+ when "image"
43
+ results = members.collect {|doc| image_resource(doc)}
44
+ else
45
+ raise "Unknown format #{self.route_as}"
46
+ end
47
+ return results
48
+ end
49
+
50
+ def basic_resource(document)
51
+ res = {}
52
+ res[:pid] = document["id"]
53
+ res[:dsid] = "CONTENT"
54
+ res[:mime_type] = document["dc_format_ssm"] ? document["dc_format_ssm"].first : "application/octect-stream"
55
+ res[:content_models] = document["has_model_ssim"]
56
+ res[:file_size] = document["extent_ssim"].first.to_i
57
+ res[:size] = (document["extent_ssim"].first.to_i / 1024).to_s + " Kb"
58
+ res
59
+ end
60
+
61
+ def image_resource(document)
62
+ res = basic_resource(document)
63
+ if document["image_width_ssim"]
64
+ res[:dimensions] = document["image_width_ssim"].first + " x " + document["image_length_ssim"].first
65
+ res[:width] = document["image_width_ssim"].first
66
+ res[:height] = document["image_length_ssim"].first
67
+ else
68
+ res[:dimensions] = "? x ?"
69
+ res[:width] = "0"
70
+ res[:height] = "0"
71
+ end
72
+ base_id = document["id"]
73
+ base_filename = base_id.gsub(/\:/,"")
74
+ img_filename = base_filename + "." + document["dc_format_ssm"].first.gsub(/^[^\/]+\//,"")
75
+ res[:filename] = img_filename
76
+ res[:block] = "CONTENT"
77
+ res[:mime_type] = document["dc_format_ssm"] ? document["dc_format_ssm"].first : "application/octect-stream"
78
+ res[:content_models] = document["has_model_ssim"]
79
+ res[:file_size] = document["extent_ssim"].first.to_i
80
+ res[:size] = (document["extent_ssim"].first.to_i / 1024).to_s + " Kb"
81
+ res[:uri] = base_id
82
+ res
83
+ end
84
+
85
+ def audio_resource(document)
86
+ res = basic_resource(document)
87
+ base_id = document["id"]
88
+ base_filename = base_id.gsub(/\:/,"")
89
+ if res[:mime_type] =~ /wav/
90
+ ext = 'wav'
91
+ elsif res[:mime_type] =~ /mpeg/
92
+ ext = 'mp3'
93
+ else
94
+ ext = 'bin'
95
+ end
96
+ filename = base_filename + "." + ext
97
+ dc_filename = base_filename + "_dc.xml"
98
+ res[:uri] = base_id
99
+ res[:block] = "CONTENT"
100
+ res[:filename] = filename
101
+ res[:dc_path] = fedora_content_path(:download_method=>"show_pretty", :uri=>base_id, :block=>"DC", :filename=>dc_filename)
102
+ res[:mime_type] = document["dc_format_ssm"] ? document["dc_format_ssm"].first : "application/octect-stream"
103
+ res[:content_models] = document["has_model_ssim"]
104
+ res[:file_size] = document["extent_ssim"].first.to_i
105
+ res[:size] = (document["extent_ssim"].first.to_i / 1024).to_s + " Kb"
106
+ res
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -1,27 +1,13 @@
1
- require "active-fedora"
2
- require "active_fedora_finders"
3
- class ContentAggregator < ::ActiveFedora::Base
4
- extend ActiveModel::Callbacks
5
- include ::ActiveFedora::Finders
6
- include ::ActiveFedora::DatastreamCollections
7
- include ::Hydra::ModelMethods
8
- include Cul::Scv::Hydra::Models::Common
9
- include Cul::Scv::Hydra::Models::Aggregator
10
-
11
- alias :file_objects :resources
12
-
13
- def route_as
14
- "multipartitem"
15
- end
1
+ class ContentAggregator < GenericAggregator
16
2
 
17
3
  def thumbnail_info
18
- members = resources
19
- if members.length > 1
4
+ _members = member_ids()
5
+ if _members.length > 1
20
6
  return {:url=>image_url("cul_scv_hydra/crystal/kmultiple.png"),:mime=>'image/png'}
21
- elsif members.length == 0
7
+ elsif _members.length == 0
22
8
  return {:url=>image_url("cul_scv_hydra/crystal/file.png"),:mime=>'image/png'}
23
9
  else
24
- member = ActiveFedora::Base.find(members[0], :cast=>true)
10
+ member = ActiveFedora::Base.find(_members[0], :cast=>true)
25
11
  if member.respond_to? :thumbnail_info
26
12
  return member.thumbnail_info
27
13
  end
@@ -8,7 +8,7 @@ class DcDocument < ActiveFedora::Base
8
8
  include Cul::Scv::Hydra::Models::Common
9
9
  alias :file_objects :resources
10
10
 
11
- has_many :parts, :property=>:cul_member_of, :class_name=>'ActiveFedora::Base'
11
+ has_and_belongs_to_many :parts, :property => :cul_member_of, :class_name=>'ActiveFedora::Base'
12
12
 
13
13
  def self.load_instance_from_solr(pid,solr_doc=nil)
14
14
  if solr_doc.nil?
@@ -1,16 +1,35 @@
1
1
  require "active-fedora"
2
2
  require "active_fedora_finders"
3
3
  class GenericAggregator < ::ActiveFedora::Base
4
- extend ActiveModel::Callbacks
5
4
  include ::ActiveFedora::Finders
6
5
  include ::ActiveFedora::DatastreamCollections
7
6
  include ::Hydra::ModelMethods
8
7
  include Cul::Scv::Hydra::Models::Common
9
8
  include Cul::Scv::Hydra::Models::Aggregator
10
9
 
11
- alias :file_objects :resources
10
+ has_many :parts, :property => :cul_member_of, :class_name=>'ActiveFedora::Base'
12
11
 
13
12
  def route_as
14
13
  "multipartitem"
15
14
  end
15
+
16
+ def index_type_label
17
+ riquery = Cul::Scv::Hydra::Models::MEMBER_ITQL.gsub(/%PID%/, self.pid)
18
+ begin
19
+ docs = Cul::Scv::Fedora.repository.find_by_itql riquery, limit: 2, format: json
20
+ docs = JSON.parse(docs)['results']
21
+ rescue Exception=>e
22
+ Rails.logger.warn("#{self.class.name} failed to find children with TQL: #{e.message}")
23
+ docs = self.parts
24
+ end
25
+ if docs.size == 0
26
+ label = "EMPTY"
27
+ elsif docs.size == 1
28
+ label = "SINGLE PART"
29
+ else
30
+ label = "MULTIPART"
31
+ end
32
+ label
33
+ end
34
+
16
35
  end
@@ -8,6 +8,8 @@ class GenericObject < ::ActiveFedora::Base
8
8
  include Cul::Scv::Hydra::Models::Common
9
9
  include Cul::Scv::Hydra::Models::Aggregator
10
10
 
11
+ has_many :parts, :property => :cul_member_of, :class_name=>'ActiveFedora::Base'
12
+
11
13
  alias :file_objects :resources
12
14
 
13
15
  def route_as
@@ -10,8 +10,11 @@ class GenericResource < ::ActiveFedora::Base
10
10
  include ::ActiveFedora::DatastreamCollections
11
11
  include ::Hydra::ModelMethods
12
12
  include Cul::Scv::Hydra::Models::Common
13
+ include Cul::Scv::Hydra::Models::ImageResource
14
+ include Cul::Scv::Fedora::UrlHelperBehavior
13
15
  include ::ActiveFedora::RelsInt
14
- alias :file_objects :resources
16
+
17
+ has_and_belongs_to_many :containers, :property=>:cul_member_of, :class_name=>'ActiveFedora::Base'
15
18
 
16
19
  IMAGE_EXT = {"image/bmp" => 'bmp', "image/gif" => 'gif', "image/jpeg" => 'jpg', "image/png" => 'png', "image/tiff" => 'tif', "image/x-windows-bmp" => 'bmp'}
17
20
  WIDTH = RDF::URI(ActiveFedora::Predicates.find_graph_predicate(:image_width))
@@ -25,7 +28,7 @@ class GenericResource < ::ActiveFedora::Base
25
28
  end
26
29
 
27
30
  def route_as
28
- "resource"
31
+ self.zooming? ? "zoomingimage" : "resource"
29
32
  end
30
33
 
31
34
  def index_type_label
@@ -49,5 +52,97 @@ class GenericResource < ::ActiveFedora::Base
49
52
  return {:url=>image_url("cul_scv_hydra/crystal/file.png"),:mime=>'image/png'}
50
53
  end
51
54
  end
52
-
55
+
56
+ def to_solr(solr_doc = Hash.new, opts={})
57
+ solr_doc = super
58
+ unless solr_doc["extent_ssim"] || self.datastreams["content"].nil?
59
+ solr_doc["extent_ssim"] = [self.datastreams["content"].size]
60
+ end
61
+ if self.zooming?
62
+ fz = rels_int.relationships(datastreams['content'], :foaf_zooming).first.object.to_s.split('/')[-1]
63
+ ds = datastreams[fz]
64
+ rft_id = ds.controlGroup == 'E' ? datastreams[fz].dsLocation : fedora_ds_url(pid, ds.dsid) + '/content'
65
+ solr_doc['rft_id_ss'] = rft_id
66
+ end
67
+ solr_doc
68
+ end
69
+
70
+ def thumbnail_info
71
+ thumb = rels_int.relationships(datastreams['content'],:foaf_thumbnail).first
72
+ if thumb
73
+ t_dsid = thumb.object.to_s.split('/')[-1]
74
+ t_url = "#{ActiveFedora.fedora_config.credentials[:url]}/objects/#{pid}/datastreams/#{t_dsid}/content"
75
+ return {:url=>t_url,:mime=>datastreams[t_dsid].mimeType}
76
+ elsif self.zooming?
77
+ t_dsid = rels_int.relationships(dsuri, :foaf_zooming).first.object.to_s.split('/')[-1]
78
+ t_parms = DJATOKA_THUMBNAIL_PARMS.merge({"rft_id" => datastreams[t_dsid].dsLocation})
79
+ url = "#{DJATOKA_BASE_URL}?#{options.map { |key, value| "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"}.join("&") }"
80
+ {:url => url, :mime => t_parms["svc.format"]}
81
+ else
82
+ return {:asset=>"crystal/file.png",:mime=>'image/png'}
83
+ end
84
+ end
85
+
86
+ def linkable_resources
87
+ # let's start with the known DSIDs from lindquist, then work our way back to parsing the solrized relsint
88
+ results = []
89
+ if (rels = rels_int.instance_variable_get :@solr_hash)
90
+ # this was loaded from solr
91
+ rels.each do |dsuri, props|
92
+ if dsuri =~ /\/content$/ or not props[FORMAT_OF_PREDICATE].blank?
93
+ dsid = dsuri.split('/')[-1]
94
+ res = datastream_as_resource(dsid, props)
95
+ results << res
96
+ end
97
+ end
98
+ else
99
+ content_uri = RDF::URI("info:fedora/#{self.pid}/content")
100
+ dsuris = [content_uri]
101
+ results = []
102
+ # read the graph
103
+ datastreams.each do |k, v|
104
+ rels = rels_int.relationships(v, :format_of, content_uri)
105
+ dsuris << rels[0].subject unless rels.blank?
106
+ end
107
+
108
+ dsuris.each do |dsuri|
109
+ dsid = dsuri.to_s.split('/')[-1]
110
+ width_rel = rels_int.relationships(dsuri, :exif_image_width)[0]
111
+ length_rel = rels_int.relationships(dsuri, :exif_image_length)[0]
112
+ extent_rel = rels_int.relationships(dsuri, :extent)[0]
113
+ props = {EXTENT_PREDICATE => [], WIDTH_PREDICATE => [], LENGTH_PREDICATE => []}
114
+ props[EXTENT_PREDICATE] << extent_rel.object.to_s unless extent_rel.blank?
115
+ props[WIDTH_PREDICATE] << width_rel.object.to_s unless width_rel.blank?
116
+ props[LENGTH_PREDICATE] << length_rel.object.to_s unless length_rel.blank?
117
+ results << datastream_as_resource(dsid, props)
118
+ end
119
+ end
120
+ results
121
+ end
122
+
123
+ def zooming?
124
+ !rels_int.relationships(datastreams['content'], :foaf_zooming).first.blank?
125
+ end
126
+
127
+ private
128
+ def datastream_as_resource(dsid, props={})
129
+ ds = datastreams[dsid]
130
+ raise "No resource at info:fedora/#{pid}/#{dsid}" unless ds
131
+ res = {}
132
+ res[:uri] = self.pid
133
+ res[:block] = dsid
134
+ res[:mime_type] = ds.mimeType
135
+ res[:content_models] = ["Datastream"]
136
+ res[:file_size] = ds.dsSize.to_s
137
+ if res[:file_size] == "0" and props[EXTENT_PREDICATE]
138
+ res[:file_size] = (props[EXTENT_PREDICATE].first || "0")
139
+ end
140
+ res[:size] = (res[:file_size].to_i / 1024).to_s + " Kb"
141
+ res[:width] = props[WIDTH_PREDICATE].first || "0"
142
+ res[:height] = props[LENGTH_PREDICATE].first || "0"
143
+ res[:dimensions] = "#{res[:width]} x #{res[:height]}"
144
+ base_filename = pid.gsub(/\:/,"")
145
+ res[:filename] = base_filename + "." + dsid + "." + ds.mimeType.gsub(/^[^\/]+\//,"")
146
+ res
147
+ end
53
148
  end
@@ -1,17 +1,9 @@
1
1
  require "active-fedora"
2
2
  require "active_fedora_finders"
3
- class JP2ImageAggregator < ::ActiveFedora::Base
4
- extend ActiveModel::Callbacks
5
- include ::ActiveFedora::Finders
6
- include ::ActiveFedora::DatastreamCollections
7
- include ::Hydra::ModelMethods
8
- include Cul::Scv::Hydra::Models::Common
9
- include Cul::Scv::Hydra::Models::Aggregator
3
+ class JP2ImageAggregator < ResourceAggregator
10
4
 
11
5
  has_datastream :name => "SOURCE", :type=>::ActiveFedora::Datastream, :mimeType=>"image/jp2", :controlGroup=>'E'
12
6
 
13
- alias :file_objects :resources
14
-
15
7
  def route_as
16
8
  "zoomingimage"
17
9
  end
@@ -8,6 +8,8 @@ class METSStructuredAggregator < ::ActiveFedora::Base
8
8
  include Cul::Scv::Hydra::Models::Common
9
9
  include Cul::Scv::Hydra::Models::Aggregator
10
10
 
11
+ has_many :parts, :property => :cul_member_of, :class_name=>'ActiveFedora::Base'
12
+
11
13
  alias :file_objects :resources
12
14
 
13
15
  def route_as
@@ -3,20 +3,20 @@ require "active_fedora_finders"
3
3
  require "cul_image_props"
4
4
  require "mime/types"
5
5
  require "uri"
6
- class Resource < ::ActiveFedora::Base
7
- extend ActiveModel::Callbacks
6
+ class Resource < ActiveFedora::Base
8
7
  include ::ActiveFedora::Finders
9
8
  include ::ActiveFedora::DatastreamCollections
10
- include ::Hydra::ModelMethods
9
+ # include ::Hydra::ModelMethods
11
10
  include Cul::Scv::Hydra::Models::Common
12
11
  include Cul::Scv::Hydra::Models::Resource
12
+ include Cul::Scv::Hydra::Models::ImageResource
13
13
 
14
- alias :file_objects :resources
14
+ belongs_to :container, :property=>:cul_member_of, :class_name=>'ActiveFedora::Base'
15
15
 
16
- CUL_WIDTH = "http://purl.oclc.org/NET/CUL/RESOURCE/STILLIMAGE/BASIC/imageWidth"
17
- CUL_LENGTH = "http://purl.oclc.org/NET/CUL/RESOURCE/STILLIMAGE/BASIC/imageLength"
18
- FORMAT = "http://purl.org/dc/elements/1.1/format"
19
- MEMBER_OF = "http://purl.oclc.org/NET/CUL/memberOf"
16
+ # CUL_WIDTH = "http://purl.oclc.org/NET/CUL/RESOURCE/STILLIMAGE/BASIC/imageWidth"
17
+ # CUL_LENGTH = "http://purl.oclc.org/NET/CUL/RESOURCE/STILLIMAGE/BASIC/imageLength"
18
+ # FORMAT = "http://purl.org/dc/elements/1.1/format"
19
+ # MEMBER_OF = "http://purl.oclc.org/NET/CUL/memberOf"
20
20
 
21
21
  def route_as
22
22
  "resource"
@@ -35,46 +35,44 @@ class Resource < ::ActiveFedora::Base
35
35
  end
36
36
 
37
37
  def set_title_and_label(new_title, opts={})
38
- if opts[:only_if_blank]
39
- if self.label.nil? || self.label.empty?
40
- self.label = new_title
41
- self.set_title( new_title )
42
- end
43
- else
38
+ if opts[:only_if_blank]
39
+ if self.label.nil? || self.label.empty?
44
40
  self.label = new_title
45
- set_title( new_title )
41
+ self.set_title( new_title )
46
42
  end
43
+ else
44
+ self.label = new_title
45
+ set_title( new_title )
47
46
  end
47
+ end
48
48
 
49
- # Set the title and label on the current object
50
- #
51
- # @param [String] new_title
52
- # @param [Hash] opts (optional) hash of configuration options
53
- def set_title(new_title, opts={})
54
- if has_desc?
55
- desc_metadata_ds = self.datastreams["descMetadata"]
56
- if desc_metadata_ds.respond_to?(:title_values)
57
- desc_metadata_ds.title_values = new_title
58
- else
59
- desc_metadata_ds.title = new_title
60
- end
49
+ # Set the title and label on the current object
50
+ #
51
+ # @param [String] new_title
52
+ # @param [Hash] opts (optional) hash of configuration options
53
+ def set_title(new_title, opts={})
54
+ if has_desc?
55
+ desc_metadata_ds = self.datastreams["descMetadata"]
56
+ if desc_metadata_ds.respond_to?(:title_values)
57
+ desc_metadata_ds.title_values = new_title
58
+ else
59
+ desc_metadata_ds.title = new_title
61
60
  end
62
61
  end
63
-
64
- def thumbnail_info
65
- # do the triples indicate this is a thumb? fetch
66
- width = object_relations[CUL_WIDTH].first.to_i
67
- length = object_relations[CUL_LENGTH].first.to_i
68
- if width <= 251 && length <= 251
69
- mime = object_relations[FORMAT].first
70
- url = {:url=>"#{ActiveFedora.fedora_config[:url]}/objects/#{self.pid}/datastreams/CONTENT/content", :mime=>mime}
62
+ end
63
+
64
+ def thumbnail_info
65
+ # do the triples indicate this is a thumb? fetch
66
+ if long <= 251
67
+ mime = object_relations[FORMAT].first
68
+ url = {:url=>"#{ActiveFedora.fedora_config[:url]}/objects/#{self.pid}/datastreams/CONTENT/content", :mime=>mime}
69
+ else
70
+ if object_relations[MEMBER_OF].blank?
71
+ return {:url=>image_url("cul_scv_hydra/crystal/file.png"),:mime=>'image/png'}
71
72
  else
72
- if object_relations[MEMBER_OF].blank?
73
- return {:url=>image_url("cul_scv_hydra/crystal/file.png"),:mime=>'image/png'}
74
- else
75
- url = StaticImageAggregator.find(object_relations[MEMBER_OF].first).thumbnail_info
76
- end
73
+ url = ActiveFedora::Base.find(parents.first, :cast => true).thumbnail_info
77
74
  end
78
- return url
79
75
  end
76
+ return url
77
+ end
80
78
  end
@@ -0,0 +1,22 @@
1
+ class ResourceAggregator < ::ActiveFedora::Base
2
+ include ::ActiveFedora::Finders
3
+ include ::ActiveFedora::DatastreamCollections
4
+ include ::Hydra::ModelMethods
5
+ include Cul::Scv::Hydra::Models::Common
6
+ include Cul::Scv::Hydra::Models::Aggregator
7
+ include Cul::Scv::Hydra::Models::LinkableResources
8
+
9
+ has_many :parts, :property => :cul_member_of, :class_name=>'Resource'
10
+
11
+ def route_as
12
+ "resource"
13
+ end
14
+
15
+ def index_type_label
16
+ "PART"
17
+ end
18
+
19
+ def thumbnail_info
20
+ return {:url=>image_url("cul_scv_hydra/crystal/file.png"),:mime=>'image/png'}
21
+ end
22
+ end
@@ -1,23 +1,11 @@
1
1
  require "active-fedora"
2
2
  require "active_fedora_finders"
3
- class StaticAudioAggregator < ::ActiveFedora::Base
4
- extend ActiveModel::Callbacks
5
- include ::ActiveFedora::Finders
6
- include ::ActiveFedora::DatastreamCollections
7
- include ::Hydra::ModelMethods
8
- include Cul::Scv::Hydra::Models::Common
9
- include Cul::Scv::Hydra::Models::Aggregator
10
-
11
- alias :file_objects :resources
3
+ class StaticAudioAggregator < ResourceAggregator
12
4
 
13
5
  def route_as
14
6
  "audio"
15
7
  end
16
8
 
17
- def index_type_label
18
- "PART"
19
- end
20
-
21
9
  def thumbnail_info
22
10
  return {:url=>image_url("cul_scv_hydra/crystal/mp3.png"),:mime=>'image/png'}
23
11
  end
@@ -1,14 +1,6 @@
1
1
  require "active-fedora"
2
2
  require "active_fedora_finders"
3
- class StaticImageAggregator < ::ActiveFedora::Base
4
- extend ActiveModel::Callbacks
5
- include ::ActiveFedora::Finders
6
- include ::ActiveFedora::DatastreamCollections
7
- include ::Hydra::ModelMethods
8
- include Cul::Scv::Hydra::Models::Common
9
- include Cul::Scv::Hydra::Models::Aggregator
10
-
11
- alias :file_objects :resources
3
+ class StaticImageAggregator < ResourceAggregator
12
4
 
13
5
  CUL_WIDTH = "http://purl.oclc.org/NET/CUL/RESOURCE/STILLIMAGE/BASIC/imageWidth"
14
6
  CUL_LENGTH = "http://purl.oclc.org/NET/CUL/RESOURCE/STILLIMAGE/BASIC/imageLength"
@@ -18,20 +10,17 @@ class StaticImageAggregator < ::ActiveFedora::Base
18
10
  end
19
11
 
20
12
  def index_type_label
21
- "PART"
13
+ 'PART'
22
14
  end
23
-
15
+
24
16
  def thumbnail_info
25
17
  candidate = nil
26
18
  max_dim = 251
27
19
  resources.each do |pid|
28
20
  resource = Resource.find(pid)
29
- width = resource.object_relations[CUL_WIDTH].first.to_i
30
- length = resource.object_relations[CUL_LENGTH].first.to_i
31
- max = (width > length) ? width : length
32
- if max_dim > max
21
+ if max_dim > resouce.long
33
22
  candidate = resource
34
- max_dim = max
23
+ max_dim = resource.long
35
24
  end
36
25
  end
37
26
  if candidate.nil?
data/config/fedora.yml CHANGED
@@ -1,19 +1,11 @@
1
- development:
2
- # :url: http://127.0.0.1:8983/fedora
3
- # :user: fedoraAdmin
4
- # :password: fedoraAdmin
1
+ alcott: &alcott
5
2
  :url: http://alcott.cul.columbia.edu:8080/fedora
6
3
  :user: fedoraAdmin
7
4
  :password: f+BULUS*^
8
- test:
9
- :url: http://127.0.0.1:8983/fedora
10
- :user: fedoraAdmin
11
- :password: fedoraAdmin
12
- production:
13
- :url: http://alcott.cul.columbia.edu:8080/fedora
14
- :user: fedoraAdmin
15
- :password: f+BULUS*^
16
- default:
5
+ default: &default
17
6
  :url: http://127.0.0.1:8983/fedora
18
7
  :user: fedoraAdmin
19
8
  :password: fedoraAdmin
9
+ development: *alcott
10
+ test: *default
11
+ production: *alcott
@@ -64,6 +64,8 @@
64
64
  :y_resolution: yResolution
65
65
  :resolution_unit: resolutionUnit
66
66
  http://xmlns.com/foaf/0.1/:
67
+ :foaf_image: image
67
68
  :foaf_thumbnail: thumbnail
69
+ :foaf_zooming: zoomingImage
68
70
  http://www.w3.org/1999/02/22-rdf-syntax-ns#:
69
71
  :rdf_type: type
@@ -0,0 +1,31 @@
1
+ module Cul
2
+ module Scv
3
+ module Fedora
4
+ class FakeObject
5
+ attr_accessor :pid
6
+ def initialize(pid, isNew=false)
7
+ @pid = pid
8
+ @isNew = isNew
9
+ end
10
+ def new_record?
11
+ @isNew
12
+ end
13
+ def connection
14
+ Cul::Scv::Fedora.connection
15
+ end
16
+ def repository
17
+ Cul::Scv::Fedora.repository
18
+ end
19
+ def spawn(pid)
20
+ s = FakeObject.new(pid)
21
+ s.connection= connection
22
+ s.repository= repository
23
+ s
24
+ end
25
+ protected
26
+ def connection=(connection); @connection = connection; end
27
+ def repository=(repo); @repository = repo; end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ module Cul
2
+ module Scv
3
+ module Fedora
4
+ module RubydoraPatch
5
+ def find_by_itql query, options = {}
6
+ self.risearch(query, {:lang => 'itql'}.merge(options))
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ module Cul
2
+ module Scv
3
+ module Fedora
4
+ module UrlHelperBehavior
5
+
6
+ def fedora_url
7
+ @fedora_url ||= ActiveFedora.config.credentials[:url]
8
+ end
9
+
10
+ def pid_for_url(pid)
11
+ pid.gsub(/^\//,'').gsub(/info:fedora\//,'')
12
+ end
13
+
14
+ def fedora_object_url(pid)
15
+ fedora_url + '/objects/' + pid_for_url(pid)
16
+ end
17
+
18
+ def fedora_ds_url(pid, dsid)
19
+ fedora_object_url(pid) + '/datastreams/' + dsid
20
+ end
21
+
22
+ def fedora_method_url(pid, method)
23
+ fedora_object_url(pid) + '/methods/' + method
24
+ end
25
+
26
+ def fedora_risearch_url
27
+ fedora_url + '/risearch'
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
data/lib/cul_scv_hydra.rb CHANGED
@@ -1,19 +1,41 @@
1
- require "rubygems"
1
+ require 'hydra/head'
2
+ require 'active_fedora_relsint'
2
3
  module Cul
3
4
  module Scv
4
5
  module Hydra
5
6
  end
7
+ module Fedora
8
+ require 'cul_scv_fedora/url_helper_behavior'
9
+ require 'cul_scv_fedora/dummy_object'
10
+ require 'cul_scv_fedora/rubydora_patch'
11
+ def self.config_path
12
+ File.join(Rails.root.to_s, 'config', 'fedora.yml')
13
+ end
14
+ def self.config
15
+ ActiveFedora.fedora_config.credentials
16
+ end
17
+ def self.connection
18
+ @connection ||= ActiveFedora::RubydoraConnection.new(ActiveFedora.fedora_config.credentials)
19
+ end
20
+
21
+ def self.repository
22
+ @repository ||= begin
23
+ repo = connection.connection
24
+ repo.extend(RubydoraPatch)
25
+ repo
26
+ end
27
+ end
28
+
29
+ def self.ds_for_uri(fedora_uri, fake_obj=nil)
30
+ return nil unless fedora_uri =~ /info\:fedora\/.*/
31
+ p = fedora_uri.split('/')
32
+ fake_obj = fake_obj.nil? ? DummyObject.new(p[1]) : fake_obj.spawn(p[1])
33
+ ::Rubydora::Datastream.new(fake_obj, p[2])
34
+ end
35
+ end
6
36
  end
7
37
  end
8
- # this is a hack to make requiring hydra possible
9
- #module Hydra
10
- # module Datastream
11
- # module CommonModsIndexMethods
12
- # end
13
- # end
14
- #end
15
- require 'hydra/head'
16
- require 'active_fedora_relsint'
38
+
17
39
  require "cul_scv_hydra/access_controls_enforcement"
18
40
  require "cul_scv_hydra/controllers"
19
41
  require "cul_scv_hydra/om"
@@ -7,6 +7,13 @@ module Cul::Scv::Hydra
7
7
  class Engine < ::Rails::Engine
8
8
  isolate_namespace Cul::Scv::Hydra
9
9
 
10
+ config.mount_at = '/'
11
+
12
+ config.autoload_paths += %W(
13
+ #{config.root}/app/controllers/concerns
14
+ #{config.root}/app/models/concerns
15
+ )
16
+
10
17
  config.generators do |g|
11
18
  g.test_framework :rspec
12
19
  g.integration_tool :rspec
@@ -1,10 +1,10 @@
1
1
  module Cul
2
2
  module Scv
3
3
  module Hydra
4
- VERSION = '0.11.0'
4
+ VERSION = '0.12.0'
5
5
  def self.version
6
6
  VERSION
7
7
  end
8
8
  end
9
9
  end
10
- end
10
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cul_scv_hydra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-04-17 00:00:00.000000000 Z
12
+ date: 2014-04-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: blacklight
@@ -210,6 +210,9 @@ extensions: []
210
210
  extra_rdoc_files: []
211
211
  files:
212
212
  - bin/rails
213
+ - lib/cul_scv_fedora/dummy_object.rb
214
+ - lib/cul_scv_fedora/rubydora_patch.rb
215
+ - lib/cul_scv_fedora/url_helper_behavior.rb
213
216
  - lib/cul_scv_hydra/access_controls_enforcement.rb
214
217
  - lib/cul_scv_hydra/controllers/aggregates.rb
215
218
  - lib/cul_scv_hydra/controllers/aggregator_controller_helper.rb
@@ -258,6 +261,8 @@ files:
258
261
  - app/models/bag_aggregator.rb
259
262
  - app/models/concerns/cul/scv/hydra/models/aggregator.rb
260
263
  - app/models/concerns/cul/scv/hydra/models/common.rb
264
+ - app/models/concerns/cul/scv/hydra/models/image_resource.rb
265
+ - app/models/concerns/cul/scv/hydra/models/linkable_resources.rb
261
266
  - app/models/concerns/cul/scv/hydra/models/resource.rb
262
267
  - app/models/concerns/cul/scv/hydra/models.rb
263
268
  - app/models/concerns/cul.rb
@@ -272,6 +277,7 @@ files:
272
277
  - app/models/jp2_image_aggregator.rb
273
278
  - app/models/mets_structured_aggregator.rb
274
279
  - app/models/resource.rb
280
+ - app/models/resource_aggregator.rb
275
281
  - app/models/static_audio_aggregator.rb
276
282
  - app/models/static_image_aggregator.rb
277
283
  - config/fedora.yml
@@ -284,6 +290,8 @@ licenses: []
284
290
  post_install_message:
285
291
  rdoc_options: []
286
292
  require_paths:
293
+ - app
294
+ - config
287
295
  - lib
288
296
  - fixtures
289
297
  required_ruby_version: !ruby/object:Gem::Requirement