queri 0.0.2

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