indexer 0.1.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.
- data/.index +54 -0
- data/HISTORY.md +9 -0
- data/README.md +145 -0
- data/bin/index +7 -0
- data/data/indexer/r2013/index.kwalify +175 -0
- data/data/indexer/r2013/index.yes +172 -0
- data/data/indexer/r2013/index.yesi +67 -0
- data/data/indexer/r2013/ruby.txt +35 -0
- data/data/indexer/r2013/yaml.txt +30 -0
- data/lib/indexer.rb +65 -0
- data/lib/indexer/attributes.rb +171 -0
- data/lib/indexer/command.rb +260 -0
- data/lib/indexer/components.rb +8 -0
- data/lib/indexer/components/author.rb +140 -0
- data/lib/indexer/components/conflict.rb +78 -0
- data/lib/indexer/components/copyright.rb +95 -0
- data/lib/indexer/components/dependency.rb +18 -0
- data/lib/indexer/components/organization.rb +133 -0
- data/lib/indexer/components/repository.rb +140 -0
- data/lib/indexer/components/requirement.rb +360 -0
- data/lib/indexer/components/resource.rb +209 -0
- data/lib/indexer/conversion.rb +14 -0
- data/lib/indexer/conversion/gemfile.rb +44 -0
- data/lib/indexer/conversion/gemspec.rb +114 -0
- data/lib/indexer/conversion/gemspec_exporter.rb +304 -0
- data/lib/indexer/core_ext.rb +4 -0
- data/lib/indexer/error.rb +23 -0
- data/lib/indexer/gemfile.rb +75 -0
- data/lib/indexer/importer.rb +144 -0
- data/lib/indexer/importer/file.rb +94 -0
- data/lib/indexer/importer/gemfile.rb +27 -0
- data/lib/indexer/importer/gemspec.rb +43 -0
- data/lib/indexer/importer/html.rb +289 -0
- data/lib/indexer/importer/markdown.rb +45 -0
- data/lib/indexer/importer/ruby.rb +47 -0
- data/lib/indexer/importer/version.rb +38 -0
- data/lib/indexer/importer/yaml.rb +46 -0
- data/lib/indexer/loadable.rb +159 -0
- data/lib/indexer/metadata.rb +879 -0
- data/lib/indexer/model.rb +237 -0
- data/lib/indexer/revision.rb +43 -0
- data/lib/indexer/valid.rb +287 -0
- data/lib/indexer/validator.rb +313 -0
- data/lib/indexer/version/constraint.rb +124 -0
- data/lib/indexer/version/exceptions.rb +11 -0
- data/lib/indexer/version/number.rb +497 -0
- metadata +141 -0
@@ -0,0 +1,209 @@
|
|
1
|
+
module Indexer
|
2
|
+
|
3
|
+
# A resource encapsulates information about a URI related to a project.
|
4
|
+
#
|
5
|
+
# TODO: This model is a little tricky b/c it's difficult to settle on a standard
|
6
|
+
# set of types or labels. It is also difficult to determine if label's and types
|
7
|
+
# should in fact be infered or just left empty if not provided. Any insight welcome!
|
8
|
+
#
|
9
|
+
class Resource < Model
|
10
|
+
|
11
|
+
# Parse `data` returning a new Resource instance.
|
12
|
+
#
|
13
|
+
# @param data [String,Array<String>,Array<Hash>,Hash]
|
14
|
+
# Resource information.
|
15
|
+
#
|
16
|
+
# @return [Resource] resource instance
|
17
|
+
def self.parse(data)
|
18
|
+
case data
|
19
|
+
when String
|
20
|
+
new(:uri=>data)
|
21
|
+
when Array
|
22
|
+
h, d = {}, data.dup # TODO: data.rekey(&:to_s)
|
23
|
+
h.update(d.pop) while Hash === d.last
|
24
|
+
if x = d.find{ |e| Valid.uri?(e) }
|
25
|
+
h[:uri] = d.delete(x)
|
26
|
+
end
|
27
|
+
if x = d.find{ |e| Valid.word?(e) && e.downcase == e }
|
28
|
+
h[:type] = d.delete(x)
|
29
|
+
end
|
30
|
+
h[:label] = d.shift unless d.empty?
|
31
|
+
raise ValidationError, "malformed resource -- #{data.inspect}" unless d.empty?
|
32
|
+
new(h)
|
33
|
+
when Hash
|
34
|
+
new(data)
|
35
|
+
else
|
36
|
+
raise(ValidationError, "not a valid resource")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Initialize new Resource instance.
|
42
|
+
#
|
43
|
+
def initialize(data={})
|
44
|
+
@data = {}
|
45
|
+
|
46
|
+
data = data.rekey(&:to_sym)
|
47
|
+
|
48
|
+
## best to assign in this order
|
49
|
+
self.uri = data.delete(:uri)
|
50
|
+
self.type = data.delete(:type) if data.key?(:type)
|
51
|
+
|
52
|
+
merge!(data)
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# A label that can be used to identify the purpose of a
|
57
|
+
# particular repository.
|
58
|
+
#
|
59
|
+
attr :label
|
60
|
+
|
61
|
+
#
|
62
|
+
# Set the resource name. This can be any brief one line description.
|
63
|
+
#
|
64
|
+
# Examples
|
65
|
+
#
|
66
|
+
# resource.label = "Homepage"
|
67
|
+
#
|
68
|
+
def label=(label)
|
69
|
+
Valid.oneline!(label) # should be word!
|
70
|
+
@data[:type] = infer_type(label) unless @type
|
71
|
+
@data[:label] = label.to_str
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Alias for #label.
|
76
|
+
#
|
77
|
+
alias :name :label
|
78
|
+
alias :name= :label=
|
79
|
+
|
80
|
+
#
|
81
|
+
# The repository's URI.
|
82
|
+
#
|
83
|
+
attr :uri
|
84
|
+
|
85
|
+
#
|
86
|
+
# Set repository URI
|
87
|
+
#
|
88
|
+
def uri=(uri)
|
89
|
+
Valid.uri!(uri) # TODO: any other limitations?
|
90
|
+
@data[:type] = infer_type(uri) unless @type
|
91
|
+
@data[:uri] = uri
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
#
|
96
|
+
#
|
97
|
+
attr :type
|
98
|
+
|
99
|
+
#
|
100
|
+
# Set the type of the resource. The type is a single downcased word.
|
101
|
+
# It can be anything but generally recognized types are:
|
102
|
+
#
|
103
|
+
# * bugs, issues
|
104
|
+
# * mail, email
|
105
|
+
# * chat, irc
|
106
|
+
# * home, homepage, website
|
107
|
+
# * work, code
|
108
|
+
#
|
109
|
+
def type=(type)
|
110
|
+
Valid.word!(type)
|
111
|
+
@data[:label] = infer_label(type) unless @label
|
112
|
+
@data[:type] = type.to_str.downcase
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
## Convert resource to Hash.
|
117
|
+
##
|
118
|
+
## @return [Hash]
|
119
|
+
##
|
120
|
+
#def to_h
|
121
|
+
# h = {}
|
122
|
+
# h['uri'] = uri
|
123
|
+
# h['label'] = label if label
|
124
|
+
# h['type'] = type if type
|
125
|
+
# h
|
126
|
+
#end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Recognized types and the default labels that do with them.
|
130
|
+
#
|
131
|
+
TYPE_LABELS = {
|
132
|
+
'work' => "Development",
|
133
|
+
'dev' => "Development",
|
134
|
+
'code' => "Source Code",
|
135
|
+
'source' => "Source Code",
|
136
|
+
'bugs' => "Issue Tracker",
|
137
|
+
'issues' => "Issue Tracker",
|
138
|
+
'mail' => "Mailing List",
|
139
|
+
'list' => "Mailing List",
|
140
|
+
'forum' => "Support Forum",
|
141
|
+
'support' => "Support Forum",
|
142
|
+
'faq' => "Fact Sheet",
|
143
|
+
'home' => "Homepage",
|
144
|
+
'homepage' => "Homepage",
|
145
|
+
'web' => "Website",
|
146
|
+
'website' => "Website",
|
147
|
+
'blog' => "Blog",
|
148
|
+
'pkg' => "Download",
|
149
|
+
'dist' => "Download",
|
150
|
+
'api' => "API Guide",
|
151
|
+
'irc' => "IRC Channel",
|
152
|
+
'chat' => "IRC Channel",
|
153
|
+
'doc' => "Documentation",
|
154
|
+
'docs' => "Documentation",
|
155
|
+
'wiki' => "User Guide"
|
156
|
+
}
|
157
|
+
|
158
|
+
#
|
159
|
+
#
|
160
|
+
#
|
161
|
+
KNOWN_TYPES = TYPE_LABELS.keys
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
#
|
166
|
+
#
|
167
|
+
#
|
168
|
+
def infer_label(type)
|
169
|
+
TYPE_LABELS[type.to_s.downcase]
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# @todo Deciding on a set of recognized types and inferences for them is rather tricky.
|
174
|
+
# Some sort of guiding design principle would be helpful.
|
175
|
+
#
|
176
|
+
def infer_type(string)
|
177
|
+
case string.to_s.downcase
|
178
|
+
when /work/i, /dev/i
|
179
|
+
'dev' #'development'
|
180
|
+
when /code/i, /source/i
|
181
|
+
'code' #'source-code'
|
182
|
+
when /bugs/i, /issue/i, /tracker/i
|
183
|
+
'bugs' #'issue-tracker'
|
184
|
+
when /mail/i, /list/i
|
185
|
+
'mail' #'mailing-list'
|
186
|
+
when /chat/i, /irc/i
|
187
|
+
'chat' #'online-chat'
|
188
|
+
when /forum/i, /support/i, /q\&a/i
|
189
|
+
'help' #'support-forum'
|
190
|
+
when /wiki/i, /user/i
|
191
|
+
'wiki' #'wiki-wiki'
|
192
|
+
when /blog/i, /weblog/i
|
193
|
+
'blog' #'weblog'
|
194
|
+
when /home/i, /website/i
|
195
|
+
'home' #'homepage'
|
196
|
+
when /gem/i, /packge/i, /dist/i, /download/i
|
197
|
+
'dist' #'package' or 'distribution' ?
|
198
|
+
when /api/i, /reference/i, /guide/i
|
199
|
+
'api' #'reference' ???
|
200
|
+
when /doc/i
|
201
|
+
'doc' #'documentation'
|
202
|
+
else
|
203
|
+
nil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Indexer
|
2
|
+
|
3
|
+
# Conversion module provides routines for converting
|
4
|
+
# other metadata sources to and from index format.
|
5
|
+
#
|
6
|
+
module Conversion
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
require_relative 'conversion/gemspec'
|
12
|
+
require_relative 'conversion/gemspec_exporter'
|
13
|
+
require_relative 'conversion/gemfile'
|
14
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Indexer
|
2
|
+
|
3
|
+
module Conversion
|
4
|
+
|
5
|
+
#
|
6
|
+
# Update Spec with a Gemfile.
|
7
|
+
#
|
8
|
+
# @param [nil,String,::Bundler::DSL,::Bundler::Definition]
|
9
|
+
# Gemfile path, Bundler::Dsl or Bundler::Definition instance.
|
10
|
+
#
|
11
|
+
def import_gemfile(file=nil)
|
12
|
+
require 'bundler'
|
13
|
+
|
14
|
+
case file
|
15
|
+
when String
|
16
|
+
# FIXME: Is this the correct way fot load a gemfile?
|
17
|
+
bundle = ::Bundler::Dsl.new
|
18
|
+
bundle.eval_gemfile(file)
|
19
|
+
when NilClass
|
20
|
+
bundle = ::Bundler.definition
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
merge!(bundle.metadata.to_h)
|
25
|
+
rescue
|
26
|
+
end
|
27
|
+
|
28
|
+
bundle.dependencies.each do |d|
|
29
|
+
add_requirement(d.name,
|
30
|
+
:version => d.requirement.to_s, # may need to parse this
|
31
|
+
:groups => d.groups,
|
32
|
+
:development => (d.type == :development)
|
33
|
+
#:engines => d.platforms ?
|
34
|
+
#:platforms => ?
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
alias_method :gemfile, :import_gemfile
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Indexer
|
2
|
+
|
3
|
+
module Conversion
|
4
|
+
|
5
|
+
#
|
6
|
+
# Create a Gem::Specification from Metadata.
|
7
|
+
#
|
8
|
+
# Becuase the specificaiton is extensive, a Gem::Specification
|
9
|
+
# can be created that is sufficient for most needs. However, there
|
10
|
+
# are a few points where the two do not intersect. In these cases
|
11
|
+
# the remaining gemspec fields need to be provided post conversion.
|
12
|
+
#
|
13
|
+
# Gem::Specification fields that the specification can't provide include:
|
14
|
+
#
|
15
|
+
# * platform
|
16
|
+
#
|
17
|
+
# In addition, some information can only be provided if a project's `root`
|
18
|
+
# directory is given. In these cases the most common project conventions
|
19
|
+
# are utilized in determining field values. Gem::Specification fields that
|
20
|
+
# draw from project files include:
|
21
|
+
#
|
22
|
+
# * files
|
23
|
+
# * bindir
|
24
|
+
# * extensions
|
25
|
+
# * rdoc_options
|
26
|
+
# * rdoc_extra_files
|
27
|
+
#
|
28
|
+
# Any or all of which can be reassigned post conversion, if need be.
|
29
|
+
#
|
30
|
+
# @param [Hash] options
|
31
|
+
# Convertion options.
|
32
|
+
#
|
33
|
+
# @option options [NilClass,String] :root
|
34
|
+
# project root directory
|
35
|
+
#
|
36
|
+
def to_gemspec(options={})
|
37
|
+
options[:data] = self.to_h
|
38
|
+
GemspecExporter.new(options).to_gemspec
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Import a Gem::Specification into Spec. This is intended to make it
|
43
|
+
# fairly easy to build the metadata from a pre-existing `.gemspec`.
|
44
|
+
#
|
45
|
+
# By making this an instance method, it is possible to import other
|
46
|
+
# resources into the Spec prior to a `.gemspec`.
|
47
|
+
#
|
48
|
+
# @todo Ensure all data possible is gathered from the gemspec.
|
49
|
+
#
|
50
|
+
def import_gemspec(gemspec)
|
51
|
+
require 'rubygems'
|
52
|
+
|
53
|
+
if not Gem::Specification === gemspec
|
54
|
+
# TODO: YAML-based gem specs ?
|
55
|
+
gemspec = Gem::Specification.load(gemspec)
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO: ensure this is robust
|
59
|
+
authors = (
|
60
|
+
zip = [gemspec.authors].flatten.zip([gemspec.email].flatten)
|
61
|
+
zip.map do |(name, email)|
|
62
|
+
email ? {:name=>name, :email=>email} : {:name=>name}
|
63
|
+
end
|
64
|
+
)
|
65
|
+
|
66
|
+
# TODO: how to handle license(s) ?
|
67
|
+
|
68
|
+
self.name = gemspec.name
|
69
|
+
self.version = gemspec.version.to_s
|
70
|
+
self.date = gemspec.date
|
71
|
+
self.title = gemspec.name.capitalize
|
72
|
+
self.summary = gemspec.summary
|
73
|
+
self.description = gemspec.description || gemspec.summary
|
74
|
+
self.authors = authors
|
75
|
+
self.load_path = gemspec.require_paths
|
76
|
+
self.homepage = gemspec.homepage
|
77
|
+
|
78
|
+
#self.engines = gemspec.platform
|
79
|
+
#self.extensions = gemspec.extensions
|
80
|
+
|
81
|
+
# TODO: Spec currently doesn't support multiple constraints for requirements.
|
82
|
+
# Probably 99.999% of the time it doesn't matter.
|
83
|
+
gemspec.dependencies.each do |d|
|
84
|
+
if d.type == :runtime
|
85
|
+
add_requirement(d.name, :versions=>d.requirements_list.first)
|
86
|
+
else
|
87
|
+
add_requirement(d.name, :versions=>d.requirements_list.first, :development=>true)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
=begin
|
98
|
+
|
99
|
+
case gemspec
|
100
|
+
when ::Gem::Specification
|
101
|
+
spec = gemspec
|
102
|
+
else
|
103
|
+
file = Dir[root + "{*,}.gemspec"].first
|
104
|
+
return unless file
|
105
|
+
|
106
|
+
text = File.read(file)
|
107
|
+
if text =~ /\A---/
|
108
|
+
spec = ::Gem::Specification.from_yaml(text)
|
109
|
+
else
|
110
|
+
spec = ::Gem::Specification.load(file)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
=end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module Indexer
|
7
|
+
|
8
|
+
# Convert index data into a gemspec.
|
9
|
+
#
|
10
|
+
# Notes:
|
11
|
+
# * Assumes all executables are in bin/.
|
12
|
+
# * Does not yet handle default_executable setting.
|
13
|
+
# * Does not yet handle platform setting.
|
14
|
+
# * Does not yet handle required_ruby_version.
|
15
|
+
# * Support for rdoc entries is weak.
|
16
|
+
#
|
17
|
+
class GemspecExporter
|
18
|
+
|
19
|
+
# File globs to include in package --unless a manifest file exists.
|
20
|
+
FILES = ".index .yardopts alt bin data demo ext features lib man spec test try* [A-Z]*.*" unless defined?(FILES)
|
21
|
+
|
22
|
+
# File globs to omit from FILES.
|
23
|
+
OMIT = "Config.rb" unless defined?(OMIT)
|
24
|
+
|
25
|
+
# Standard file patterns.
|
26
|
+
PATTERNS = {
|
27
|
+
:root => '{.index,Gemfile}',
|
28
|
+
:bin => 'bin/*',
|
29
|
+
:lib => 'lib/{**/}*', #.rb',
|
30
|
+
:ext => 'ext/{**/}extconf.rb',
|
31
|
+
:doc => '*.{txt,rdoc,md,markdown,tt,textile}',
|
32
|
+
:test => '{test,spec}/{**/}*.rb'
|
33
|
+
} unless defined?(PATTERNS)
|
34
|
+
|
35
|
+
# For which revision of indexer spec is this converter intended?
|
36
|
+
REVISION = 2013 unless defined?(REVISION)
|
37
|
+
|
38
|
+
#
|
39
|
+
def self.gemspec
|
40
|
+
new.to_gemspec
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
attr :metadata
|
45
|
+
|
46
|
+
#
|
47
|
+
def initialize(metadata=nil)
|
48
|
+
@root_check = false
|
49
|
+
|
50
|
+
if metadata
|
51
|
+
root_dir = metadata.delete(:root)
|
52
|
+
if root_dir
|
53
|
+
@root = root_dir
|
54
|
+
@root_check = true
|
55
|
+
end
|
56
|
+
metadata = nil if metadata.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
@metadata = metadata || YAML.load_file(root + '.index')
|
60
|
+
|
61
|
+
if @metadata['revision'].to_i != REVISION
|
62
|
+
warn "This gemspec exporter was not designed for this revision of index metadata."
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
def has_root?
|
68
|
+
root ? true : false
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
def root
|
73
|
+
return @root if @root || @root_check
|
74
|
+
@root_check = true
|
75
|
+
@root = find_root
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
def manifest
|
80
|
+
return nil unless root
|
81
|
+
@manifest ||= Dir.glob(root + 'manifest{,.txt}', File::FNM_CASEFOLD).first
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
def scm
|
86
|
+
return nil unless root
|
87
|
+
@scm ||= %w{git hg}.find{ |m| (root + ".#{m}").directory? }.to_sym
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
def files
|
92
|
+
return [] unless root
|
93
|
+
@files ||= \
|
94
|
+
if manifest
|
95
|
+
File.readlines(manifest).
|
96
|
+
map{ |line| line.strip }.
|
97
|
+
reject{ |line| line.empty? || line[0,1] == '#' }
|
98
|
+
else
|
99
|
+
list = []
|
100
|
+
Dir.chdir(root) do
|
101
|
+
FILES.split(/\s+/).each do |pattern|
|
102
|
+
list.concat(glob(pattern))
|
103
|
+
end
|
104
|
+
OMIT.split(/\s+/).each do |pattern|
|
105
|
+
list = list - glob(pattern)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
list
|
109
|
+
end.select{ |path| File.file?(path) }.uniq
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
def glob_files(pattern)
|
114
|
+
return [] unless root
|
115
|
+
Dir.chdir(root) do
|
116
|
+
Dir.glob(pattern).select do |path|
|
117
|
+
File.file?(path) && files.include?(path)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def patterns
|
123
|
+
PATTERNS
|
124
|
+
end
|
125
|
+
|
126
|
+
def executables
|
127
|
+
@executables ||= \
|
128
|
+
glob_files(patterns[:bin]).map do |path|
|
129
|
+
File.basename(path)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def extensions
|
134
|
+
@extensions ||= \
|
135
|
+
glob_files(patterns[:ext]).map do |path|
|
136
|
+
File.basename(path)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def name
|
141
|
+
metadata['name'] || metadata['title'].downcase.gsub(/\W+/,'_')
|
142
|
+
end
|
143
|
+
|
144
|
+
def homepage
|
145
|
+
page = (
|
146
|
+
metadata['resources'].find{ |r| r['type'] =~ /^home/i } ||
|
147
|
+
metadata['resources'].find{ |r| r['name'] =~ /^home/i } ||
|
148
|
+
metadata['resources'].find{ |r| r['name'] =~ /^web/i }
|
149
|
+
)
|
150
|
+
page ? page['uri'] : false
|
151
|
+
end
|
152
|
+
|
153
|
+
def licenses
|
154
|
+
metadata['copyrights'].map{ |c| c['license'] }.compact
|
155
|
+
end
|
156
|
+
|
157
|
+
def require_paths
|
158
|
+
metadata['load_path'] || ['lib']
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# Convert to gemnspec.
|
163
|
+
#
|
164
|
+
def to_gemspec
|
165
|
+
if has_root?
|
166
|
+
Gem::Specification.new do |gemspec|
|
167
|
+
to_gemspec_data(gemspec)
|
168
|
+
to_gemspec_paths(gemspec)
|
169
|
+
end
|
170
|
+
else
|
171
|
+
Gem::Specification.new do |gemspec|
|
172
|
+
to_gemspec_data(gemspec)
|
173
|
+
to_gemspec_paths(gemspec)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
#
|
179
|
+
# Convert pure data settings.
|
180
|
+
#
|
181
|
+
def to_gemspec_data(gemspec)
|
182
|
+
gemspec.name = name
|
183
|
+
gemspec.version = metadata['version']
|
184
|
+
gemspec.summary = metadata['summary']
|
185
|
+
gemspec.description = metadata['description']
|
186
|
+
|
187
|
+
metadata['authors'].each do |author|
|
188
|
+
gemspec.authors << author['name']
|
189
|
+
|
190
|
+
if author.has_key?('email')
|
191
|
+
if gemspec.email
|
192
|
+
gemspec.email << author['email']
|
193
|
+
else
|
194
|
+
gemspec.email = [author['email']]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
gemspec.licenses = licenses
|
200
|
+
|
201
|
+
requirements = metadata['requirements'] || []
|
202
|
+
requirements.each do |req|
|
203
|
+
next if req['optional']
|
204
|
+
next if req['external']
|
205
|
+
|
206
|
+
name = req['name']
|
207
|
+
groups = req['groups'] || []
|
208
|
+
|
209
|
+
version = gemify_version(req['version'])
|
210
|
+
|
211
|
+
if groups.empty? or groups.include?('runtime')
|
212
|
+
# populate runtime dependencies
|
213
|
+
if gemspec.respond_to?(:add_runtime_dependency)
|
214
|
+
gemspec.add_runtime_dependency(name,*version)
|
215
|
+
else
|
216
|
+
gemspec.add_dependency(name,*version)
|
217
|
+
end
|
218
|
+
else
|
219
|
+
# populate development dependencies
|
220
|
+
if gemspec.respond_to?(:add_development_dependency)
|
221
|
+
gemspec.add_development_dependency(name,*version)
|
222
|
+
else
|
223
|
+
gemspec.add_dependency(name,*version)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# convert external dependencies into gemspec requirements
|
229
|
+
requirements.each do |req|
|
230
|
+
next unless req['external']
|
231
|
+
gemspec.requirements << ("%s-%s" % req.values_at('name', 'version'))
|
232
|
+
end
|
233
|
+
|
234
|
+
gemspec.homepage = homepage
|
235
|
+
gemspec.require_paths = require_paths
|
236
|
+
gemspec.post_install_message = metadata['install_message']
|
237
|
+
end
|
238
|
+
|
239
|
+
#
|
240
|
+
# Set gemspec settings that require a root directory path.
|
241
|
+
#
|
242
|
+
def to_gemspec_paths(gemspec)
|
243
|
+
gemspec.files = files
|
244
|
+
gemspec.extensions = extensions
|
245
|
+
gemspec.executables = executables
|
246
|
+
|
247
|
+
if Gem::VERSION < '1.7.'
|
248
|
+
gemspec.default_executable = gemspec.executables.first
|
249
|
+
end
|
250
|
+
|
251
|
+
gemspec.test_files = glob_files(patterns[:test])
|
252
|
+
|
253
|
+
unless gemspec.files.include?('.document')
|
254
|
+
gemspec.extra_rdoc_files = glob_files(patterns[:doc])
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
#
|
259
|
+
# Return a copy of this file. This is used to generate a local
|
260
|
+
# .gemspec file that can automatically read the index file.
|
261
|
+
#
|
262
|
+
def self.source_code
|
263
|
+
File.read(__FILE__)
|
264
|
+
end
|
265
|
+
|
266
|
+
private
|
267
|
+
|
268
|
+
def find_root
|
269
|
+
root_files = patterns[:root]
|
270
|
+
if Dir.glob(root_files).first
|
271
|
+
Pathname.new(Dir.pwd)
|
272
|
+
elsif Dir.glob("../#{root_files}").first
|
273
|
+
Pathname.new(Dir.pwd).parent
|
274
|
+
else
|
275
|
+
#raise "Can't find root of project containing `#{root_files}'."
|
276
|
+
warn "Can't find root of project containing `#{root_files}'."
|
277
|
+
nil
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def glob(pattern)
|
282
|
+
if File.directory?(pattern)
|
283
|
+
Dir.glob(File.join(pattern, '**', '*'))
|
284
|
+
else
|
285
|
+
Dir.glob(pattern)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def gemify_version(version)
|
290
|
+
case version
|
291
|
+
when /^(.*?)\+$/
|
292
|
+
">= #{$1}"
|
293
|
+
when /^(.*?)\-$/
|
294
|
+
"< #{$1}"
|
295
|
+
when /^(.*?)\~$/
|
296
|
+
"~> #{$1}"
|
297
|
+
else
|
298
|
+
version
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|