dor-services 4.25.1 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/bin/dor-indexer +20 -19
  3. data/bin/dor-indexerd +3 -2
  4. data/config/certs/robots-dor-dev.crt +29 -0
  5. data/config/certs/robots-dor-dev.key +27 -0
  6. data/config/config_defaults.yml +0 -6
  7. data/config/dev_console_env.rb +65 -0
  8. data/config/environments/development.rb +84 -0
  9. data/config/environments/development.rb.old +84 -0
  10. data/config/environments/test.rb +84 -0
  11. data/lib/dor-services.rb +8 -18
  12. data/lib/dor/config.rb +18 -24
  13. data/lib/dor/datastreams/administrative_metadata_ds.rb +8 -7
  14. data/lib/dor/datastreams/content_metadata_ds.rb +200 -278
  15. data/lib/dor/datastreams/datastream_spec_solrizer.rb +1 -1
  16. data/lib/dor/datastreams/default_object_rights_ds.rb +10 -8
  17. data/lib/dor/datastreams/desc_metadata_ds.rb +30 -34
  18. data/lib/dor/datastreams/embargo_metadata_ds.rb +17 -13
  19. data/lib/dor/datastreams/events_ds.rb +12 -12
  20. data/lib/dor/datastreams/geo_metadata_ds.rb +3 -244
  21. data/lib/dor/datastreams/identity_metadata_ds.rb +34 -30
  22. data/lib/dor/datastreams/role_metadata_ds.rb +6 -6
  23. data/lib/dor/datastreams/simple_dublin_core_ds.rb +12 -9
  24. data/lib/dor/datastreams/version_metadata_ds.rb +14 -33
  25. data/lib/dor/datastreams/workflow_definition_ds.rb +18 -18
  26. data/lib/dor/datastreams/workflow_ds.rb +74 -65
  27. data/lib/dor/migrations/identifiable/assert_adminPolicy.rb +1 -1
  28. data/lib/dor/migrations/identifiable/fix_model_assertions.rb +1 -1
  29. data/lib/dor/migrations/identifiable/record_remediation.rb +2 -2
  30. data/lib/dor/migrations/identifiable/uriify_augmented_contentlocation_refs.rb +1 -1
  31. data/lib/dor/migrations/identifiable/uriify_contentlocation_refs.rb +1 -1
  32. data/lib/dor/migrations/processable/unify_workflows.rb +4 -4
  33. data/lib/dor/migrations/versionable/add_missing_version_md.rb +1 -1
  34. data/lib/dor/models/admin_policy_object.rb +1 -1
  35. data/lib/dor/models/assembleable.rb +3 -4
  36. data/lib/dor/models/collection.rb +0 -2
  37. data/lib/dor/models/contentable.rb +34 -35
  38. data/lib/dor/models/describable.rb +80 -122
  39. data/lib/dor/models/editable.rb +57 -73
  40. data/lib/dor/models/embargoable.rb +13 -15
  41. data/lib/dor/models/eventable.rb +3 -3
  42. data/lib/dor/models/geoable.rb +8 -9
  43. data/lib/dor/models/governable.rb +36 -54
  44. data/lib/dor/models/identifiable.rb +119 -115
  45. data/lib/dor/models/item.rb +4 -4
  46. data/lib/dor/models/itemizable.rb +9 -9
  47. data/lib/dor/models/presentable.rb +133 -0
  48. data/lib/dor/models/preservable.rb +4 -4
  49. data/lib/dor/models/processable.rb +29 -28
  50. data/lib/dor/models/publishable.rb +36 -30
  51. data/lib/dor/models/releasable.rb +310 -0
  52. data/lib/dor/models/shelvable.rb +14 -14
  53. data/lib/dor/models/upgradable.rb +13 -13
  54. data/lib/dor/models/versionable.rb +4 -7
  55. data/lib/dor/models/workflow_object.rb +16 -36
  56. data/lib/dor/services/cleanup_reset_service.rb +28 -34
  57. data/lib/dor/services/cleanup_service.rb +4 -4
  58. data/lib/dor/services/digital_stacks_service.rb +10 -10
  59. data/lib/dor/services/merge_service.rb +1 -1
  60. data/lib/dor/services/metadata_handlers/mdtoolkit_handler.rb +2 -2
  61. data/lib/dor/services/metadata_service.rb +20 -20
  62. data/lib/dor/services/registration_service.rb +26 -27
  63. data/lib/dor/services/reset_workspace_service.rb +15 -15
  64. data/lib/dor/services/sdr_ingest_service.rb +4 -4
  65. data/lib/dor/services/search_service.rb +4 -9
  66. data/lib/dor/services/suri_service.rb +5 -5
  67. data/lib/dor/services/technical_metadata_service.rb +3 -2
  68. data/lib/dor/utils/ng_tidy.rb +9 -9
  69. data/lib/dor/utils/predicate_patch.rb +1 -1
  70. data/lib/dor/utils/solr_doc_helper.rb +13 -5
  71. data/lib/dor/version.rb +1 -1
  72. data/lib/dor/workflow/document.rb +28 -30
  73. data/lib/dor/workflow/graph.rb +36 -36
  74. data/lib/dor/workflow/process.rb +12 -12
  75. data/lib/tasks/dor.rake +1 -1
  76. data/lib/tasks/rdoc.rake +3 -3
  77. metadata +67 -76
  78. data/lib/dor/datastreams/geo2mods.xsl +0 -867
  79. data/lib/dor/models/discoverable.rb +0 -64
  80. data/lib/dor/models/releaseable.rb +0 -357
  81. data/lib/dor/services/indexing_service.rb +0 -64
  82. data/lib/dor/utils/sdr_client.rb +0 -23
  83. data/lib/dor/utils/utc_date_field_mapper.rb +0 -7
@@ -27,7 +27,7 @@ module Dor
27
27
  unless @primary.allows_modification?
28
28
  raise Dor::Exception.new "Primary object is not editable: #{@primary.pid}"
29
29
  end
30
- if ( non_editable = (@secondary_objs.detect {|obj| !obj.allows_modification? } ))
30
+ if ( non_editable = (@secondary_objs.detect {|obj| ! obj.allows_modification? } ))
31
31
  raise Dor::Exception.new "Secondary object is not editable: #{non_editable.pid}"
32
32
  end
33
33
  end
@@ -26,7 +26,7 @@ handler = Class.new do
26
26
  end
27
27
  case xml.root.name
28
28
  when 'msDesc' then xml.xpath('/msDesc/msIdentifier/collection').text
29
- when 'mods' then
29
+ when 'mods' then
30
30
  xml.root.add_namespace_definition('mods','http://www.loc.gov/mods/v3')
31
31
  xml.xpath('/mods:mods/mods:titleInfo[1]').xpath('mods:title|mods:nonSort').collect { |n| n.text }.join(' ').strip
32
32
  end
@@ -37,4 +37,4 @@ handler = Class.new do
37
37
  end
38
38
  end
39
39
 
40
- Dor::MetadataService.register(handler)
40
+ Dor::MetadataService.register(handler)
@@ -3,27 +3,27 @@ require 'cache'
3
3
  module Dor
4
4
 
5
5
  class MetadataError < Exception ; end
6
-
6
+
7
7
  # class MetadataHandler
8
- #
8
+ #
9
9
  # def fetch(prefix, identifier)
10
10
  # ### Return metadata for prefix/identifier combo
11
11
  # end
12
- #
12
+ #
13
13
  # def label(metadata)
14
14
  # ### Return a Fedora-compatible label from the metadata format returned by #fetch
15
15
  # end
16
- #
16
+ #
17
17
  # end
18
-
18
+
19
19
  class MetadataService
20
-
20
+
21
21
  class << self
22
22
  @@cache = Cache.new(nil, nil, 250, 300)
23
-
23
+
24
24
  def register(handler_class)
25
25
  ['fetch', 'label', 'prefixes'].each do |method|
26
- unless handler_class.instance_methods.include?(method) or handler_class.instance_methods.include?(method.to_sym)
26
+ unless handler_class.instance_methods.include?(method) || handler_class.instance_methods.include?(method.to_sym)
27
27
  raise TypeError, "Metadata handlers must define ##{method.to_s}"
28
28
  end
29
29
  end
@@ -31,23 +31,23 @@ module Dor
31
31
  handler.prefixes.each do |prefix|
32
32
  handlers[prefix.to_sym] = handler
33
33
  end
34
- handler
34
+ return handler
35
35
  end
36
-
36
+
37
37
  def known_prefixes
38
- handlers.keys
38
+ return handlers.keys
39
39
  end
40
-
40
+
41
41
  def can_resolve?(identifier)
42
42
  (prefix, identifier) = identifier.split(/:/,2)
43
43
  handlers.keys.include?(prefix.to_sym)
44
44
  end
45
-
45
+
46
46
  # TODO: Return a prioritized list
47
47
  def resolvable(identifiers)
48
48
  identifiers.select { |identifier| self.can_resolve?(identifier) }
49
49
  end
50
-
50
+
51
51
  def fetch(identifier)
52
52
  @@cache.fetch(identifier) do
53
53
  (prefix, identifier) = identifier.split(/:/,2)
@@ -61,24 +61,24 @@ module Dor
61
61
  handler = handler_for(prefix)
62
62
  handler.label(handler.fetch(prefix, identifier))
63
63
  end
64
-
64
+
65
65
  def handler_for(prefix)
66
66
  handler = handlers[prefix.to_sym]
67
67
  if handler.nil?
68
68
  raise MetadataError, "Unkown metadata prefix: #{prefix}"
69
69
  end
70
- handler
70
+ return handler
71
71
  end
72
-
72
+
73
73
  private
74
74
  def handlers
75
75
  @handlers ||= {}
76
76
  end
77
-
77
+
78
78
  end
79
-
79
+
80
80
  end
81
-
81
+
82
82
  end
83
83
 
84
84
  Dir[File.join(File.dirname(__FILE__),'metadata_handlers','*.rb')].each { |handler_file|
@@ -8,26 +8,25 @@ module Dor
8
8
  def register_object(params = {})
9
9
  Dor.ensure_models_loaded!
10
10
  [:object_type, :label].each do |required_param|
11
- raise Dor::ParameterError, "#{required_param.inspect} must be specified in call to #{name}.register_object" unless params[required_param]
11
+ raise Dor::ParameterError, "#{required_param.inspect} must be specified in call to #{self.name}.register_object" unless params[required_param]
12
12
  end
13
- metadata_source=params[:metadata_source]
13
+ metadata_source = params[:metadata_source]
14
14
  if params[:label].length<1 && (metadata_source=='label' || metadata_source=='none')
15
- raise Dor::ParameterError, "label cannot be empty to call #{name}.register_object"
15
+ raise Dor::ParameterError, "label cannot be empty to call #{self.name}.register_object"
16
16
  end
17
17
  object_type = params[:object_type]
18
18
  item_class = Dor.registered_classes[object_type]
19
19
  raise Dor::ParameterError, "Unknown item type: '#{object_type}'" if item_class.nil?
20
20
 
21
21
  content_model = params[:content_model]
22
- admin_policy = params[:admin_policy]
23
- label = params[:label]
24
- source_id = params[:source_id] || {}
25
- other_ids = params[:other_ids] || {}
26
- tags = params[:tags] || []
27
- parent = params[:parent]
28
- collection = params[:collection]
22
+ admin_policy = params[:admin_policy]
23
+ label = params[:label]
24
+ source_id = params[:source_id] || {}
25
+ other_ids = params[:other_ids] || {}
26
+ tags = params[:tags] || []
27
+ parent = params[:parent]
28
+ collection = params[:collection]
29
29
  pid = nil
30
- metadata_source=params[:metadata_source]
31
30
  if params[:pid]
32
31
  pid = params[:pid]
33
32
  existing_pid = SearchService.query_by_id(pid).first
@@ -42,7 +41,7 @@ module Dor
42
41
  if params[:rights]
43
42
  rights=params[:rights]
44
43
  unless ['world','stanford','dark','default','none'].include? rights
45
- raise Dor::ParameterError,"Unknown rights setting" + rights + "when calling #{name}.register_object"
44
+ raise Dor::ParameterError, "Unknown rights setting '#{rights}' when calling #{self.name}.register_object"
46
45
  end
47
46
  end
48
47
 
@@ -54,14 +53,10 @@ module Dor
54
53
  end
55
54
  end
56
55
 
57
- if (other_ids.has_key?(:uuid) || other_ids.has_key?('uuid')) == false
56
+ if (other_ids.has_key?(:uuid) or other_ids.has_key?('uuid')) == false
58
57
  other_ids[:uuid] = UUIDTools::UUID.timestamp_create.to_s
59
58
  end
60
- short_label=label
61
- if label.length>254
62
- short_label=label[0,254]
63
- end
64
-
59
+ short_label = label.length>254 ? label[0,254] : label
65
60
  apo_object = Dor.find(admin_policy, :lightweight => true)
66
61
  adm_xml = apo_object.administrativeMetadata.ng_xml
67
62
 
@@ -89,10 +84,10 @@ module Dor
89
84
  if collection
90
85
  new_item.add_collection(collection)
91
86
  end
92
- if(rights && ['item','collection'].include?(object_type) )
87
+ if (rights && ['item','collection'].include?(object_type))
93
88
  rights_xml=apo_object.defaultObjectRights.ng_xml
94
89
  new_item.datastreams['rightsMetadata'].content=rights_xml.to_s
95
- new_item.set_read_rights(rights)
90
+ new_item.set_read_rights(rights) unless rights == 'default' # already defaulted to default!
96
91
  end
97
92
  #create basic mods from the label
98
93
  if(metadata_source=='label')
@@ -104,24 +99,28 @@ module Dor
104
99
  }
105
100
  }
106
101
  }
107
-
108
- ds.content=builder.to_xml
109
-
110
- end
102
+ ds.content=builder.to_xml
103
+ end
111
104
 
112
105
  workflow_priority = params[:workflow_priority] ? params[:workflow_priority].to_i : 0
113
106
 
114
107
  Array(params[:seed_datastream]).each { |datastream_name| new_item.build_datastream(datastream_name) }
115
- Array(params[:initiate_workflow]).each { |workflow_id| new_item.initialize_workflow(workflow_id, !new_item.new_object?, workflow_priority)}
108
+ Array(params[:initiate_workflow]).each { |workflow_id| new_item.initialize_workflow(workflow_id, 'dor', !new_item.new_object?, workflow_priority)}
116
109
 
117
110
  new_item.assert_content_model
111
+
112
+ new_item.class.ancestors.select { |x| x.respond_to? :to_class_uri }.each do |parent_class|
113
+ next if parent_class == ActiveFedora::Base
114
+ new_item.add_relationship(:has_model, parent_class.to_class_uri)
115
+ end
116
+
118
117
  new_item.save
119
118
  begin
120
119
  new_item.update_index if ::ENABLE_SOLR_UPDATES
121
120
  rescue StandardError => e
122
121
  Dor.logger.warn "Dor::RegistrationService.register_object failed to update solr index for #{new_item.pid}: #<#{e.class.name}: #{e.message}>"
123
122
  end
124
- (new_item)
123
+ return(new_item)
125
124
  end
126
125
 
127
126
  def create_from_request(params)
@@ -159,7 +158,7 @@ module Dor
159
158
  }
160
159
  dor_params.delete_if { |k,v| v.nil? }
161
160
 
162
- dor_obj = register_object(dor_params)
161
+ dor_obj = self.register_object(dor_params)
163
162
  pid = dor_obj.pid
164
163
  location = URI.parse(Dor::Config.fedora.safeurl.sub(/\/*$/,'/')).merge("objects/#{pid}").to_s
165
164
  reg_response = dor_params.dup.merge({ :location => location, :pid => pid })
@@ -4,32 +4,32 @@ module Dor
4
4
  class ResetWorkspaceService
5
5
 
6
6
  def self.reset_workspace_druid_tree(druid, version, workspace_root)
7
-
7
+
8
8
  druid_tree_path = DruidTools::Druid.new(druid, workspace_root).pathname.to_s
9
-
10
- raise "The archived directory #{druid_tree_path}_v#{version} already existed." if File.exists?("#{druid_tree_path}_v#{version}")
11
-
12
- if File.exists?(druid_tree_path)
9
+
10
+ raise "The archived directory #{druid_tree_path}_v#{version} already existed." if File.exists?("#{druid_tree_path}_v#{version}")
11
+
12
+ if File.exists?(druid_tree_path)
13
13
  FileUtils.mv(druid_tree_path, "#{druid_tree_path}_v#{version}")
14
14
  end #Else is a truncated tree where we shouldn't do anything
15
15
 
16
16
  end
17
17
 
18
18
  def self.reset_export_bag(druid, version, export_root)
19
-
19
+
20
20
  id = druid.split(':').last
21
21
  bag_dir = File.join(export_root, id)
22
22
 
23
- raise "The archived bag #{bag_dir}_v#{version} already existed." if File.exists?("#{bag_dir}_v#{version}")
24
-
25
- if File.exists?(bag_dir)
23
+ raise "The archived bag #{bag_dir}_v#{version} already existed." if File.exists?("#{bag_dir}_v#{version}")
24
+
25
+ if File.exists?(bag_dir)
26
26
  FileUtils.mv(bag_dir, "#{bag_dir}_v#{version}")
27
- end
28
-
29
- if File.exists?("#{bag_dir}.tar")
27
+ end
28
+
29
+ if File.exists?("#{bag_dir}.tar")
30
30
  FileUtils.mv("#{bag_dir}.tar", "#{bag_dir}_v#{version}.tar")
31
- end
31
+ end
32
32
  end
33
-
33
+
34
34
  end
35
- end
35
+ end
@@ -6,7 +6,7 @@ module Dor
6
6
  # @param [Dor::Item] dor_item The representation of the digital object
7
7
  # @param [String] agreement_id depreciated, included for backward compatability with common-accessoning
8
8
  # @return [void] Create the moab manifests, export data to a BagIt bag, kick off the SDR ingest workflow
9
- def self.transfer(dor_item, agreement_id = nil)
9
+ def self.transfer(dor_item, agreement_id=nil)
10
10
  druid = dor_item.pid
11
11
  workspace = DruidTools::Druid.new(druid,Dor::Config.sdr.local_workspace_root)
12
12
  signature_catalog = get_signature_catalog(druid)
@@ -36,7 +36,7 @@ module Dor
36
36
  bagger.create_tagfiles
37
37
  verify_bag_structure(bag_dir)
38
38
  # Now bootstrap SDR workflow. but do not create the workflows datastream
39
- dor_item.initialize_workflow('sdrIngestWF', false)
39
+ dor_item.initialize_workflow('sdrIngestWF', 'sdr', false)
40
40
  rescue Exception => e
41
41
  raise LyberCore::Exceptions::ItemError.new(druid, "Export failure", e)
42
42
  end
@@ -61,7 +61,7 @@ module Dor
61
61
  Config.sdr.datastreams.to_hash.each_pair do |ds_name, required|
62
62
  ds_name = ds_name.to_s
63
63
  metadata_file = metadata_dir.join("#{ds_name}.xml")
64
- metadata_string = get_datastream_content(dor_item, ds_name, required)
64
+ metadata_string = self.get_datastream_content(dor_item, ds_name, required)
65
65
  metadata_file.open('w') { |f| f << metadata_string } if metadata_string
66
66
  end
67
67
  metadata_dir
@@ -74,7 +74,7 @@ module Dor
74
74
  # If not found, return nil unless it is a required datastream in which case raise exception
75
75
  def self.get_datastream_content(dor_item, ds_name, required)
76
76
  ds = (ds_name == 'relationshipMetadata' ? 'RELS-EXT' : ds_name)
77
- if dor_item.datastreams.keys.include?(ds) && !dor_item.datastreams[ds].new?
77
+ if dor_item.datastreams.keys.include?(ds) && ! dor_item.datastreams[ds].new?
78
78
  return dor_item.datastreams[ds].content
79
79
  elsif (required == 'optional')
80
80
  return nil
@@ -5,18 +5,13 @@ module Dor
5
5
 
6
6
  class SearchService
7
7
 
8
- include Solrizer::FieldNameMapper
9
8
  RISEARCH_TEMPLATE = "select $object from <#ri> where $object <dc:identifier> '%s'"
10
9
  @@index_version = nil
11
10
 
12
11
  class << self
13
12
 
14
13
  def index_version
15
- if @@index_version.nil?
16
- xsl_doc = Nokogiri::XML(File.read(File.expand_path('../../../gsearch/demoFoxmlToSolr.xslt',__FILE__)))
17
- @@index_version = xsl_doc.at_xpath('/xsl:stylesheet/xsl:variable[@name="INDEXVERSION"]/text()').to_s
18
- end
19
- @@index_version
14
+ Dor::VERSION
20
15
  end
21
16
 
22
17
  def reindex(*pids)
@@ -73,7 +68,7 @@ module Dor
73
68
  result = JSON.parse(client["select?#{query_string}"].get)
74
69
  end
75
70
 
76
- def query query, args = {}
71
+ def query query, args={}
77
72
  params = args.merge({ :q => query })
78
73
  params[:start] ||= 0
79
74
  resp = solr.find params
@@ -96,7 +91,7 @@ module Dor
96
91
  elsif id.is_a?(Array) # Two values: [ 'google', 'STANFORD_0123456789' ]
97
92
  id = id.join(':')
98
93
  end
99
- q = %{#{solr_name 'identifier', :string}:"#{id}"}
94
+ q = %{#{Solrizer.solr_name 'identifier', :facetable}:"#{id}"}
100
95
  result = []
101
96
  resp = query(q, :fl => 'id', :rows => 1000) do |resp|
102
97
  result += resp.docs.collect { |doc| doc['id'] }
@@ -128,4 +123,4 @@ module Dor
128
123
 
129
124
  end
130
125
 
131
- end
126
+ end
@@ -6,7 +6,7 @@ module Dor
6
6
  # If Dor::Config.suri.mint_ids is set to true, then this method
7
7
  # returns Config.suri.id_namespace:id_from_suri
8
8
  # Throws an exception if there were any problems
9
- def self.mint_id quantity = nil
9
+ def self.mint_id quantity=nil
10
10
  want_array = quantity.is_a?(Numeric)
11
11
  quantity = 1 if quantity.nil?
12
12
  ids = []
@@ -20,13 +20,13 @@ module Dor
20
20
  resp = Nokogiri::XML(repo.next_pid :numPIDs => quantity)
21
21
  ids = resp.xpath('/pidList/pid').collect { |node| node.text }
22
22
  end
23
- want_array ? ids : ids.first
23
+ return want_array ? ids : ids.first
24
24
 
25
25
  # rescue Exception => e
26
26
  # Rails.logger.error("Unable to mint id from suri: #{e.to_s}")
27
27
  # raise e
28
28
  end
29
-
30
-
29
+
30
+
31
31
  end
32
- end
32
+ end
@@ -99,7 +99,7 @@ module Dor
99
99
  # The data is updated to the latest format.
100
100
  def self.get_dor_technical_metadata(dor_item)
101
101
  ds = "technicalMetadata"
102
- if dor_item.datastreams.keys.include?(ds) && !dor_item.datastreams[ds].new?
102
+ if dor_item.datastreams.keys.include?(ds) and not dor_item.datastreams[ds].new?
103
103
  dor_techmd = dor_item.datastreams[ds].content
104
104
  else
105
105
  return nil
@@ -127,7 +127,7 @@ module Dor
127
127
  # @param [Array<String>] new_files The list of filenames for files that are either added or modifed since the previous version
128
128
  # @return [String] The technicalMetadata datastream for the new files of the new digital object version
129
129
  def self.get_new_technical_metadata(druid, new_files)
130
- return nil if new_files.nil? || new_files.empty?
130
+ return nil if new_files.nil? or new_files.empty?
131
131
  workspace = DruidTools::Druid.new(druid, Dor::Config.sdr.local_workspace_root)
132
132
  content_dir = workspace.find_filelist_parent('content',new_files)
133
133
  temp_dir = workspace.temp_dir
@@ -223,3 +223,4 @@ module Dor
223
223
  end
224
224
 
225
225
  end
226
+
@@ -1,11 +1,11 @@
1
1
  class Nokogiri::XML::Text
2
-
2
+
3
3
  def normalize
4
- content =~ /\S/ ? content.gsub(/\s+/,' ').strip : content
4
+ self.content =~ /\S/ ? self.content.gsub(/\s+/,' ').strip : self.content
5
5
  end
6
-
6
+
7
7
  def normalize!
8
- self.content = normalize
8
+ self.content = self.normalize
9
9
  end
10
10
 
11
11
  end
@@ -13,13 +13,13 @@ end
13
13
  class Nokogiri::XML::Node
14
14
 
15
15
  def normalize_text!
16
- xpath('//text()').each { |t| t.normalize! }
16
+ self.xpath('//text()').each { |t| t.normalize! }
17
17
  end
18
-
18
+
19
19
  end
20
20
 
21
21
  class Nokogiri::XML::Document
22
-
22
+
23
23
  def prettify
24
24
  xslt = Nokogiri::XSLT <<-EOC
25
25
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
@@ -33,5 +33,5 @@ class Nokogiri::XML::Document
33
33
  EOC
34
34
  xslt.transform(self).to_xml
35
35
  end
36
-
37
- end
36
+
37
+ end