queri 0.0.2

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +96 -0
  7. data/Rakefile +7 -0
  8. data/lib/queri/realtime/agents.rb +80 -0
  9. data/lib/queri/realtime/queues.rb +64 -0
  10. data/lib/queri/realtime.rb +14 -0
  11. data/lib/queri/stats/agents_and_sessions/agent_availability.rb +76 -0
  12. data/lib/queri/stats/agents_and_sessions.rb +11 -0
  13. data/lib/queri/stats/answered_calls/agents_on_queue.rb +73 -0
  14. data/lib/queri/stats/answered_calls/all_calls.rb +81 -0
  15. data/lib/queri/stats/answered_calls.rb +11 -0
  16. data/lib/queri/stats/unanswered_calls/all_calls.rb +80 -0
  17. data/lib/queri/stats/unanswered_calls.rb +11 -0
  18. data/lib/queri/stats.rb +9 -0
  19. data/lib/queri/version.rb +3 -0
  20. data/lib/queri.rb +113 -0
  21. data/queri.gemspec +25 -0
  22. data/spec/queri_spec.rb +268 -0
  23. data/spec/realtime/agents_spec.rb +18 -0
  24. data/spec/realtime/queues_spec.rb +18 -0
  25. data/spec/realtime/realtime_spec.rb +10 -0
  26. data/spec/spec_helper.rb +11 -0
  27. data/spec/stats/agents_and_sessions/agent_availability_spec.rb +18 -0
  28. data/spec/stats/agents_and_sessions/agents_and_sessions_spec.rb +7 -0
  29. data/spec/stats/answered_calls/agents_on_queue_spec.rb +18 -0
  30. data/spec/stats/answered_calls/all_calls_spec.rb +18 -0
  31. data/spec/stats/answered_calls/answered_calls_spec.rb +7 -0
  32. data/spec/stats/stats_spec.rb +7 -0
  33. data/spec/stats/unanswered_calls/all_calls_spec.rb +18 -0
  34. data/spec/stats/unanswered_calls/unanswered_calls_spec.rb +7 -0
  35. data/spec/support/constantize.rb +27 -0
  36. data/spec/support/shared/shared_examples_for_agent_level_report_instance.rb +37 -0
  37. data/spec/support/shared/shared_examples_for_aggregate_report_instance.rb +37 -0
  38. data/spec/support/shared/shared_examples_for_private_constant.rb +9 -0
  39. data/spec/support/shared/shared_examples_for_queuemetrics_report.rb +25 -0
  40. data/spec/support/shared/shared_examples_for_realtime_report.rb +32 -0
  41. data/spec/support/shared/shared_examples_for_realtime_report_instance.rb +26 -0
  42. data/spec/support/shared/shared_examples_for_report_class.rb +13 -0
  43. data/spec/support/shared/shared_examples_for_report_method.rb +13 -0
  44. data/spec/support/shared/shared_examples_for_stats_report.rb +44 -0
  45. data/spec/support/time_helper.rb +17 -0
  46. metadata +154 -0
data/lib/queri.rb ADDED
@@ -0,0 +1,113 @@
1
+ require "queri/version"
2
+ require "yaml"
3
+ require "xmlrpc/client"
4
+
5
+ Dir[ File.join( File.dirname(__FILE__), "**", "*.rb" ) ].each {|f| require f}
6
+
7
+ module Queri
8
+ LOGFILE = ""
9
+ PERIOD = ""
10
+ AGENT_FILTER = ""
11
+
12
+ @config = {
13
+ :host => "127.0.0.1",
14
+ :path => "/path/to/queuemetrics.do",
15
+ :port => "8080",
16
+ :username => "username",
17
+ :password => "some_secret"
18
+ }
19
+
20
+ @valid_config_keys = @config.keys
21
+
22
+ @@server = nil
23
+
24
+ private_constant :LOGFILE, :PERIOD, :AGENT_FILTER
25
+
26
+ def self.config
27
+ @config
28
+ end
29
+
30
+ def self.configure opts={}
31
+ raise ArgumentError, "expected hash, got something else" if opts.class != Hash
32
+ unless opts.empty?
33
+ if (@valid_config_keys - opts.keys).any?
34
+ puts "WARNING: erroneous keys given to ::config. Acceptible keys include #{@valid_config_keys}"
35
+ end
36
+ if (opts.keys & @valid_config_keys).empty?
37
+ raise ArgumentError, "erroneous keys given to ::config. Acceptible keys include #{@valid_config_keys}"
38
+ else
39
+ opts.each {|k,v| @config[k.to_sym] = v if @valid_config_keys.include?(k.to_sym)}
40
+ @@server = XMLRPC::Client.new(host = @config[:host], path = @config[:path], port = @config[:port])
41
+ end
42
+ end
43
+ end
44
+
45
+ def self.configure_with path_to_yaml_file
46
+ raise ArgumentError, "expected path to yaml file as string, got something else" if path_to_yaml_file.class != String
47
+ begin
48
+ config = YAML.load(IO.read(path_to_yaml_file))
49
+ rescue Errno::ENOENT
50
+ raise LoadError, "the specified configuration file could not be found"
51
+ rescue Psych::SyntaxError
52
+ raise LoadError, "the specified configuration file contains invalid syntax"
53
+ end
54
+ configure(config)
55
+ @@server = XMLRPC::Client.new(host = @config[:host], path = @config[:path], port = @config[:port])
56
+ end
57
+
58
+ def self.send_request *args
59
+ raise LoadError, "client configs not set. Assign by passing a hash to Queri.configure or a yaml file path to Queri.configure_with" if (@config.nil? || @config.empty?)
60
+ request = Request.new(*args)
61
+ response = @@server.call(*request.parameters)
62
+ return response[request.report.class.xml_code]
63
+ end
64
+
65
+ class Request
66
+ attr_reader :report
67
+
68
+ def initialize *args
69
+ raise ArgumentError, "expected arguments: queues(Array), report(Stats or Realtime obj), period_start(Time or Nil), period_end(Time or Nil)" unless valid_args?(args)
70
+ @queues, @report, @period_start, @period_end = validate_args(args)
71
+ end
72
+
73
+ def parameters
74
+ if @period_start.nil? && @period_end.nil?
75
+ [@report.class.query_method, @queues.join("|"), Queri.config[:username], Queri.config[:password], LOGFILE, AGENT_FILTER, [@report.class.xml_code]]
76
+ else
77
+ [@report.class.query_method, @queues.join("|"), Queri.config[:username], Queri.config[:password], LOGFILE, PERIOD, @period_start, @period_end, AGENT_FILTER, [@report.class.xml_code]]
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def valid_args? args
84
+ valid = true
85
+ valid = false if args[0].class != Array
86
+ valid = false if args[1].class == NilClass
87
+ valid = false if (args[2].class != NilClass && args[2].class != Time)
88
+ valid = false if (args[3].class != NilClass && args[3].class != Time)
89
+ return valid
90
+ end
91
+
92
+ def validate_args args
93
+ raise ArgumentError, "queues array cannot be empty" if args[0].empty?
94
+ if args[1].class.name.include?("Queri::Stats::")
95
+ if (args[2].nil? || args[3].nil?)
96
+ raise ArgumentError, "Queri::Stats reports require non-nil period start and end times"
97
+ end
98
+ elsif args[1].class.name.include?("Queri::Realtime::")
99
+ unless (args[2].nil? && args[3].nil?)
100
+ raise ArgumentError, "Queri::Realtime reports require nil period start and end times"
101
+ end
102
+ end
103
+ args[2] = format_time(args[2])
104
+ args[3] = format_time(args[3])
105
+ return args
106
+ end
107
+
108
+ def format_time time
109
+ return time unless time.respond_to?(:strftime)
110
+ time.strftime("%Y-%m-%d.%H:%M:%S")
111
+ end
112
+ end
113
+ end
data/queri.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'queri/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "queri"
8
+ spec.version = Queri::VERSION
9
+ spec.authors = ["Brad Rice"]
10
+ spec.email = ["bradley_rice@mac.com"]
11
+ spec.description = %q{Queuemetrics Reporting Interface}
12
+ spec.summary = %q{Send requests to QM's XMLRPC client and receive human-readable responses}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.files -= ["spec/config.yml"]
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec", "~> 2.14.1"
25
+ end
@@ -0,0 +1,268 @@
1
+ require 'spec_helper'
2
+
3
+ describe Queri do
4
+ let(:queues) { ["10000"] }
5
+ let(:report) { double(Queri::Realtime::Queues) }
6
+
7
+ describe "constants" do
8
+ describe "for log file" do
9
+ let(:const_symbol) { :LOGFILE }
10
+ let(:constant) { Queri::LOGFILE }
11
+
12
+ it_behaves_like "a private constant"
13
+ end
14
+
15
+ describe "for period" do
16
+ let(:const_symbol) { :PERIOD }
17
+ let(:constant) { Queri::PERIOD }
18
+
19
+ it_behaves_like "a private constant"
20
+ end
21
+
22
+ describe "for agent filter" do
23
+ let(:const_symbol) { :AGENT_FILTER }
24
+ let(:constant) { Queri::AGENT_FILTER }
25
+
26
+ it_behaves_like "a private constant"
27
+ end
28
+ end
29
+
30
+ describe "::config" do
31
+ subject { Queri.config }
32
+
33
+ it "should respond" do
34
+ expect{ subject }.to_not raise_error
35
+ end
36
+
37
+ it "should return a Hash" do
38
+ subject.should be_a Hash
39
+ end
40
+ end
41
+
42
+ describe "::configure" do
43
+ let(:configs) { Hash[:password, "new_secret"] }
44
+
45
+ before do
46
+ @saved_configs = Queri.config.dup
47
+ end
48
+
49
+ subject { Queri.configure(configs) }
50
+
51
+ after do
52
+ Queri.configure(@saved_configs)
53
+ end
54
+
55
+ it "should respond" do
56
+ expect{ subject }.to_not raise_error
57
+ end
58
+
59
+ it "should require a Hash" do
60
+ expect{ Queri.configure(configs.to_a) }.to raise_error(ArgumentError)
61
+ expect{ Queri.configure(configs) }.to_not raise_error
62
+ end
63
+
64
+ it "should default to accept a Hash" do
65
+ expect{ Queri.configure }.to_not raise_error
66
+ end
67
+
68
+ it "should update project configurations" do
69
+ old_configs = Queri.config.dup
70
+ subject
71
+ Queri.config.should_not eq old_configs
72
+ end
73
+
74
+ context "given some erroneous keys" do
75
+ let(:configs) { Hash[:password, "new_secret", :foo, "bar"] }
76
+
77
+ it "should warn the user against bad data" do
78
+ Queri.should_receive(:puts).with("WARNING: erroneous keys given to ::config. Acceptible keys include #{Queri.instance_variable_get(:@valid_config_keys)}")
79
+ subject
80
+ end
81
+
82
+ it "should update project configurations" do
83
+ old_configs = Queri.config.dup
84
+ subject
85
+ Queri.config.should_not eq old_configs
86
+ end
87
+ end
88
+
89
+ context "given all erroneous keys" do
90
+ let(:configs) { Hash[:foo, "bar"] }
91
+
92
+ it "should raise ArgumentError" do
93
+ expect{ subject }.to raise_error(ArgumentError)
94
+ end
95
+
96
+ it "should not update project configurations" do
97
+ old_configs = Queri.config
98
+ begin
99
+ subject
100
+ rescue ArgumentError
101
+ end
102
+ Queri.config.should eq old_configs
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "::configure_with" do
108
+ let(:test_config_file) { File.join(File.dirname(__FILE__), 'test_config.yml') }
109
+
110
+ before do
111
+ @saved_configs = Queri.config.dup
112
+ File.open(test_config_file, 'w') do |f|
113
+ f << "---\n"
114
+ f << " :password: \"new_secret\""
115
+ end
116
+ end
117
+
118
+ subject { Queri.configure_with(test_config_file) }
119
+
120
+ after do
121
+ FileUtils.rm(test_config_file)
122
+ Queri.configure(@saved_configs)
123
+ end
124
+
125
+ it "should respond" do
126
+ expect{ subject }.to_not raise_error
127
+ end
128
+
129
+ it "should require a String" do
130
+ expect{ Queri.configure_with(Array.new) }.to raise_error(ArgumentError)
131
+ expect{ Queri.configure_with(test_config_file) }.to_not raise_error
132
+ end
133
+
134
+ it "should update project configurations" do
135
+ old_configs = Queri.config.dup
136
+ subject
137
+ Queri.config.should_not eq old_configs
138
+ end
139
+
140
+ context "given a bad file path" do
141
+ let(:bad_config_file) { File.join(File.dirname(__FILE__), 'nonexistant_config.yml') }
142
+
143
+ subject { Queri.configure_with(bad_config_file) }
144
+
145
+ it "should raise LoadError" do
146
+ expect{ subject }.to raise_error(LoadError)
147
+ end
148
+
149
+ it "should not update project configurations" do
150
+ old_configs = Queri.config.dup
151
+ begin
152
+ subject
153
+ rescue LoadError
154
+ end
155
+ Queri.config.should eq old_configs
156
+ end
157
+ end
158
+
159
+ context "given file with syntax error" do
160
+ before do
161
+ File.open(test_config_file, 'w') do |f|
162
+ f << "---"
163
+ f << "\t:password: bad_syntax"
164
+ end
165
+ end
166
+
167
+ it "should raise LoadError" do
168
+ expect{ subject }.to raise_error(LoadError)
169
+ end
170
+
171
+ it "should not update project configurations" do
172
+ old_configs = Queri.config.dup
173
+ begin
174
+ subject
175
+ rescue LoadError
176
+ end
177
+ Queri.config.should eq old_configs
178
+ end
179
+ end
180
+ end
181
+
182
+ describe "::send_request" do
183
+ before do
184
+ @client = double(XMLRPC::Client)
185
+ @client.stub(:call).and_return( {"xml_code" => [:keys, :for, :report]} )
186
+ end
187
+
188
+ subject { Queri.send_request(queues, report) }
189
+
190
+ context "without configurations" do
191
+ before do
192
+ @saved_configs = Queri.config.dup
193
+ Queri.instance_variable_set(:@config, {})
194
+ end
195
+
196
+ after do
197
+ Queri.configure(@saved_configs)
198
+ end
199
+
200
+ it "should raise LoadError" do
201
+ expect{ subject }.to raise_error(LoadError)
202
+ end
203
+ end
204
+
205
+ context "with configurations" do
206
+
207
+ before do
208
+ Queri.configure_with(File.join(File.dirname(__FILE__), 'config.yml'))
209
+ report.stub(:class).and_return( Queri::Realtime::Queues )
210
+ end
211
+
212
+ it "should respond" do
213
+ expect{ subject }.to_not raise_error
214
+ end
215
+
216
+ it "should call the XMLRPC client" do
217
+ xml_client = Queri.class_variable_get(:@@server)
218
+ Queri.class_variable_set(:@@server, @client)
219
+ subject
220
+ @client.should have_received(:call)
221
+ Queri.class_variable_set(:@@server, xml_client)
222
+ end
223
+
224
+ context "given no arguments" do
225
+ subject { Queri.send_request }
226
+
227
+ it "should raise ArgumentError" do
228
+ expect{ subject }.to raise_error(ArgumentError)
229
+ end
230
+ end
231
+
232
+ context "given an empty queues array" do
233
+ subject { Queri.send_request([], report) }
234
+
235
+ it "should raise ArgumentError" do
236
+ expect{ subject }.to raise_error(ArgumentError)
237
+ end
238
+ end
239
+
240
+ context "given a Stats report object" do
241
+ context "and period_start or period_end are nil" do
242
+ let(:report) { double(Queri::Stats::AnsweredCalls::AllCalls) }
243
+ let(:period_start) { Time.now - 10 }
244
+ let(:period_end) { nil }
245
+
246
+ subject { Queri.send_request(queues, report, period_start, period_end) }
247
+
248
+ it "should raise ArgumentError" do
249
+ expect{ subject }.to raise_error(ArgumentError)
250
+ end
251
+ end
252
+ end
253
+
254
+ context "given a Realtime report object" do
255
+ context "and period_start or period_end are not nil" do
256
+ let(:period_start) { Time.now - 10 }
257
+ let(:period_end) { nil }
258
+
259
+ subject { Queri.send_request(queues, report, period_start, period_end) }
260
+
261
+ it "should raise ArgumentError" do
262
+ expect{ subject }.to raise_error(ArgumentError)
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,18 @@
1
+ require File.join( File.dirname(__FILE__), '..', 'spec_helper' )
2
+
3
+ describe Queri::Realtime::Agents do
4
+ let(:report_class) { Queri::Realtime::Agents }
5
+ let(:xml_code) { report_class.xml_code }
6
+
7
+ it_behaves_like "a report class"
8
+
9
+ it_behaves_like "a report method"
10
+
11
+ it_behaves_like "a queuemetrics report"
12
+
13
+ it_behaves_like "a realtime report"
14
+
15
+ describe "An instance of", Queri::Realtime::Agents do
16
+ it_behaves_like "a realtime report instance"
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require File.join( File.dirname(__FILE__), '..', 'spec_helper' )
2
+
3
+ describe Queri::Realtime::Queues do
4
+ let(:report_class) { Queri::Realtime::Queues }
5
+ let(:xml_code) { report_class.xml_code }
6
+
7
+ it_behaves_like "a report class"
8
+
9
+ it_behaves_like "a report method"
10
+
11
+ it_behaves_like "a queuemetrics report"
12
+
13
+ it_behaves_like "a realtime report"
14
+
15
+ describe "An instance of", Queri::Realtime::Queues do
16
+ it_behaves_like "a realtime report instance"
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ require File.join( File.dirname(__FILE__), '..', 'spec_helper' )
2
+
3
+ describe Queri::Realtime do
4
+ let(:report_method) { Queri::Realtime }
5
+ let(:report_class) { Queri::Realtime }
6
+
7
+ it_behaves_like "a report method"
8
+
9
+ it_behaves_like "a report class"
10
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'queri'
4
+ Dir[File.join( File.dirname(__FILE__), 'support', '**', '*.rb' )].each {|f| require f}
5
+ #require File.join(File.dirname(__FILE__), '..', 'lib', 'queuemetrics.rb')
6
+
7
+ RSpec.configure do |config|
8
+ include TimeHelper
9
+ include Constantize
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ end
@@ -0,0 +1,18 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'spec_helper' )
2
+
3
+ describe Queri::Stats::AgentsAndSessions::AgentAvailability do
4
+ let(:report_class) { Queri::Stats::AgentsAndSessions::AgentAvailability }
5
+ let(:xml_code) { report_class.xml_code }
6
+
7
+ it_behaves_like "a report class"
8
+
9
+ it_behaves_like "a report method"
10
+
11
+ it_behaves_like "a queuemetrics report"
12
+
13
+ it_behaves_like "a stats report"
14
+
15
+ describe "An instance of", Queri::Stats::AgentsAndSessions::AgentAvailability do
16
+ it_behaves_like "an agent-level report instance"
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'spec_helper' )
2
+
3
+ describe Queri::Stats::AgentsAndSessions do
4
+ let(:report_class) { Queri::Stats::AgentsAndSessions }
5
+
6
+ it_behaves_like "a report class"
7
+ end
@@ -0,0 +1,18 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'spec_helper' )
2
+
3
+ describe Queri::Stats::AnsweredCalls::AgentsOnQueue do
4
+ let(:report_class) { Queri::Stats::AnsweredCalls::AgentsOnQueue }
5
+ let(:xml_code) { report_class.xml_code }
6
+
7
+ it_behaves_like "a report class"
8
+
9
+ it_behaves_like "a report method"
10
+
11
+ it_behaves_like "a queuemetrics report"
12
+
13
+ it_behaves_like "a stats report"
14
+
15
+ describe "An instance of", Queri::Stats::AnsweredCalls::AgentsOnQueue do
16
+ it_behaves_like "an agent-level report instance"
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'spec_helper' )
2
+
3
+ describe Queri::Stats::AnsweredCalls::AllCalls do
4
+ let(:report_class) { Queri::Stats::AnsweredCalls::AllCalls }
5
+ let(:xml_code) { report_class.xml_code }
6
+
7
+ it_behaves_like "a report class"
8
+
9
+ it_behaves_like "a report method"
10
+
11
+ it_behaves_like "a queuemetrics report"
12
+
13
+ it_behaves_like "a stats report"
14
+
15
+ describe "An instance of", Queri::Stats::AnsweredCalls::AllCalls do
16
+ it_behaves_like "an aggregate report instance"
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'spec_helper' )
2
+
3
+ describe Queri::Stats::AnsweredCalls do
4
+ let(:report_class) { Queri::Stats::AnsweredCalls }
5
+
6
+ it_behaves_like "a report class"
7
+ end
@@ -0,0 +1,7 @@
1
+ require File.join( File.dirname(__FILE__), '..', 'spec_helper' )
2
+
3
+ describe Queri::Stats do
4
+ let(:report_class) { Queri::Stats }
5
+
6
+ it_behaves_like "a report method"
7
+ end
@@ -0,0 +1,18 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'spec_helper' )
2
+
3
+ describe Queri::Stats::UnansweredCalls::AllCalls do
4
+ let(:report_class) { Queri::Stats::UnansweredCalls::AllCalls }
5
+ let(:xml_code) { report_class.xml_code }
6
+
7
+ it_behaves_like "a report class"
8
+
9
+ it_behaves_like "a report method"
10
+
11
+ it_behaves_like "a queuemetrics report"
12
+
13
+ it_behaves_like "a stats report"
14
+
15
+ describe "An instance of", Queri::Stats::UnansweredCalls::AllCalls do
16
+ it_behaves_like "an aggregate report instance"
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'spec_helper' )
2
+
3
+ describe Queri::Stats::UnansweredCalls do
4
+ let(:report_class) { Queri::Stats::UnansweredCalls }
5
+
6
+ it_behaves_like "a report class"
7
+ end
@@ -0,0 +1,27 @@
1
+ module Constantize
2
+ def constantize(camel_cased_word)
3
+ names = camel_cased_word.split('::')
4
+ names.shift if names.empty? || names.first.empty?
5
+
6
+ names.inject(Object) do |constant, name|
7
+ if constant == Object
8
+ constant.const_get(name)
9
+ else
10
+ candidate = constant.const_get(name)
11
+ next candidate if constant.const_defined?(name, false)
12
+ next candidate unless Object.const_defined?(name)
13
+
14
+ # Go down the ancestors to check it it's owned
15
+ # directly before we reach Object or the end of ancestors.
16
+ constant = constant.ancestors.inject do |const, ancestor|
17
+ break const if ancestor == Object
18
+ break ancestor if ancestor.const_defined?(name, false)
19
+ const
20
+ end
21
+
22
+ # owner is in Object, so raise
23
+ constant.const_get(name, false)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ shared_examples "an agent-level report instance" do
2
+ let(:queues) { ["10000"] }
3
+ let(:period_start) { Time.now - 7200 }
4
+ let(:period_end) { Time.now - 3600 }
5
+
6
+ before do
7
+ @client = double(XMLRPC::Client)
8
+ @client.stub(:call).and_return( {xml_code => [report_class.keys]} )
9
+ end
10
+
11
+ subject { report_class.new(queues, period_start, period_end) }
12
+
13
+ it { should_not respond_to :response= }
14
+
15
+ describe "#response" do
16
+ subject { report_class.new(queues, period_start, period_end).response }
17
+
18
+ it "should respond" do
19
+ XMLRPC::Client.stub(:new).and_return(@client)
20
+ expect{ subject }.to_not raise_error
21
+ end
22
+
23
+ it "should be an Array" do
24
+ XMLRPC::Client.stub(:new).and_return(@client)
25
+ subject.should be_a Array
26
+ end
27
+
28
+ context "for a period during which records are saved" do
29
+ let(:period_start) { most_recent_11_am_hour }
30
+ let(:period_end) { most_recent_12_pm_hour }
31
+
32
+ it "should contain Hashes" do
33
+ subject.each {|obj| obj.should be_a Hash}
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ shared_examples "an aggregate report instance" do
2
+ let(:queues) { ["10000"] }
3
+ let(:period_start) { Time.now - 7200 }
4
+ let(:period_end) { Time.now - 3600 }
5
+
6
+ before do
7
+ @client = double(XMLRPC::Client)
8
+ @client.stub(:call).and_return( {xml_code => []} )
9
+ end
10
+
11
+ subject { report_class.new(queues, period_start, period_end) }
12
+
13
+ it { should_not respond_to :response= }
14
+
15
+ describe "#response" do
16
+ subject { report_class.new(queues, period_start, period_end).response }
17
+
18
+ it "should respond" do
19
+ XMLRPC::Client.stub(:new).and_return(@client)
20
+ expect{ subject }.to_not raise_error
21
+ end
22
+
23
+ it "should be a Hash" do
24
+ XMLRPC::Client.stub(:new).and_return(@client)
25
+ subject.should be_a Hash
26
+ end
27
+
28
+ context "for a period during which records are saved" do
29
+ let(:period_start) { most_recent_11_am_hour }
30
+ let(:period_end) { most_recent_12_pm_hour }
31
+
32
+ it "should not be an empty Hash" do
33
+ subject.should_not be_empty
34
+ end
35
+ end
36
+ end
37
+ end