oaipmh 0.0.1 → 0.0.2

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.
data/README CHANGED
@@ -1,3 +1,129 @@
1
- README for oaipmh
2
- =================
3
-
1
+ # = README
2
+ #
3
+ # Copyright (C) 2006 William Groppe
4
+ #
5
+ # Will Groppe mailto:wfg@artstor.org
6
+ #
7
+ # Open Archives Initiative - Protocol for Metadata Harvesting see
8
+ # http://www.openarchives.org/
9
+ #
10
+ # === Features
11
+ # * Easily setup a simple repository
12
+ # * Simple integration with ActiveRecord
13
+ # * Dublin Core metadata format included
14
+ # * Easily add addition metadata formats
15
+ # * Adaptable to any data source
16
+ #
17
+ #
18
+ # === Current shortcomings
19
+ # * No resumption tokens
20
+ # * Doesn't validate metadata
21
+ # * No deletion support
22
+ # * Many others I can't think of right now. :-)
23
+ #
24
+ #
25
+ # === ActiveRecord integration
26
+ #
27
+ # To successfully use ActiveRecord as a OAI PMH datasource the database table
28
+ # should include an updated_at column so that updates to the table are
29
+ # tracked by ActiveRecord. This provides much of the base functionality for
30
+ # selecting update periods.
31
+ #
32
+ # To understand how the data is extracted from the AR model it's best to just
33
+ # go thru the logic:
34
+ #
35
+ # Does the model respond to 'to_{prefix}'? Where prefix is the
36
+ # metadata prefix. If it does then just include the response from
37
+ # the model. So if you want to provide custom or complex metadata you can
38
+ # simply define a 'to_{prefix}' method on your model.
39
+ #
40
+ # Example:
41
+ #
42
+ # class Record < ActiveRecord::Base
43
+ #
44
+ # def to_oai_dc
45
+ # xml = Builder::XmlMarkup.new
46
+ # xml.tag!('oai_dc:dc',
47
+ # 'xmlns:oai_dc' => "http://www.openarchives.org/OAI/2.0/oai_dc/",
48
+ # 'xmlns:dc' => "http://purl.org/dc/elements/1.1/",
49
+ # 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
50
+ # 'xsi:schemaLocation' =>
51
+ # %{http://www.openarchives.org/OAI/2.0/oai_dc/
52
+ # http://www.openarchives.org/OAI/2.0/oai_dc.xsd}) do
53
+ #
54
+ # xml.oai_dc :title, title
55
+ # xml.oai_dc :subject, subject
56
+ # end
57
+ # xml.to_s
58
+ # end
59
+ #
60
+ # end
61
+ #
62
+ # If the model doesn't define a 'to_{prefix}' then start iterating thru
63
+ # the defined metadata fields.
64
+ #
65
+ # Grab a mapping if one exists by trying to call 'map_{prefix}'.
66
+ #
67
+ # Now do the iteration and try calling methods on the model that match
68
+ # the field names, or the mapped field names.
69
+ #
70
+ # So with Dublin Core we end up with the following:
71
+ #
72
+ # 1. Check for 'title' mapped to a different method.
73
+ # 2. Call model.titles - try plural
74
+ # 3. Call model.title - try singular last
75
+ #
76
+ # Extremely contrived Blog example:
77
+ #
78
+ # class Post < ActiveRecord::Base
79
+ # def map_oai_dc
80
+ # {:subject => :tags,
81
+ # :description => :text,
82
+ # :creator => :user,
83
+ # :contibutor => :comments}
84
+ # end
85
+ # end
86
+ #
87
+ # === Supporting custom metadata
88
+ #
89
+ # See OaiPmh::Metadata for details.
90
+ #
91
+ # == Examples
92
+ #
93
+ # === Sub classing a provider
94
+ #
95
+ # class MyProvider < OaiPmh::Provider
96
+ # name 'My little OAI provider'
97
+ # url 'http://localhost/provider'
98
+ # prefix 'oai:localhost'
99
+ # email 'root@localhost' # String or Array
100
+ # deletes 'no' # future versions will support deletes
101
+ # granularity 'YYYY-MM-DDThh:mm:ssZ' # update resolution
102
+ # model MyModel # Class to get data from
103
+ # end
104
+ #
105
+ # # Now use it
106
+ #
107
+ # provider = MyProvider.new
108
+ # provider.identify
109
+ # provider.list_sets
110
+ # provider.list_metadata_formats
111
+ # # these verbs require a working model
112
+ # provider.list_identifiers
113
+ # provider.list_records
114
+ # provider.get_record('oai:localhost/1')
115
+ #
116
+ #
117
+ # === Configuring the default provider
118
+ #
119
+ # class OaiPmh::Provider
120
+ # name 'My little OAI Provider'
121
+ # url 'http://localhost/provider'
122
+ # prefix 'oai:localhost'
123
+ # email 'root@localhost' # String or Array
124
+ # deletes 'no' # future versions will support deletes
125
+ # granularity 'YYYY-MM-DDThh:mm:ssZ' # update resolution
126
+ # model MyModel # Class to get data from
127
+ # end
128
+ #
129
+ #
data/Rakefile CHANGED
@@ -10,9 +10,9 @@ require 'fileutils'
10
10
  include FileUtils
11
11
  require File.join(File.dirname(__FILE__), 'lib', 'oaipmh', 'version')
12
12
 
13
- AUTHOR = "will"
14
- EMAIL = "your contact email for bug fixes and info"
15
- DESCRIPTION = "description of gem"
13
+ AUTHOR = "Will Groppe"
14
+ EMAIL = "will.groppe@gmail.com"
15
+ DESCRIPTION = "OAI-PMH Provider"
16
16
  RUBYFORGE_PROJECT = "oaipmh"
17
17
  HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
18
18
  BIN_FILES = %w( )
@@ -57,8 +57,10 @@ spec =
57
57
  s.require_path = "lib"
58
58
  s.autorequire = "oaipmh"
59
59
 
60
- #s.add_dependency('activesupport', '>=1.3.1')
61
- #s.required_ruby_version = '>= 1.8.2'
60
+ s.add_dependency('activesupport', '>=1.3.1')
61
+ s.add_dependency('chronic', '>=0.1.4')
62
+ s.add_dependency('builder', '>=2.0.0')
63
+ s.required_ruby_version = '>= 1.8.2'
62
64
 
63
65
  s.files = %w(README CHANGELOG Rakefile) +
64
66
  Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
@@ -1,9 +1,8 @@
1
- # Copyright (C) 2006 William Groppe
2
- #
3
- # Will Groppe mailto:wfg@artstor.org
4
- #
5
- #
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'builder'
4
+ require 'chronic'
6
5
 
7
- Dir[File.join(File.dirname(__FILE__), 'oaipmh/**/*.rb')].sort.each do |lib|
6
+ Dir[File.join(File.dirname(__FILE__), 'oaipmh/*.rb')].sort.each do |lib|
8
7
  require lib
9
8
  end
@@ -1,5 +1,7 @@
1
1
  module OaiPmh
2
2
 
3
+ METADATA = {}
4
+
3
5
  module Const
4
6
 
5
7
  # OAI defines six verbs with various allowable options.
@@ -1,11 +1,11 @@
1
1
  require 'oaipmh'
2
2
 
3
3
  module OaiPmh
4
- module Extensions
5
- module Camped
4
+ module Goes
5
+ module Camping
6
6
 
7
7
  def self.included(mod)
8
- instance_eval(%{module #{mod}::Controllers
8
+ instance_eval(%{module ::#{mod}::Controllers
9
9
  class Oai
10
10
  def get
11
11
  @headers['Content-Type'] = 'text/xml'
@@ -1,7 +1,5 @@
1
1
  require 'oaipmh/metadata/oai_dc'
2
2
  module OaiPmh
3
-
4
- METADATA = {}
5
3
 
6
4
  def OaiPmh.register_metadata_class(metadata_class)
7
5
  METADATA[metadata_class.to_s] = metadata_class
@@ -132,28 +132,31 @@ module OaiPmh
132
132
  class Provider
133
133
  include Helpers
134
134
 
135
- @@options = {}
136
-
137
135
  AVAILABLE_FORMATS = {}
138
136
 
139
137
  class << self
140
-
141
- OaiPmh::Const::DEFAULTS.keys.each do |field|
142
- class_eval %{
143
- def #{field}(value)
144
- @@options[:#{field}] = value
145
- end
146
- }
147
- end
148
-
138
+ attr_accessor :options
139
+
149
140
  def model(value)
150
- @@options[:model] = value
141
+ self.options ||={}
142
+ self.options[:model] = value
151
143
  end
152
-
144
+
153
145
  end
154
146
 
147
+ OaiPmh::Const::DEFAULTS.keys.each do |field|
148
+ class_eval %{
149
+ def self.#{field}(value)
150
+ self.options ||={}
151
+ self.options[:#{field}] = value
152
+ end
153
+ }
154
+ end
155
+
155
156
  def initialize
156
- @config = OaiPmh::Const::DEFAULTS.merge(@@options)
157
+ @config = self.class.options ?
158
+ OaiPmh::Const::DEFAULTS.merge(self.class.options) :
159
+ OaiPmh::Const::DEFAULTS
157
160
  end
158
161
 
159
162
  def identify
@@ -235,12 +238,13 @@ module OaiPmh
235
238
  end
236
239
 
237
240
  def list_sets_response
238
- raise SetException.new unless sets
241
+ raise SetException.new unless @model && @model.oai_sets
239
242
  @xml.ListSets do |ls|
240
- oai_sets.each do |ms|
243
+ @model.oai_sets.each do |ms|
241
244
  @xml.set do |set|
242
245
  @xml.setSpec ms.spec
243
246
  @xml.setName ms.name
247
+ @xml.setDescription(ms.description) if ms.respond_to?(:description)
244
248
  end
245
249
  end
246
250
  end
@@ -250,19 +254,19 @@ module OaiPmh
250
254
  @xml.ListMetadataFormats do
251
255
  @config[:formats].each_pair do |key, format|
252
256
  @xml.metadataFormat do
253
- @xml.metadataPrefix instance_eval("#{format}.prefix")
254
- @xml.schema instance_eval("#{format}.schema")
255
- @xml.metadataNamespace instance_eval("#{format}.namespace")
257
+ @xml.metadataPrefix format.send(:prefix)
258
+ @xml.schema format.send(:schema)
259
+ @xml.metadataNamespace format.send(:namespace)
256
260
  end
257
261
  end
258
262
  end
259
263
  end
260
264
 
261
265
  def list_identifiers_response
262
- raise FORMAT_ERROR unless @config[:formats].include? @format
266
+ raise FormatException.new unless @config[:formats].include? @format
263
267
  records = find :all
264
268
 
265
- raise RECORDS_ERROR if records.nil? || records.empty?
269
+ raise NoMatchException.new if records.nil? || records.empty?
266
270
 
267
271
  @xml.ListIdentifiers do
268
272
  records.each do |record|
@@ -350,10 +354,12 @@ module OaiPmh
350
354
 
351
355
  # emit record header
352
356
  def metadata_header(record)
353
- @xml.header do |h|
354
- h.identifier "#{@config[:prefix]}/#{record.id}"
355
- h.datestamp record.updated_at.utc.xmlschema
356
- h.set @opts[:set] if @opts[:set]
357
+ @xml.header do
358
+ @xml.identifier "#{@config[:prefix]}/#{record.id}"
359
+ @xml.datestamp record.updated_at.utc.xmlschema
360
+ record.sets.each do |set|
361
+ @xml.setSpec set.spec
362
+ end if record.respond_to?(:sets)
357
363
  end
358
364
  end
359
365
 
@@ -369,8 +375,8 @@ module OaiPmh
369
375
  @xml << str
370
376
  end
371
377
  else
372
- map = record.respond_to?("map_#{@format}") ?
373
- instance_eval("record.map_#{@format}") : {}
378
+ map = @model.respond_to?("map_#{@format}") ?
379
+ @model.send("map_#{@format}") : {}
374
380
 
375
381
  mdformat = @config[:formats][@format]
376
382
  @xml.metadata do
@@ -378,7 +384,7 @@ module OaiPmh
378
384
  mdformat.fields.each do |field|
379
385
  set = value_for(field, record, map)
380
386
  set.each do |mdv|
381
- instance_eval("@xml.#{mdformat.element_ns} :#{field}, %{#{mdv}}")
387
+ @xml.tag! "#{mdformat.element_ns}:#{field}", mdv
382
388
  end
383
389
  end
384
390
  end
@@ -397,17 +403,20 @@ module OaiPmh
397
403
  # 4) Try calling the singular name method on the model, if it's not a
398
404
  # reserved word.
399
405
  def value_for(field, record, map)
400
- if map.keys.include?(field)
401
- return map[field].nil? ? [] :
402
- record.send("#{map[field]}").to_a
406
+ if map.keys.include?(field.intern)
407
+ value = record.send(map[field.intern])
408
+ if value.kind_of?(String)
409
+ return [value]
410
+ end
411
+ return value.to_a
403
412
  end
404
413
 
405
414
  begin
406
- return record.send("#{field.pluralize}").to_a
407
- rescue
415
+ return record.send(field.pluralize).to_a
416
+ rescue
408
417
  unless OaiPmh::Const::RESERVED_WORDS.include?(field)
409
418
  begin
410
- return record.send("#{field}").to_a
419
+ return [record.send(field)]
411
420
  rescue
412
421
  return []
413
422
  end
@@ -0,0 +1,30 @@
1
+ # = set.rb
2
+ #
3
+ # Copyright (C) 2006 William Groppe
4
+ #
5
+ # Will Groppe mailto: wfg@artstor.org
6
+ #
7
+ #
8
+ # Implementing a set from scratch requires overridding two methods from
9
+ # OaiPmh::Set
10
+ #
11
+ # * name - descriptive name of this set
12
+ # * spec - short name of set
13
+ #
14
+ # and optionally
15
+ #
16
+ # * description - long description of the set
17
+ #
18
+ module OaiPmh
19
+ class Set
20
+
21
+ def name
22
+ "unknown"
23
+ end
24
+
25
+ def spec
26
+ "set"
27
+ end
28
+
29
+ end
30
+ end
@@ -2,7 +2,7 @@ module Oaipmh #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 1
5
+ TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,11 +1,81 @@
1
+ require 'rexml/document'
1
2
  require File.dirname(__FILE__) + '/test_helper.rb'
2
3
 
4
+ class MappedProvider < OaiPmh::Provider
5
+ name 'Mapped Provider'
6
+ prefix 'oai:test'
7
+ model MappedModel
8
+ end
9
+
10
+ class SimpleProvider < OaiPmh::Provider
11
+ name 'Test Provider'
12
+ prefix 'oai:test'
13
+ model SimpleModel
14
+ end
15
+
3
16
  class OaipmhTest < Test::Unit::TestCase
4
17
 
5
18
  def setup
19
+ @simple_provider = SimpleProvider.new
20
+ @mapped_provider = MappedProvider.new
21
+ end
22
+
23
+ def test_indentify
24
+ doc = REXML::Document.new(@simple_provider.identify)
25
+ assert doc.elements["/OAI-PMH/Identify/repositoryName"].text == 'Test Provider'
26
+ assert doc.elements["/OAI-PMH/Identify/earliestDatestamp"].text == SimpleModel.oai_earliest.to_s
27
+ end
28
+
29
+ def test_list_sets
30
+ doc = REXML::Document.new(@simple_provider.list_sets)
31
+ sets = doc.elements["/OAI-PMH/ListSets"]
32
+ assert sets.size == 2
33
+ assert sets[0].elements["//setName"].text == "Test Set"
34
+ end
35
+
36
+ def test_metadata_formats
37
+ assert_nothing_raised { REXML::Document.new(@simple_provider.list_metadata_formats) }
38
+ doc = REXML::Document.new(@simple_provider.list_metadata_formats)
39
+ assert doc.elements['/OAI-PMH/ListMetadataFormats/metadataFormat/metadataPrefix'].text == 'oai_dc'
6
40
  end
7
41
 
8
- def test_truth
9
- assert true
42
+ def test_list_records
43
+ assert_nothing_raised { REXML::Document.new(@simple_provider.list_records) }
44
+ doc = REXML::Document.new(@simple_provider.list_records)
45
+ assert_equal 5, doc.elements['OAI-PMH/ListRecords'].to_a.size
46
+ doc = REXML::Document.new(@simple_provider.list_records(:set => 'A'))
47
+ assert_equal 5, doc.elements['OAI-PMH/ListRecords'].to_a.size
48
+ doc = REXML::Document.new(@simple_provider.list_records(:set => 'A:B'))
49
+ assert_equal 2, doc.elements['OAI-PMH/ListRecords'].to_a.size
10
50
  end
51
+
52
+ def test_list_identifiers
53
+ assert_nothing_raised { REXML::Document.new(@simple_provider.list_identifiers) }
54
+ doc = REXML::Document.new(@simple_provider.list_identifiers)
55
+ assert_equal 5, doc.elements['OAI-PMH/ListIdentifiers'].to_a.size
56
+ doc = REXML::Document.new(@simple_provider.list_identifiers(:set => 'A'))
57
+ assert_equal 5, doc.elements['OAI-PMH/ListIdentifiers'].to_a.size
58
+ doc = REXML::Document.new(@simple_provider.list_identifiers(:set => 'A:B'))
59
+ assert_equal 2, doc.elements['OAI-PMH/ListIdentifiers'].to_a.size
60
+ end
61
+
62
+ def test_get_record
63
+ assert_nothing_raised { REXML::Document.new(@simple_provider.get_record('oai:test/1')) }
64
+ doc = REXML::Document.new(@simple_provider.get_record('oai:test/1'))
65
+ assert_equal 'oai:test/1', doc.elements['OAI-PMH/GetRecord/record/header/identifier'].text
66
+ end
67
+
68
+ def test_mapped_source
69
+ assert_nothing_raised { REXML::Document.new(@mapped_provider.list_records) }
70
+ doc = REXML::Document.new(@mapped_provider.list_records)
71
+ assert_equal "title 1", doc.elements['OAI-PMH/ListRecords/record/metadata/oai_dc:dc/dc:creator'].text
72
+ assert_equal "creator", doc.elements['OAI-PMH/ListRecords/record/metadata/oai_dc:dc/dc:title'].text
73
+ assert_equal "tag 1", doc.elements['OAI-PMH/ListRecords/record/metadata/oai_dc:dc/dc:subject'].text
74
+ end
75
+
76
+ def test_verb_exception
77
+ doc = REXML::Document.new(@simple_provider.process_verb('NoVerb'))
78
+ assert doc.elements["/OAI-PMH/error"].attributes["code"] == 'badVerb'
79
+ end
80
+
11
81
  end
@@ -1,2 +1,106 @@
1
1
  require 'test/unit'
2
2
  require File.dirname(__FILE__) + '/../lib/oaipmh'
3
+
4
+ class Record
5
+ attr_accessor :id, :titles, :creator, :tags, :sets, :updated_at
6
+
7
+ def initialize(id, titles, creator, tags, sets)
8
+ @id = id;
9
+ @titles = titles
10
+ @creator = creator
11
+ @tags = tags
12
+ @sets = sets
13
+ @updated_at = Time.new.utc
14
+ end
15
+
16
+ # Override Object.id
17
+ def id
18
+ @id
19
+ end
20
+
21
+ def in_set(spec)
22
+ @sets.each { |set| return true if set.spec == spec }
23
+ false
24
+ end
25
+
26
+ end
27
+
28
+ class OneSet < OaiPmh::Set
29
+
30
+ def name
31
+ "Test Set"
32
+ end
33
+
34
+ def spec
35
+ "A"
36
+ end
37
+
38
+ def description
39
+ "A long winded description of this set."
40
+ end
41
+
42
+ end
43
+
44
+ class TwoSet < OaiPmh::Set
45
+
46
+ def name
47
+ "Not so test Set"
48
+ end
49
+
50
+ def spec
51
+ "A:B"
52
+ end
53
+
54
+ def description
55
+ "A short winded description of this set."
56
+ end
57
+
58
+ end
59
+
60
+
61
+
62
+
63
+ class SimpleModel
64
+ include OaiPmh::Model
65
+
66
+ RECORDS = [
67
+ Record.new(1, ['title 1', 'title 2'], 'creator', ['tag 1', 'tag 2'], [OneSet.new]),
68
+ Record.new(2, ['title 3', 'title 4'], 'creator', ['tag 3', 'tag 4'], [OneSet.new]),
69
+ Record.new(3, ['title 5', 'title 6'], 'creator', ['tag 5', 'tag 6'], [OneSet.new]),
70
+ Record.new(4, ['title 7', 'title 8'], 'creator', ['tag 9', 'tag 8'], [OneSet.new, TwoSet.new]),
71
+ Record.new(5, ['title 9', 'title 10'], 'creator', ['tag 9', 'tag 10'], [OneSet.new, TwoSet.new]),
72
+ ]
73
+
74
+ class << self
75
+ def oai_earliest
76
+ Time.parse("2006-10-31T00:00:00Z")
77
+ end
78
+
79
+ def oai_sets
80
+ [OneSet.new, TwoSet.new]
81
+ end
82
+
83
+ def oai_find(selector, opts = {})
84
+ if selector == :all
85
+ if opts[:set]
86
+ return RECORDS.select { |rec| rec.in_set(opts[:set]) }
87
+ else
88
+ return RECORDS
89
+ end
90
+ else
91
+ RECORDS.each do |record|
92
+ return record if record.id.to_s == selector
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ class MappedModel < SimpleModel
100
+
101
+ def self.map_oai_dc
102
+ {:title => :creator, :creator => :titles, :subject => :tags}
103
+ end
104
+
105
+ end
106
+
metadata CHANGED
@@ -3,31 +3,31 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: oaipmh
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2006-11-02 00:00:00 -05:00
8
- summary: description of gem
6
+ version: 0.0.2
7
+ date: 2006-11-07 00:00:00 -05:00
8
+ summary: OAI-PMH Provider
9
9
  require_paths:
10
10
  - lib
11
- email: your contact email for bug fixes and info
11
+ email: will.groppe@gmail.com
12
12
  homepage: http://oaipmh.rubyforge.org
13
13
  rubyforge_project: oaipmh
14
- description: description of gem
14
+ description: OAI-PMH Provider
15
15
  autorequire: oaipmh
16
16
  default_executable:
17
17
  bindir: bin
18
18
  has_rdoc: true
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
- - - ">"
21
+ - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.0.0
23
+ version: 1.8.2
24
24
  version:
25
25
  platform: ruby
26
26
  signing_key:
27
27
  cert_chain:
28
28
  post_install_message:
29
29
  authors:
30
- - will
30
+ - Will Groppe
31
31
  files:
32
32
  - README
33
33
  - CHANGELOG
@@ -44,6 +44,7 @@ files:
44
44
  - lib/oaipmh/metadata.rb
45
45
  - lib/oaipmh/model.rb
46
46
  - lib/oaipmh/provider.rb
47
+ - lib/oaipmh/set.rb
47
48
  - lib/oaipmh/version.rb
48
49
  - lib/oaipmh/extensions/camping.rb
49
50
  - lib/oaipmh/metadata/oai_dc.rb
@@ -70,5 +71,31 @@ extensions: []
70
71
 
71
72
  requirements: []
72
73
 
73
- dependencies: []
74
-
74
+ dependencies:
75
+ - !ruby/object:Gem::Dependency
76
+ name: activesupport
77
+ version_requirement:
78
+ version_requirements: !ruby/object:Gem::Version::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.3.1
83
+ version:
84
+ - !ruby/object:Gem::Dependency
85
+ name: chronic
86
+ version_requirement:
87
+ version_requirements: !ruby/object:Gem::Version::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 0.1.4
92
+ version:
93
+ - !ruby/object:Gem::Dependency
94
+ name: builder
95
+ version_requirement:
96
+ version_requirements: !ruby/object:Gem::Version::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 2.0.0
101
+ version: