active-fedora 4.0.0.rc12 → 4.0.0.rc13
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/History.txt +2 -1
- data/lib/active_fedora/file_configurator.rb +20 -38
- data/lib/active_fedora/rdf_datastream.rb +8 -9
- data/lib/active_fedora/version.rb +1 -1
- data/spec/unit/file_configurator_spec.rb +16 -19
- data/spec/unit/ntriples_datastream_spec.rb +63 -64
- metadata +3 -3
data/Gemfile.lock
CHANGED
data/History.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
4.0.0.
|
1
|
+
4.0.0.rc13
|
2
2
|
Removed deprecations
|
3
3
|
* allowing :fedora level in fedora.yml
|
4
4
|
* automatic includes of Relationships and FileMethods is removed
|
@@ -20,6 +20,7 @@ HYDRA-758 Added ActiveFedora::Base.exists?
|
|
20
20
|
Fixes for Ruby 1.9 compatibility
|
21
21
|
HYDRA-741 Use fixture loader in repo:* rake tasks. Parameters changed to foxml and dir.
|
22
22
|
rubydora to 0.5.7
|
23
|
+
Lazily load solr config
|
23
24
|
|
24
25
|
3.3.2
|
25
26
|
HYDRA-745 No need to require :url be present on external datastreams
|
@@ -44,7 +44,7 @@ module ActiveFedora
|
|
44
44
|
# url: http://127.0.0.1:8983/fedora2
|
45
45
|
#
|
46
46
|
|
47
|
-
attr_accessor :
|
47
|
+
attr_accessor :config_env
|
48
48
|
attr_reader :config_options, :fedora_config_path, :solr_config_path
|
49
49
|
|
50
50
|
# The configuration hash that gets used by RSolr.connect
|
@@ -66,6 +66,10 @@ module ActiveFedora
|
|
66
66
|
load_configs
|
67
67
|
@fedora_config
|
68
68
|
end
|
69
|
+
def solr_config
|
70
|
+
load_configs
|
71
|
+
@solr_config
|
72
|
+
end
|
69
73
|
|
70
74
|
def config_reload!
|
71
75
|
ActiveSupport::Deprecation.warn("config_reload! is not supported")
|
@@ -80,6 +84,7 @@ module ActiveFedora
|
|
80
84
|
def reset!
|
81
85
|
@config_loaded = false #Force reload of configs
|
82
86
|
@fedora_config = {}
|
87
|
+
@solr_config = {}
|
83
88
|
@config_options = {}
|
84
89
|
@predicate_config_path = nil
|
85
90
|
end
|
@@ -92,48 +97,25 @@ module ActiveFedora
|
|
92
97
|
return if config_loaded?
|
93
98
|
@config_env = ActiveFedora.environment
|
94
99
|
|
100
|
+
load_fedora_config
|
101
|
+
load_solr_config
|
102
|
+
@config_loaded = true
|
103
|
+
end
|
104
|
+
|
105
|
+
def load_fedora_config
|
106
|
+
return @fedora_config unless @fedora_config.empty?
|
95
107
|
config_path = get_config_path(:fedora)
|
96
108
|
@fedora_config = YAML.load(File.open(config_path))[@config_env] || {}
|
97
|
-
load_config(:solr)
|
98
|
-
@config_loaded = true
|
99
109
|
end
|
100
110
|
|
101
|
-
def
|
102
|
-
|
103
|
-
|
104
|
-
self.instance_variable_set "@#{config_type}_config_path".to_sym, config_path
|
105
|
-
config_path = self.send("#{config_type}_config_path".to_sym)
|
111
|
+
def load_solr_config
|
112
|
+
return @solr_config unless @solr_config.empty?
|
113
|
+
@solr_config_path = get_config_path(:solr)
|
106
114
|
|
107
|
-
logger.info("
|
108
|
-
config = YAML.load(File.open(
|
109
|
-
raise "The #{@config_env.to_sym} environment settings were not found in the
|
110
|
-
|
111
|
-
config[:url] = determine_url(config_type,config)
|
112
|
-
|
113
|
-
self.instance_variable_set("@#{config_type}_config", config)
|
114
|
-
config
|
115
|
-
end
|
116
|
-
|
117
|
-
# Determines and sets the fedora_config[:url] or solr_config[:url]
|
118
|
-
# @param [String] config_type Either 'fedora' or 'solr'
|
119
|
-
# @param [Hash] config The config hash
|
120
|
-
# @return [String] the solr or fedora url
|
121
|
-
def determine_url(config_type,config)
|
122
|
-
c = config[ActiveFedora.environment.to_sym]
|
123
|
-
c.symbolize_keys!
|
124
|
-
if config_type == "fedora"
|
125
|
-
url = c[:url]
|
126
|
-
if url && !c[:user]
|
127
|
-
u = URI.parse url
|
128
|
-
c[:user] = u.user
|
129
|
-
c[:password] = u.password
|
130
|
-
c[:url] = "#{u.scheme}://#{u.host}:#{u.port}#{u.path}"
|
131
|
-
url = c[:url]
|
132
|
-
end
|
133
|
-
return url
|
134
|
-
else
|
135
|
-
return get_solr_url(c) if config_type == "solr"
|
136
|
-
end
|
115
|
+
logger.info("ActiveFedora: loading solr config from #{File.expand_path(@solr_config_path)}")
|
116
|
+
config = YAML.load(File.open(@solr_config_path)).symbolize_keys
|
117
|
+
raise "The #{@config_env.to_sym} environment settings were not found in the solr.yml config. If you already have a solr.yml file defined, make sure it defines settings for the #{@config_env} environment" unless config[@config_env.to_sym]
|
118
|
+
@solr_config = {:url=> get_solr_url(config[ActiveFedora.environment.to_sym].symbolize_keys)}
|
137
119
|
end
|
138
120
|
|
139
121
|
# Given the solr_config that's been loaded for this environment,
|
@@ -27,6 +27,11 @@ module ActiveFedora
|
|
27
27
|
def config
|
28
28
|
ActiveFedora::Predicates.predicate_config
|
29
29
|
end
|
30
|
+
def prefix(name)
|
31
|
+
name = name.to_s unless name.is_a? String
|
32
|
+
pre = self.to_s.sub(/RDFDatastream$/, '').underscore
|
33
|
+
return "#{pre}__#{name}".to_sym
|
34
|
+
end
|
30
35
|
def register_vocabularies(*vocabs)
|
31
36
|
@vocabularies = {}
|
32
37
|
vocabs.each do |v|
|
@@ -60,6 +65,7 @@ module ActiveFedora
|
|
60
65
|
# needed for AF::Predicates integration & drives all other
|
61
66
|
# functionality below
|
62
67
|
vocab = vocab.to_s
|
68
|
+
name = self.prefix(name)
|
63
69
|
if config
|
64
70
|
if config[:predicate_mapping].has_key? vocab
|
65
71
|
config[:predicate_mapping][vocab][name] = predicate
|
@@ -122,12 +128,6 @@ module ActiveFedora
|
|
122
128
|
end
|
123
129
|
|
124
130
|
end
|
125
|
-
# def empty?
|
126
|
-
# @values.empty?
|
127
|
-
# end
|
128
|
-
# def to_s
|
129
|
-
# @values.to_s
|
130
|
-
# end
|
131
131
|
end
|
132
132
|
|
133
133
|
include ModelMethods
|
@@ -156,7 +156,7 @@ module ActiveFedora
|
|
156
156
|
vocabs_list = self.class.vocabularies.select { |ns, v| v.__prefix__ == vocab_sym }
|
157
157
|
vocab = vocabs_list.first.first.to_s
|
158
158
|
vocab_hash = self.class.config[:predicate_mapping][vocab]
|
159
|
-
mapped_names = vocab_hash.select {|k, v| v.to_s == name.to_s}
|
159
|
+
mapped_names = vocab_hash.select { |k, v| name.to_s == v.to_s && k.to_s.split("__")[0] == self.class.prefix(name).to_s.split("__")[0]}
|
160
160
|
name = mapped_names.first.first.to_s
|
161
161
|
next unless vocab_hash.has_key?("#{name}type".to_sym) and vocab_hash.has_key?("#{name}behaviors".to_sym)
|
162
162
|
type = vocab_hash["#{name}type".to_sym]
|
@@ -184,7 +184,7 @@ module ActiveFedora
|
|
184
184
|
|
185
185
|
# @param [Symbol, RDF::URI] predicate the predicate to insert into the graph
|
186
186
|
def find_predicate(predicate)
|
187
|
-
predicate = predicate
|
187
|
+
predicate = self.class.prefix(predicate) unless predicate.kind_of? RDF::URI
|
188
188
|
result = ActiveFedora::Predicates.find_predicate(predicate)
|
189
189
|
RDF::URI(result.reverse.join)
|
190
190
|
end
|
@@ -212,7 +212,6 @@ module ActiveFedora
|
|
212
212
|
ensure_loaded
|
213
213
|
predicate = find_predicate(predicate) unless predicate.kind_of? RDF::URI
|
214
214
|
graph.delete(predicate)
|
215
|
-
|
216
215
|
args = [args] unless args.respond_to? :each
|
217
216
|
args.each do |arg|
|
218
217
|
graph.add(predicate, arg, true) unless arg.empty?
|
@@ -35,12 +35,23 @@ describe ActiveFedora::FileConfigurator do
|
|
35
35
|
subject.fedora_config.should == {"url"=>"http://127.0.0.1:8983/fedora-test", "password"=>"fedoraAdmin", "user"=>"fedoraAdmin"}
|
36
36
|
end
|
37
37
|
end
|
38
|
+
describe "#solr_config" do
|
39
|
+
before do
|
40
|
+
subject.reset!
|
41
|
+
end
|
42
|
+
it "should trigger configuration to load" do
|
43
|
+
subject.solr_config.should == {:url=>"http://localhost:8983/solr/test"}
|
44
|
+
end
|
45
|
+
end
|
38
46
|
|
39
47
|
describe "#reset!" do
|
40
48
|
before { subject.reset! }
|
41
49
|
it "should clear @fedora_config" do
|
42
50
|
subject.instance_variable_get(:@fedora_config).should == {}
|
43
51
|
end
|
52
|
+
it "should clear @solr_config" do
|
53
|
+
subject.instance_variable_get(:@solr_config).should == {}
|
54
|
+
end
|
44
55
|
it "should clear @config_options" do
|
45
56
|
subject.instance_variable_get(:@config_options).should == {}
|
46
57
|
end
|
@@ -127,25 +138,13 @@ describe ActiveFedora::FileConfigurator do
|
|
127
138
|
|
128
139
|
end
|
129
140
|
|
130
|
-
describe "
|
131
|
-
it "should support config['environment']['url'] if config_type is fedora" do
|
132
|
-
config = {:test=> {:url=>"http://fedoraAdmin:fedorAdmin@localhost:8983/fedora"}}
|
133
|
-
subject.determine_url("fedora",config).should eql("http://localhost:8983/fedora")
|
134
|
-
end
|
135
|
-
|
136
|
-
it "should call #get_solr_url to determine the solr url if config_type is solr" do
|
137
|
-
config = {:test=>{:default => "http://default.solr:8983"}}
|
138
|
-
subject.expects(:get_solr_url).with(config[:test]).returns("http://default.solr:8983")
|
139
|
-
subject.determine_url("solr",config).should eql("http://default.solr:8983")
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
describe "load_config" do
|
141
|
+
describe "load_solr_config" do
|
144
142
|
it "should load the file specified in solr_config_path" do
|
145
|
-
subject.expects(:
|
143
|
+
subject.expects(:get_config_path).with(:solr).returns("/path/to/solr.yml")
|
144
|
+
subject.expects(:load_fedora_config)
|
146
145
|
File.expects(:open).with("/path/to/solr.yml").returns("development:\n default:\n url: http://devsolr:8983\ntest:\n default:\n url: http://mysolr:8080")
|
147
|
-
subject.
|
148
|
-
subject.solr_config.should
|
146
|
+
subject.load_solr_config.should == {:url=>"http://mysolr:8080"}
|
147
|
+
subject.solr_config.should == {:url=>"http://mysolr:8080"}
|
149
148
|
end
|
150
149
|
end
|
151
150
|
|
@@ -155,8 +154,6 @@ describe ActiveFedora::FileConfigurator do
|
|
155
154
|
subject.instance_variable_set :@config_loaded, nil
|
156
155
|
end
|
157
156
|
it "should load the fedora and solr configs" do
|
158
|
-
#ActiveFedora.expects(:load_config).with(:fedora)
|
159
|
-
subject.expects(:load_config).with(:solr)
|
160
157
|
subject.config_loaded?.should be_false
|
161
158
|
subject.load_configs
|
162
159
|
subject.config_loaded?.should be_true
|
@@ -112,12 +112,12 @@ describe ActiveFedora::NtriplesRDFDatastream do
|
|
112
112
|
@subject.content = File.new('spec/fixtures/solr_rdf_descMetadata.nt').read
|
113
113
|
@subject.stubs(:pid => 'test:1')
|
114
114
|
@subject.stubs(:new? => false)
|
115
|
-
@sample_fields = {:
|
116
|
-
:
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
120
|
-
:
|
115
|
+
@sample_fields = {:my_datastream__publisher => {:values => ["publisher1"], :type => :string, :behaviors => [:facetable, :sortable, :searchable, :displayable]},
|
116
|
+
:my_datastream__based_near => {:values => ["coverage1", "coverage2"], :type => :text, :behaviors => [:displayable, :facetable, :searchable]},
|
117
|
+
:my_datastream__created => {:values => "fake-date", :type => :date, :behaviors => [:sortable, :displayable]},
|
118
|
+
:my_datastream__title => {:values => "fake-title", :type => :text, :behaviors => [:searchable, :displayable, :sortable]},
|
119
|
+
:my_datastream__related_url => {:values => "http://example.org/", :type =>:string, :behaviors => [:searchable]},
|
120
|
+
:my_datastream__empty_field => {:values => [], :type => :string, :behaviors => [:searchable]}
|
121
121
|
}
|
122
122
|
end
|
123
123
|
after(:all) do
|
@@ -139,35 +139,34 @@ describe ActiveFedora::NtriplesRDFDatastream do
|
|
139
139
|
it "should iterate through @fields hash" do
|
140
140
|
@subject.expects(:fields).returns(@sample_fields)
|
141
141
|
solr_doc = @subject.to_solr
|
142
|
-
solr_doc["
|
143
|
-
solr_doc["
|
144
|
-
solr_doc["
|
145
|
-
solr_doc["
|
146
|
-
solr_doc["
|
147
|
-
solr_doc["
|
148
|
-
solr_doc["
|
149
|
-
solr_doc["
|
150
|
-
solr_doc["
|
151
|
-
solr_doc["
|
152
|
-
solr_doc["
|
153
|
-
solr_doc["
|
154
|
-
solr_doc["
|
155
|
-
solr_doc["
|
156
|
-
end
|
157
|
-
it "should allow multiple values for a single field"
|
142
|
+
solr_doc["my_datastream__publisher_t"].should == ["publisher1"]
|
143
|
+
solr_doc["my_datastream__publisher_sort"].should == ["publisher1"]
|
144
|
+
solr_doc["my_datastream__publisher_display"].should == ["publisher1"]
|
145
|
+
solr_doc["my_datastream__publisher_facet"].should == ["publisher1"]
|
146
|
+
solr_doc["my_datastream__based_near_t"].sort.should == ["coverage1", "coverage2"]
|
147
|
+
solr_doc["my_datastream__based_near_display"].sort.should == ["coverage1", "coverage2"]
|
148
|
+
solr_doc["my_datastream__based_near_facet"].sort.should == ["coverage1", "coverage2"]
|
149
|
+
solr_doc["my_datastream__created_sort"].should == ["fake-date"]
|
150
|
+
solr_doc["my_datastream__created_display"].should == ["fake-date"]
|
151
|
+
solr_doc["my_datastream__title_t"].should == ["fake-title"]
|
152
|
+
solr_doc["my_datastream__title_sort"].should == ["fake-title"]
|
153
|
+
solr_doc["my_datastream__title_display"].should == ["fake-title"]
|
154
|
+
solr_doc["my_datastream__related_url_t"].should == ["http://example.org/"]
|
155
|
+
solr_doc["my_datastream__empty_field_t"].should be_nil
|
156
|
+
end
|
158
157
|
it 'should append create keys in format field_name + _ + field_type' do
|
159
158
|
@subject.stubs(:fields).returns(@sample_fields)
|
160
159
|
|
161
160
|
#should have these
|
162
|
-
@subject.to_solr["
|
163
|
-
@subject.to_solr["
|
164
|
-
@subject.to_solr["
|
165
|
-
@subject.to_solr["
|
161
|
+
@subject.to_solr["my_datastream__publisher_t"].should_not be_nil
|
162
|
+
@subject.to_solr["my_datastream__based_near_t"].should_not be_nil
|
163
|
+
@subject.to_solr["my_datastream__title_t"].should_not be_nil
|
164
|
+
@subject.to_solr["my_datastream__related_url_t"].should_not be_nil
|
166
165
|
|
167
166
|
#should NOT have these
|
168
|
-
@subject.to_solr["
|
169
|
-
@subject.to_solr["
|
170
|
-
@subject.to_solr["
|
167
|
+
@subject.to_solr["my_datastream__narrator"].should be_nil
|
168
|
+
@subject.to_solr["my_datastream__empty_field"].should be_nil
|
169
|
+
@subject.to_solr["my_datastream__creator"].should be_nil
|
171
170
|
end
|
172
171
|
it "should use Solr mappings to generate field names" do
|
173
172
|
ActiveFedora::SolrService.load_mappings(File.join(File.dirname(__FILE__), "..", "..", "config", "solr_mappings_af_0.1.yml"))
|
@@ -175,15 +174,15 @@ describe ActiveFedora::NtriplesRDFDatastream do
|
|
175
174
|
solr_doc = @subject.to_solr
|
176
175
|
|
177
176
|
#should have these
|
178
|
-
solr_doc["
|
179
|
-
solr_doc["
|
180
|
-
solr_doc["
|
181
|
-
solr_doc["
|
177
|
+
solr_doc["my_datastream__publisher_field"].should == ["publisher1"]
|
178
|
+
solr_doc["my_datastream__based_near_field"].sort.should == ["coverage1", "coverage2"]
|
179
|
+
solr_doc["my_datastream__created_display"].should == ["fake-date"]
|
180
|
+
solr_doc["my_datastream__title_field"].should == ["fake-title"]
|
182
181
|
|
183
|
-
solr_doc["
|
184
|
-
solr_doc["
|
185
|
-
solr_doc["
|
186
|
-
solr_doc["
|
182
|
+
solr_doc["my_datastream__title_t"].should be_nil
|
183
|
+
solr_doc["my_datastream__publisher_t"].should be_nil
|
184
|
+
solr_doc["my_datastream__based_near_t"].should be_nil
|
185
|
+
solr_doc["my_datastream__created_dt"].should be_nil
|
187
186
|
|
188
187
|
# Reload default mappings
|
189
188
|
ActiveFedora::SolrService.load_mappings
|
@@ -204,22 +203,22 @@ describe ActiveFedora::NtriplesRDFDatastream do
|
|
204
203
|
@obj.fields.keys.count.should == 5
|
205
204
|
end
|
206
205
|
it "should return the right fields" do
|
207
|
-
@obj.fields.keys.should include(:
|
208
|
-
@obj.fields.keys.should include(:
|
209
|
-
@obj.fields.keys.should include(:
|
210
|
-
@obj.fields.keys.should include(:
|
211
|
-
@obj.fields.keys.should include(:
|
206
|
+
@obj.fields.keys.should include(:my_datastream__related_url)
|
207
|
+
@obj.fields.keys.should include(:my_datastream__publisher)
|
208
|
+
@obj.fields.keys.should include(:my_datastream__created)
|
209
|
+
@obj.fields.keys.should include(:my_datastream__title)
|
210
|
+
@obj.fields.keys.should include(:my_datastream__based_near)
|
212
211
|
end
|
213
212
|
it "should return the right values" do
|
214
|
-
@obj.fields[:
|
213
|
+
@obj.fields[:my_datastream__related_url][:values].should == ["http://example.org/blogtastic/"]
|
215
214
|
end
|
216
215
|
it "should return the right type information" do
|
217
|
-
@obj.fields[:
|
216
|
+
@obj.fields[:my_datastream__created][:type].should == :date
|
218
217
|
end
|
219
218
|
it "should return multi-value fields as expected" do
|
220
|
-
@obj.fields[:
|
221
|
-
@obj.fields[:
|
222
|
-
@obj.fields[:
|
219
|
+
@obj.fields[:my_datastream__based_near][:values].count.should == 2
|
220
|
+
@obj.fields[:my_datastream__based_near][:values].should include("Tacoma, WA")
|
221
|
+
@obj.fields[:my_datastream__based_near][:values].should include("Renton, WA")
|
223
222
|
end
|
224
223
|
end
|
225
224
|
describe ".to_solr()" do
|
@@ -227,27 +226,27 @@ describe ActiveFedora::NtriplesRDFDatastream do
|
|
227
226
|
@obj.to_solr.keys.count.should == 13
|
228
227
|
end
|
229
228
|
it "should return the right fields" do
|
230
|
-
@obj.to_solr.keys.should include("
|
231
|
-
@obj.to_solr.keys.should include("
|
232
|
-
@obj.to_solr.keys.should include("
|
233
|
-
@obj.to_solr.keys.should include("
|
234
|
-
@obj.to_solr.keys.should include("
|
235
|
-
@obj.to_solr.keys.should include("
|
236
|
-
@obj.to_solr.keys.should include("
|
237
|
-
@obj.to_solr.keys.should include("
|
238
|
-
@obj.to_solr.keys.should include("
|
239
|
-
@obj.to_solr.keys.should include("
|
240
|
-
@obj.to_solr.keys.should include("
|
241
|
-
@obj.to_solr.keys.should include("
|
242
|
-
@obj.to_solr.keys.should include("
|
229
|
+
@obj.to_solr.keys.should include("my_datastream__related_url_t")
|
230
|
+
@obj.to_solr.keys.should include("my_datastream__publisher_t")
|
231
|
+
@obj.to_solr.keys.should include("my_datastream__publisher_sort")
|
232
|
+
@obj.to_solr.keys.should include("my_datastream__publisher_display")
|
233
|
+
@obj.to_solr.keys.should include("my_datastream__publisher_facet")
|
234
|
+
@obj.to_solr.keys.should include("my_datastream__created_sort")
|
235
|
+
@obj.to_solr.keys.should include("my_datastream__created_display")
|
236
|
+
@obj.to_solr.keys.should include("my_datastream__title_t")
|
237
|
+
@obj.to_solr.keys.should include("my_datastream__title_sort")
|
238
|
+
@obj.to_solr.keys.should include("my_datastream__title_display")
|
239
|
+
@obj.to_solr.keys.should include("my_datastream__based_near_t")
|
240
|
+
@obj.to_solr.keys.should include("my_datastream__based_near_facet")
|
241
|
+
@obj.to_solr.keys.should include("my_datastream__based_near_display")
|
243
242
|
end
|
244
243
|
it "should return the right values" do
|
245
|
-
@obj.to_solr["
|
244
|
+
@obj.to_solr["my_datastream__related_url_t"].should == ["http://example.org/blogtastic/"]
|
246
245
|
end
|
247
246
|
it "should return multi-value fields as expected" do
|
248
|
-
@obj.to_solr["
|
249
|
-
@obj.to_solr["
|
250
|
-
@obj.to_solr["
|
247
|
+
@obj.to_solr["my_datastream__based_near_t"].count.should == 2
|
248
|
+
@obj.to_solr["my_datastream__based_near_t"].should include("Tacoma, WA")
|
249
|
+
@obj.to_solr["my_datastream__based_near_t"].should include("Renton, WA")
|
251
250
|
end
|
252
251
|
end
|
253
252
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active-fedora
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15424143
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 4
|
8
8
|
- 0
|
9
9
|
- 0
|
10
10
|
- rc
|
11
|
-
-
|
12
|
-
version: 4.0.0.
|
11
|
+
- 13
|
12
|
+
version: 4.0.0.rc13
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Matt Zumwalt
|