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.
Files changed (47) hide show
  1. data/.index +54 -0
  2. data/HISTORY.md +9 -0
  3. data/README.md +145 -0
  4. data/bin/index +7 -0
  5. data/data/indexer/r2013/index.kwalify +175 -0
  6. data/data/indexer/r2013/index.yes +172 -0
  7. data/data/indexer/r2013/index.yesi +67 -0
  8. data/data/indexer/r2013/ruby.txt +35 -0
  9. data/data/indexer/r2013/yaml.txt +30 -0
  10. data/lib/indexer.rb +65 -0
  11. data/lib/indexer/attributes.rb +171 -0
  12. data/lib/indexer/command.rb +260 -0
  13. data/lib/indexer/components.rb +8 -0
  14. data/lib/indexer/components/author.rb +140 -0
  15. data/lib/indexer/components/conflict.rb +78 -0
  16. data/lib/indexer/components/copyright.rb +95 -0
  17. data/lib/indexer/components/dependency.rb +18 -0
  18. data/lib/indexer/components/organization.rb +133 -0
  19. data/lib/indexer/components/repository.rb +140 -0
  20. data/lib/indexer/components/requirement.rb +360 -0
  21. data/lib/indexer/components/resource.rb +209 -0
  22. data/lib/indexer/conversion.rb +14 -0
  23. data/lib/indexer/conversion/gemfile.rb +44 -0
  24. data/lib/indexer/conversion/gemspec.rb +114 -0
  25. data/lib/indexer/conversion/gemspec_exporter.rb +304 -0
  26. data/lib/indexer/core_ext.rb +4 -0
  27. data/lib/indexer/error.rb +23 -0
  28. data/lib/indexer/gemfile.rb +75 -0
  29. data/lib/indexer/importer.rb +144 -0
  30. data/lib/indexer/importer/file.rb +94 -0
  31. data/lib/indexer/importer/gemfile.rb +27 -0
  32. data/lib/indexer/importer/gemspec.rb +43 -0
  33. data/lib/indexer/importer/html.rb +289 -0
  34. data/lib/indexer/importer/markdown.rb +45 -0
  35. data/lib/indexer/importer/ruby.rb +47 -0
  36. data/lib/indexer/importer/version.rb +38 -0
  37. data/lib/indexer/importer/yaml.rb +46 -0
  38. data/lib/indexer/loadable.rb +159 -0
  39. data/lib/indexer/metadata.rb +879 -0
  40. data/lib/indexer/model.rb +237 -0
  41. data/lib/indexer/revision.rb +43 -0
  42. data/lib/indexer/valid.rb +287 -0
  43. data/lib/indexer/validator.rb +313 -0
  44. data/lib/indexer/version/constraint.rb +124 -0
  45. data/lib/indexer/version/exceptions.rb +11 -0
  46. data/lib/indexer/version/number.rb +497 -0
  47. metadata +141 -0
@@ -0,0 +1,237 @@
1
+ module Indexer
2
+
3
+ class Model
4
+
5
+ class << self
6
+
7
+ =begin
8
+ #
9
+ # Revision factory returns a versioned instance of the model class.
10
+ #
11
+ # @param [Hash] data
12
+ # The data to populate the instance.
13
+ #
14
+ def new(data={})
15
+ revision, data = revised(data)
16
+ basename = name.split('::').last
17
+ V[revision].const_get(basename).new(data)
18
+ end
19
+
20
+ #
21
+ # Revision factory returns a validated versioned instance of the model class.
22
+ #
23
+ # @param [Hash] data
24
+ # The data to populate the instance.
25
+ #
26
+ def valid(data)
27
+ revision, data = revised(data)
28
+ basename = name.split('::').last
29
+ V[revision].const_get(basename).valid(data)
30
+ end
31
+
32
+ #
33
+ # When Model is inherited alias `#valid` to `#new` and setup an inherited
34
+ # callback for subclass which will do the same for `#new`.
35
+ #
36
+ # @param [Class] child
37
+ # The subclass inheriting Model.
38
+ #
39
+ def inherited(child)
40
+ def child.inherited(child)
41
+ class << child
42
+ alias :new :_new
43
+ alias :valid :_new
44
+ end
45
+ end
46
+ end
47
+ =end
48
+
49
+ #
50
+ #
51
+ #
52
+ def attr_reader(name)
53
+ module_eval %{
54
+ def #{name}
55
+ @data[:#{name}]
56
+ end
57
+ }
58
+ end
59
+
60
+ #
61
+ #
62
+ #
63
+ alias :attr :attr_reader
64
+
65
+ #
66
+ #
67
+ #
68
+ def attr_writer(name)
69
+ module_eval %{
70
+ def #{name}=(value)
71
+ @data[:#{name}] = value
72
+ end
73
+ }
74
+ end
75
+
76
+ end
77
+
78
+ #
79
+ # New instance.
80
+ #
81
+ # @param [Hash] data
82
+ # The metadata to populate the instance.
83
+ #
84
+ def initialize(data={})
85
+ initialize_attributes
86
+
87
+ merge!(data)
88
+ end
89
+
90
+ #
91
+ # Set default attributes.
92
+ #
93
+ def initialize_attributes
94
+ @data = {}
95
+ end
96
+
97
+ #
98
+ # Access metadata with hash-like getter method.
99
+ #
100
+ # @param [String, Symbol] key
101
+ # The name of the metadata field.
102
+ #
103
+ # @return [Object]
104
+ # The value associated with the key.
105
+ #
106
+ def [](key)
107
+ if respond_to?(key)
108
+ __send__("#{key}")
109
+ else
110
+ @data[key.to_sym]
111
+ end
112
+ end
113
+
114
+ #
115
+ # Assign metadata with hash-like setter method.
116
+ #
117
+ # @param [String, Symbol] key
118
+ # The name of the metadata field.
119
+ #
120
+ # @param [Object] value
121
+ # The value of the metadata field.
122
+ #
123
+ # @return [Object]
124
+ # The newly set value.
125
+ #
126
+ # @raise [ArgumentError]
127
+ # The key is not known.
128
+ #
129
+ def []=(key, value)
130
+ #unless self.class.attributes.include?(key)
131
+ # #error = ArgumentError.new("unknown attribute: #{key.inspect}")
132
+ # #error.extend Error
133
+ # #raise(error)
134
+ #end
135
+ if respond_to?("#{key}=")
136
+ __send__("#{key}=", value)
137
+ else
138
+ @data[key.to_sym] = value
139
+ end
140
+ end
141
+
142
+ #
143
+ # Merge data source into metadata.
144
+ #
145
+ # @param [Hash] data
146
+ # The data source which responds to #each like a Hash.
147
+ #
148
+ def merge!(data)
149
+ data.each do |key, value|
150
+ self[key] = value
151
+ end
152
+ end
153
+
154
+ #
155
+ #
156
+ #
157
+ def key?(name)
158
+ @data.key?(name.to_sym)
159
+ end
160
+
161
+ #
162
+ # Convert metadata to a Hash.
163
+ #
164
+ # @return [Hash{String => Object}]
165
+ #
166
+ def to_h
167
+ h = {}
168
+ #self.class.attributes.each do |key|
169
+ # h[key.to_s] = __send__(name)
170
+ #end
171
+ @data.each do |k, v|
172
+ h[k.to_s] = v if v
173
+ end
174
+ h
175
+ end
176
+
177
+ #
178
+ # Converts the Hash-like object to YAML.
179
+ #
180
+ # @param [Hash] opts
181
+ # Options used by YAML.
182
+ #
183
+ # TODO: Should we have #to_yaml in here?
184
+ def to_yaml(io={})
185
+ to_h.to_yaml(io)
186
+ end
187
+
188
+ #
189
+ # Models are open collections. Any arbitrary settings can be made
190
+ # in order to support non-specification indexing.
191
+ #
192
+ def method_missing(sym, *args, &blk)
193
+ super(sym, *args, &blk) if blk
194
+ name = sym.to_s
195
+ type = name[-1,1]
196
+
197
+ case type
198
+ when '='
199
+ value = (
200
+ case v = args.first
201
+ when String then String(v)
202
+ when Integer then Integer(v)
203
+ when Float then Float(v)
204
+ when Array then Array(v)
205
+ when Hash then Hash(v)
206
+ else
207
+ raise ValidationError, "custom metadata for `#{sym}' not simple type -- `#{value.class}'"
208
+ end
209
+ )
210
+ @data[name.chomp('=').to_sym] = value
211
+ when '!'
212
+ super(sym, *args, &blk)
213
+ when '?'
214
+ super(sym, *args, &blk)
215
+ else
216
+ key?(name) ? @data[name.to_sym] : nil #super(sym, *args, &blk)
217
+ end
218
+ end
219
+
220
+ private
221
+
222
+ #
223
+ def store(key, value)
224
+ @data[key.to_sym] = value
225
+ end
226
+
227
+ #
228
+ def validate(value, field, *types)
229
+ types.each do |type|
230
+ Valid.send(type, value, field)
231
+ end
232
+ return value
233
+ end
234
+
235
+ end
236
+
237
+ end
@@ -0,0 +1,43 @@
1
+ module Indexer
2
+
3
+ # Indexer's revision strategy is a "no project left behind" strategy.
4
+ # When using the API, is a specification is loaded that is outdated,
5
+ # it will be upconverted to the latest standard.
6
+ #
7
+ # If you absolutely *must* support an old revision then use an older
8
+ # version of Indexer, or work with the metadata manually (via YAML).
9
+ #
10
+ module Revision
11
+ extend self
12
+
13
+ #
14
+ # Revise the metadata to current standard.
15
+ #
16
+ def upconvert(data)
17
+ revision = data['revision'] || data[:revision]
18
+
19
+ revision = REVISION unless revision
20
+
21
+ if revision != REVISION
22
+ begin
23
+ require "revisions/r#{revision}"
24
+ __send__("r#{revision}", data)
25
+ rescue LoadError
26
+ raise ValidationError, "unknown revision #{revision}"
27
+ end
28
+ else
29
+ data
30
+ end
31
+ end
32
+
33
+ #
34
+ # Update R2013 specification to current specification.
35
+ #
36
+ def r2013(data)
37
+ data
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
@@ -0,0 +1,287 @@
1
+ module Indexer
2
+
3
+ # Validation functions.
4
+ module Valid
5
+ extend self
6
+
7
+ # Valid name regular expression.
8
+ TYPE = /^[A-Za-z][\/:A-Za-z0-9_-]*[A-Za-z0-9]$/
9
+
10
+ # Valid name regular expression.
11
+ NAME = /^[A-Za-z][A-Za-z0-9_-]*[A-Za-z0-9]$/
12
+
13
+ # Valid URL regular expression.
14
+ URL = /^(\w+)\:\/\/\S+$/
15
+
16
+ # Valid IRC channel.
17
+ IRC = /^\#\w+$/
18
+
19
+ # Regular expression for matching valid email addresses.
20
+ EMAIL = /\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i #/<.*?>/
21
+
22
+ # FIXME: Regular expression to limit date-time fields to ISO 8601 (Zulu).
23
+ DATE = /^\d\d\d\d-\d\d-\d\d(\s+\d\d:\d\d:\d\d)?$/
24
+
25
+ #
26
+ def type?(type)
27
+ TYPE =~ type
28
+ end
29
+
30
+ #
31
+ def type!(type, field=nil)
32
+ string!(type, field)
33
+ raise_invalid("type", type, field) unless type?(name)
34
+ return type
35
+ end
36
+
37
+ #
38
+ def name?(name)
39
+ NAME =~ name
40
+ end
41
+
42
+ #
43
+ def name!(name, field=nil)
44
+ string!(name, field)
45
+ raise_invalid("name", name, field) unless name?(name)
46
+ return name
47
+ end
48
+
49
+ #
50
+ def url?(url)
51
+ URL =~ url
52
+ end
53
+
54
+ #
55
+ def url!(url, field=nil)
56
+ raise_invalid("URL", url, field) unless url?(url)
57
+ return url
58
+ end
59
+
60
+ #
61
+ def irc?(irc)
62
+ IRC =~ irc
63
+ end
64
+
65
+ #
66
+ def irc!(irc, field=nil)
67
+ raise_invalid("IRC", irc, field) unless irc?(irc)
68
+ return irc
69
+ end
70
+
71
+ #
72
+ def uri?(uri)
73
+ url?(uri) || irc?(uri)
74
+ end
75
+
76
+ #
77
+ def uri!(uri, field=nil)
78
+ raise_invalid("URI", uri, field) unless uri?(uri)
79
+ return uri
80
+ end
81
+
82
+ #
83
+ def email?(email)
84
+ EMAIL =~ email
85
+ end
86
+
87
+ #
88
+ def email!(email, field=nil)
89
+ unless email?(email)
90
+ raise_invalid("email address", email, field)
91
+ end
92
+ return email
93
+ end
94
+
95
+ #
96
+ def oneline?(string)
97
+ string?(string) && !string.index("\n")
98
+ end
99
+
100
+ #
101
+ def oneline!(string, field=nil)
102
+ unless oneline?(string)
103
+ raise_invalid("one line string", string, field)
104
+ end
105
+ return string
106
+ end
107
+
108
+ #
109
+ def string?(string)
110
+ String === string
111
+ end
112
+
113
+ #
114
+ def string!(string, field=nil)
115
+ unless string?(string)
116
+ raise_invalid("string", string, field)
117
+ end
118
+ return string
119
+ end
120
+
121
+ # TODO: Should we bother with #to_ary?
122
+ def array?(array)
123
+ Array === array || array.respond_to?(:to_ary)
124
+ end
125
+
126
+ #
127
+ def array!(array, field=nil)
128
+ unless array?(array)
129
+ raise_invalid("array", array, field)
130
+ end
131
+ return array
132
+ end
133
+
134
+ #
135
+ def hash?(hash)
136
+ Hash === hash
137
+ end
138
+
139
+ #
140
+ def hash!(hash, field=nil)
141
+ unless hash?(hash)
142
+ raise_invalid("hash", hash, field)
143
+ end
144
+ return hash
145
+ end
146
+
147
+ #
148
+ def word?(word, field=nil)
149
+ return false unless string?(word)
150
+ return false if /^[A-Za-z]/ !~ word
151
+ return false if /[A-Za-z0-9]$/ !~ word
152
+ return false if /[^A-Za-z0-9_-]/ =~ word
153
+ return true
154
+ end
155
+
156
+ #
157
+ #--
158
+ # TODO: Do we really need to be so detailed about the error?
159
+ # Doing so prevent us from using #word? here.
160
+ #++
161
+ def word!(word, field=nil)
162
+ string!(word, field)
163
+ raise_invalid_message("#{field} must start with a letter -- #{word}") if /^[A-Za-z]/ !~ word
164
+ raise_invalid_message("#{field} must end with a letter or number -- #{word}") if /[A-Za-z0-9]$/ !~ word
165
+ raise_invalid_message("#{field} must be a word -- #{word}") if /[^A-Za-z0-9_-]/ =~ word
166
+ return word
167
+ end
168
+
169
+ #
170
+ #def integer_string?(integer)
171
+ # /^\d+$/ =~ integer
172
+ #end
173
+
174
+ # TODO: This is probably the wrong name for iso8601
175
+ def utc_date?(date)
176
+ return false unless string?(date)
177
+ return false unless DATE =~ date
178
+ begin
179
+ Time.parse(date)
180
+ rescue
181
+ return false
182
+ end
183
+ true
184
+ end
185
+
186
+ # TODO: This is probably the wrong name for iso8601
187
+ def utc_date!(date, field=nil)
188
+ unless utc_date?(date)
189
+ raise_invalid("ISO 8601 formatted date", date, field)
190
+ end
191
+ return date
192
+ end
193
+
194
+ # Four digit year.
195
+ def copyright_year?(year)
196
+ year = year.to_s
197
+ return true if /^\d\d\d\d$/ =~ year
198
+ return true if /^\d\d\d\d\-\d\d\d\d$/ =~ year
199
+ return true if /^\d\d\d\d(\,\d\d\d\d)+$/ =~ year
200
+ false
201
+ end
202
+
203
+ # Four digit year.
204
+ def copyright_year!(year, field=nil)
205
+ unless copyright_year?(year)
206
+ raise_invalid("copyright year", year, field)
207
+ end
208
+ return year
209
+ end
210
+
211
+ #
212
+ def version_string?(string)
213
+ return false unless string?(string)
214
+ return false if /^\D/ =~ string
215
+ return false if /[^.A-Za-z0-9]/ =~ string
216
+ return true
217
+ end
218
+
219
+ #
220
+ def version_string!(value, field=nil)
221
+ string!(value, field)
222
+ case value
223
+ when /^\D/
224
+ raise_invalid_message("#{field} must start with number - #{value.inspect}")
225
+ when /[^.A-Za-z0-9]/
226
+ raise_invalid_message("#{field} contains invalid characters - #{value.inspect}")
227
+ end
228
+ return value
229
+ end
230
+
231
+ # FIXME: better validation for path
232
+ def path?(path)
233
+ return false unless string?(path)
234
+ return true
235
+ end
236
+
237
+ #
238
+ def path!(path, field=nil)
239
+ unless path?(path)
240
+ raise_invalid("path", path, field)
241
+ end
242
+ return path
243
+ end
244
+
245
+ #
246
+ # TODO: Only allow double colons.
247
+ def constant?(name)
248
+ name = name.to_s if Symbol === name
249
+ /^[A-Z][A-Za-z0-9_:]*/ =~ name
250
+ end
251
+
252
+ #
253
+ def constant!(name, field=nil)
254
+ unless constant?(name)
255
+ raise_invalid("constant name", name, field)
256
+ end
257
+ return name
258
+ end
259
+
260
+ #
261
+ def raise_invalid(type, value, field=nil)
262
+ case field
263
+ when Exception
264
+ raise(field)
265
+ else
266
+ if field
267
+ message = "invalid %s for `%s' - %s" % [type, field, value.inspect]
268
+ else
269
+ message = "invalid %s - %s" % [type, value.inspect]
270
+ end
271
+ raise(ValidationError, message.strip)
272
+ end
273
+ end
274
+
275
+ #
276
+ def raise_invalid_message(message)
277
+ raise(ValidationError, message.strip)
278
+ end
279
+
280
+ end
281
+
282
+ # Use this error for all validation errors when reading a spec.
283
+ class ValidationError < ArgumentError
284
+ include Error
285
+ end
286
+
287
+ end