forked from aiwilliams/dataset
/
resolver.rb
110 lines (96 loc) · 3.44 KB
/
resolver.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
module Dataset
# An error raised when a dataset class cannot be found.
#
class DatasetNotFound < StandardError
end
# A dataset may be referenced as a class or as a name. A Dataset::Resolver
# will take an identifier, whether a class or a name, and return the class.
#
class Resolver
cattr_accessor :default
def identifiers
@identifiers ||= {}
end
# Attempt to convert a name to a constant. With the identifier :people, it
# will search for 'PeopleDataset', then 'People'.
#
def resolve(identifier)
return identifier if identifier.is_a?(Class)
if constant = identifiers[identifier]
return constant
end
constant = resolve_class(identifier)
unless constant
constant = resolve_identifier(identifier)
end
identifiers[identifier] = constant
end
protected
def resolve_identifier(identifier) # :nodoc:
constant = resolve_class(identifier)
unless constant
raise Dataset::DatasetNotFound, "Could not find a dataset '#{identifier.to_s.camelize}' or '#{identifier.to_s.camelize + suffix}'."
end
constant
end
def resolve_class(identifier)
names = [identifier.to_s.camelize, identifier.to_s.camelize + suffix]
constant = resolve_these(names.reverse)
if constant && constant.superclass != ::Dataset::Base
raise Dataset::DatasetNotFound, "Found a class '#{constant.name}', but it does not subclass 'Dataset::Base'."
end
constant
end
def resolve_these(names) # :nodoc:
names.each do |name|
constant = name.constantize rescue nil
return constant if constant && constant.is_a?(Class)
end
nil
end
def suffix # :nodoc:
@suffix ||= 'Dataset'
end
end
# Resolves a dataset by looking for a file in the provided directory path
# that has a name matching the identifier. Of course, should the identifier
# be a class already, it is simply returned.
#
class DirectoryResolver < Resolver
def initialize(*paths)
@paths = paths
end
def <<(path)
@paths << path
end
protected
def resolve_identifier(identifier) # :nodoc:
@paths.each do |path|
file = File.join(path, identifier.to_s)
unless File.exists?(file + '.rb')
file = file + '_' + file_suffix
next unless File.exists?(file + '.rb')
end
require file
begin
return super
rescue Dataset::DatasetNotFound => dnf
if dnf.message =~ /\ACould not find/
raise Dataset::DatasetNotFound, "Found the dataset file '#{file + '.rb'}', but it did not define #{dnf.message.sub('Could not find ', '')}"
else
raise Dataset::DatasetNotFound, "Found the dataset file '#{file + '.rb'}' and a class #{dnf.message.sub('Found a class ', '')}"
end
end
end
raise DatasetNotFound, "Could not find a dataset file in #{@paths.inspect} having the name '#{identifier}.rb' or '#{identifier}_#{file_suffix}.rb'."
end
def file_suffix # :nodoc:
@file_suffix ||= suffix.downcase
end
end
# The default resolver, used by the Dataset::Sessions that aren't given a
# different instance. You can set this to something else in your
# test/spec_helper.
#
Resolver.default = Resolver.new
end