indexer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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