vnstat-ruby 1.0.0

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 (60) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +14 -0
  3. data/.document +5 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +1171 -0
  6. data/.travis.yml +14 -0
  7. data/Gemfile +11 -0
  8. data/Gemfile.lock +84 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +81 -0
  11. data/Rakefile +28 -0
  12. data/VERSION +1 -0
  13. data/lib/vnstat-ruby.rb +1 -0
  14. data/lib/vnstat.rb +65 -0
  15. data/lib/vnstat/configuration.rb +35 -0
  16. data/lib/vnstat/document.rb +56 -0
  17. data/lib/vnstat/error.rb +6 -0
  18. data/lib/vnstat/errors/executable_not_found.rb +7 -0
  19. data/lib/vnstat/errors/unknown_interface.rb +12 -0
  20. data/lib/vnstat/interface.rb +161 -0
  21. data/lib/vnstat/interface_collection.rb +106 -0
  22. data/lib/vnstat/parser.rb +54 -0
  23. data/lib/vnstat/result.rb +55 -0
  24. data/lib/vnstat/result/date_delegation.rb +31 -0
  25. data/lib/vnstat/result/day.rb +45 -0
  26. data/lib/vnstat/result/hour.rb +56 -0
  27. data/lib/vnstat/result/minute.rb +62 -0
  28. data/lib/vnstat/result/month.rb +47 -0
  29. data/lib/vnstat/result/time_comparable.rb +16 -0
  30. data/lib/vnstat/system_call.rb +90 -0
  31. data/lib/vnstat/traffic.rb +11 -0
  32. data/lib/vnstat/traffic/base.rb +46 -0
  33. data/lib/vnstat/traffic/daily.rb +40 -0
  34. data/lib/vnstat/traffic/hourly.rb +44 -0
  35. data/lib/vnstat/traffic/monthly.rb +27 -0
  36. data/lib/vnstat/traffic/tops.rb +39 -0
  37. data/lib/vnstat/utils.rb +68 -0
  38. data/spec/lib/vnstat/configuration_spec.rb +72 -0
  39. data/spec/lib/vnstat/document_spec.rb +54 -0
  40. data/spec/lib/vnstat/errors/executable_not_found_spec.rb +5 -0
  41. data/spec/lib/vnstat/errors/unknown_interface_spec.rb +5 -0
  42. data/spec/lib/vnstat/interface_collection_spec.rb +124 -0
  43. data/spec/lib/vnstat/interface_spec.rb +213 -0
  44. data/spec/lib/vnstat/result/day_spec.rb +39 -0
  45. data/spec/lib/vnstat/result/hour_spec.rb +43 -0
  46. data/spec/lib/vnstat/result/minute_spec.rb +52 -0
  47. data/spec/lib/vnstat/result/month_spec.rb +39 -0
  48. data/spec/lib/vnstat/result_spec.rb +86 -0
  49. data/spec/lib/vnstat/system_call_spec.rb +209 -0
  50. data/spec/lib/vnstat/traffic/daily_spec.rb +109 -0
  51. data/spec/lib/vnstat/traffic/hourly_spec.rb +153 -0
  52. data/spec/lib/vnstat/traffic/monthly_spec.rb +46 -0
  53. data/spec/lib/vnstat/traffic/tops_spec.rb +48 -0
  54. data/spec/lib/vnstat/utils_spec.rb +128 -0
  55. data/spec/lib/vnstat_spec.rb +61 -0
  56. data/spec/spec_helper.rb +98 -0
  57. data/spec/support/shared_examples/shared_examples_for_date_delegation.rb +19 -0
  58. data/spec/support/shared_examples/shared_examples_for_traffic_collection.rb +19 -0
  59. data/vnstat-ruby.gemspec +113 -0
  60. metadata +187 -0
@@ -0,0 +1,39 @@
1
+ module Vnstat
2
+ module Traffic
3
+ ##
4
+ # A class representing a collection of tracked tops.
5
+ class Tops < Base
6
+ ##
7
+ # Iterates over all results in the collection.
8
+ #
9
+ # @overload each
10
+ # @return [Enumerator]
11
+ #
12
+ # @overload each(&block)
13
+ # @yield [result]
14
+ # @yieldparam [Result::Minute] result
15
+ # @return [Tops]
16
+ def each(&block)
17
+ to_a.each(&block)
18
+ end
19
+
20
+ ##
21
+ # Fetches a single {Result::Minute} from the collection.
22
+ #
23
+ # @param [Integer] index The index of the entry in the collection.
24
+ # @return [Result::Minute]
25
+ def [](index)
26
+ to_a[index]
27
+ end
28
+
29
+ ##
30
+ # @return [Array<Result::Minute>]
31
+ def to_a
32
+ elements = traffic_data.xpath('tops/top')
33
+ elements.map do |element|
34
+ Result::Minute.extract_from_xml_element(element)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,68 @@
1
+ module Vnstat
2
+ ##
3
+ # A module containing several utility methods.
4
+ module Utils
5
+ module_function
6
+
7
+ ##
8
+ # Initiates a system call with the given arguments.
9
+ #
10
+ # @param [Array<String>] args The arguments for the system call.
11
+ #
12
+ # @overload system_call(*args)
13
+ # @return [String] The output of STDOUT or STDERR, depending of the exit
14
+ # status of the called command.
15
+ #
16
+ # @overload system_call(*args, &block)
17
+ # @yield [error_result] A block yielded when the called command exited
18
+ # unsuccessfully.
19
+ # @yieldparam [String] error_result The STDERR output.
20
+ # @return [Object] The value the called block returns.
21
+ def system_call(*args)
22
+ system_call = SystemCall.call(*args)
23
+ return system_call.success_result if system_call.success?
24
+ return yield(system_call.error_result) if block_given?
25
+ system_call.error_result
26
+ end
27
+
28
+ ##
29
+ # Initiates a system call with the given arguments and returning whether the
30
+ # command has been executed successfully.
31
+ #
32
+ # @param [Array<String>] args The arguments for the system call.
33
+ # @return [true, false] Indicates whether the command has been executed
34
+ # successfully (true), or not (false).
35
+ def system_call_returning_status(*args)
36
+ SystemCall.call(*args).success?
37
+ end
38
+
39
+ ##
40
+ # Calls the vnstat CLI with the given arguments.
41
+ #
42
+ # @param [Array<String>] args The arguments for the system call.
43
+ #
44
+ # @overload call_executable(*args)
45
+ # @return [String] The output of STDOUT or STDERR, depending of the exit
46
+ # status of the called command.
47
+ #
48
+ # @overload call_executable(*args, &block)
49
+ # @yield [error_result] A block yielded when the called command exited
50
+ # unsuccessfully.
51
+ # @yieldparam [String] error_result The STDERR output.
52
+ # @return [Object] The value the called block returns.
53
+ def call_executable(*args, &block)
54
+ system_call(Vnstat.config.executable_path, *args, &block)
55
+ end
56
+
57
+ ##
58
+ # Calls the vnstat CLI with the given arguments and returning whether the
59
+ # command has been executed successfully.
60
+ #
61
+ # @param [Array<String>] args The arguments for the system call.
62
+ # @return [true, false] Indicates whether the command has been executed
63
+ # successfully (true), or not (false).
64
+ def call_executable_returning_status(*args)
65
+ system_call_returning_status(Vnstat.config.executable_path, *args)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,72 @@
1
+ describe Vnstat::Configuration do
2
+ describe '#reset' do
3
+ before :each do
4
+ subject.executable_path = '/test/path/vnstat'
5
+ end
6
+
7
+ it 'returns self' do
8
+ expect(subject.reset).to eq subject
9
+ end
10
+
11
+ it 'sets #executable_path to nil' do
12
+ expect { subject.reset }
13
+ .to change { subject.instance_variable_get(:@executable_path) }
14
+ .from('/test/path/vnstat').to(nil)
15
+ end
16
+ end
17
+
18
+ describe '#executable_path' do
19
+ context 'when configured' do
20
+ before :each do
21
+ subject.executable_path = '/test/path/vnstat'
22
+ end
23
+
24
+ it 'returns configured path' do
25
+ expect(subject.executable_path).to eq '/test/path/vnstat'
26
+ end
27
+ end
28
+
29
+ context 'when not configured' do
30
+ it 'calls Utils.system_call to invoke `which vnstat`' do
31
+ expect(Vnstat::Utils).to receive(:system_call)
32
+ .with('which', 'vnstat')
33
+ .and_return('/test/other_path/vnstat')
34
+
35
+ subject.executable_path
36
+ end
37
+
38
+ context 'with path detected by `which vnstat`' do
39
+ before :each do
40
+ allow(Vnstat::Utils).to receive(:system_call)
41
+ .with('which', 'vnstat')
42
+ .and_return('/test/other_path/vnstat')
43
+ end
44
+
45
+ it 'returns detected path' do
46
+ expect(subject.executable_path).to eq '/test/other_path/vnstat'
47
+ end
48
+ end
49
+
50
+ context 'with path not detectable by `which vnstat`' do
51
+ before :each do
52
+ allow(Vnstat::Utils).to receive(:system_call)
53
+ .with('which', 'vnstat').and_yield
54
+ end
55
+
56
+ it 'raises Vnstat::ExecutableNotFound' do
57
+ expect { subject.executable_path }
58
+ .to raise_error(Vnstat::ExecutableNotFound,
59
+ 'Unable to locate vnstat executable')
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#executable_path=' do
66
+ it 'sets instance variable @executable_path' do
67
+ expect { subject.executable_path = '/test/path/vnstat' }
68
+ .to change { subject.instance_variable_get(:@executable_path) }
69
+ .from(nil).to('/test/path/vnstat')
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,54 @@
1
+ describe Vnstat::Document do
2
+ subject do
3
+ described_class.new('<vnstat version="1.23" xmlversion="1"></vnstat>')
4
+ end
5
+
6
+ describe '#initialize' do
7
+ it 'invokes #data= forwarding the first argument' do
8
+ expect_any_instance_of(described_class)
9
+ .to receive(:data=).with('test')
10
+
11
+ described_class.new('test')
12
+ end
13
+ end
14
+
15
+ describe '#data=' do
16
+ context 'when setting nil' do
17
+ subject { described_class.new('<vnstat />') }
18
+
19
+ it 'raises ArgumentError' do
20
+ expect { subject.data = nil }
21
+ .to raise_error(ArgumentError, 'No document data specified')
22
+ end
23
+ end
24
+
25
+ context 'when setting a String' do
26
+ subject { described_class.new('<old />') }
27
+
28
+ before :each do
29
+ end
30
+
31
+ it 'changes #data' do
32
+ expect { subject.data = '<vnstat />' }.to change { subject.data }
33
+ end
34
+
35
+ it 'stores the XML fragment in #data' do
36
+ subject.data = '<vnstat />'
37
+
38
+ expect(subject.data).to be_a Nokogiri::XML::Document
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#version' do
44
+ it 'returns the version attribute value from the vnstat element' do
45
+ expect(subject.version).to eq '1.23'
46
+ end
47
+ end
48
+
49
+ describe '#xml_version' do
50
+ it 'returns the xmlversion attribute value from the vnstat element' do
51
+ expect(subject.xml_version).to eq '1'
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,5 @@
1
+ describe Vnstat::ExecutableNotFound do
2
+ it 'inherits from Vnstat::Error' do
3
+ expect(described_class).to be < Vnstat::Error
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ describe Vnstat::UnknownInterface do
2
+ it 'inherits from Vnstat::Error' do
3
+ expect(described_class).to be < Vnstat::Error
4
+ end
5
+ end
@@ -0,0 +1,124 @@
1
+ describe Vnstat::InterfaceCollection do
2
+ let :data do
3
+ <<-XML
4
+ <vnstat version="0.00" xmlversion="1">
5
+ <interface id="eth0"></interface>
6
+ <interface id="wlan0"></interface>
7
+ </vnstat>
8
+ XML
9
+ end
10
+
11
+ subject { described_class.new(data) }
12
+
13
+ it { is_expected.to be_a Vnstat::Document }
14
+
15
+ describe '.open' do
16
+ it 'calls .new' do
17
+ allow(described_class).to receive(:load_data).and_return('test data')
18
+
19
+ expect(described_class).to receive(:new).with('test data')
20
+
21
+ described_class.open
22
+ end
23
+
24
+ it 'calls .load_data' do
25
+ expect(described_class).to receive(:load_data).and_return('<vnstat />')
26
+
27
+ described_class.open
28
+ end
29
+ end
30
+
31
+ describe '#ids' do
32
+ it 'returns interface names from the data' do
33
+ expect(subject.ids).to eq %w(eth0 wlan0)
34
+ end
35
+ end
36
+
37
+ describe '#[]' do
38
+ context 'when existing interface given' do
39
+ context 'with String argument' do
40
+ it 'returns Vnstat::Interface' do
41
+ expect(subject['eth0']).to be_a Vnstat::Interface
42
+ end
43
+
44
+ it 'returns object with id matching the given argument' do
45
+ expect(subject['eth0'].id).to eq 'eth0'
46
+ end
47
+ end
48
+
49
+ context 'with symbolic argument' do
50
+ it 'returns Vnstat::Interface' do
51
+ expect(subject[:eth0]).to be_a Vnstat::Interface
52
+ end
53
+
54
+ it 'returns object with id matching the given argument' do
55
+ expect(subject[:eth0].id).to eq 'eth0'
56
+ end
57
+ end
58
+ end
59
+
60
+ context 'when non-existing interface given' do
61
+ it 'raises Vnstat::UnknownInterface' do
62
+ expect { subject['eth1'] }.to raise_error(Vnstat::UnknownInterface)
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '#reload' do
68
+ before :each do
69
+ allow(described_class).to receive(:load_data).and_return('<test />')
70
+ end
71
+
72
+ it 'sets #data using .load_data' do
73
+ expect(subject).to receive(:data=).with('<test />')
74
+
75
+ subject.reload
76
+ end
77
+
78
+ it 'returns self' do
79
+ expect(subject.reload).to eq subject
80
+ end
81
+ end
82
+
83
+ describe '#rebuild' do
84
+ before :each do
85
+ allow(described_class).to receive(:load_data).and_return('<test />')
86
+ end
87
+
88
+ context 'when Vnstat::Utils.call_executable_returning_status returns true' do
89
+ before :each do
90
+ allow(Vnstat::Utils)
91
+ .to receive(:call_executable_returning_status)
92
+ .with('--rebuildtotal').and_return(true)
93
+ end
94
+
95
+ it 'returns self' do
96
+ expect(subject.rebuild).to eq subject
97
+ end
98
+
99
+ it 'calls #reload' do
100
+ expect(subject).to receive(:reload)
101
+
102
+ subject.rebuild
103
+ end
104
+ end
105
+
106
+ context 'when Vnstat::Utils.call_executable_returning_status returns false' do
107
+ before :each do
108
+ allow(Vnstat::Utils)
109
+ .to receive(:call_executable_returning_status)
110
+ .with('--rebuildtotal').and_return(false)
111
+ end
112
+
113
+ it 'returns self' do
114
+ expect(subject.rebuild).to eq subject
115
+ end
116
+
117
+ it 'does not call #reload' do
118
+ expect(subject).not_to receive(:reload)
119
+
120
+ subject.rebuild
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,213 @@
1
+ describe Vnstat::Interface do
2
+ let :data do
3
+ <<-XML
4
+ <vnstat version="1.12" xmlversion="1">
5
+ <interface id="eth0">
6
+ <id>eth0-test</id>
7
+ <nick>Ethernet</nick>
8
+ <created>
9
+ <date><year>2015</year><month>10</month><day>21</day></date>
10
+ </created>
11
+ <updated>
12
+ <date><year>2015</year><month>10</month><day>21</day></date>
13
+ <time><hour>22</hour><minute>58</minute></time>
14
+ </updated>
15
+ <traffic>
16
+ <total><rx>1000</rx><tx>2000</tx></total>
17
+ <days>
18
+ <day id="0">
19
+ <date><year>2015</year><month>1</month><day>1</day></date>
20
+ <rx>1000</rx><tx>2000</tx>
21
+ </day>
22
+ <day id="1">
23
+ <date><year>2015</year><month>1</month><day>2</day></date>
24
+ <rx>3000</rx><tx>4000</tx>
25
+ </day>
26
+ </days>
27
+ <months>
28
+ <month id="0">
29
+ <date><year>2015</year><month>1</month></date>
30
+ <rx>1000</rx><tx>2000</tx>
31
+ </month>
32
+ <month id="1">
33
+ <date><year>2015</year><month>2</month></date>
34
+ <rx>3000</rx><tx>4000</tx>
35
+ </month>
36
+ </months>
37
+ <tops>
38
+ <top id="0">
39
+ <date><year>2015</year><month>10</month><day>22</day></date>
40
+ <time><hour>00</hour><minute>00</minute></time>
41
+ <rx>1000</rx><tx>2000</tx>
42
+ </top>
43
+ <top id="1">
44
+ <date><year>2015</year><month>10</month><day>21</day></date>
45
+ <time><hour>19</hour><minute>53</minute></time>
46
+ <rx>3000</rx><tx>4000</tx>
47
+ </top>
48
+ </tops>
49
+ <hours>
50
+ <hour id="19">
51
+ <date><year>2015</year><month>10</month><day>21</day></date>
52
+ <rx>1000</rx><tx>2000</tx>
53
+ </hour>
54
+ <hour id="20">
55
+ <date><year>2015</year><month>10</month><day>21</day></date>
56
+ <rx>3000</rx><tx>4000</tx>
57
+ </hour>
58
+ </hours>
59
+ </traffic>
60
+ </interface>
61
+ </vnstat>
62
+ XML
63
+ end
64
+
65
+ subject { described_class.new('eth0', data) }
66
+
67
+ it { is_expected.to be_a Vnstat::Document }
68
+
69
+ describe '.open' do
70
+ it 'calls .new forwarding the first argument' do
71
+ allow(described_class).to receive(:load_data).with('test')
72
+ .and_return('test data')
73
+
74
+ expect(described_class).to receive(:new).with('test', 'test data')
75
+
76
+ described_class.open('test')
77
+ end
78
+
79
+ it 'calls .load_data forwarding the argument' do
80
+ expect(described_class).to receive(:load_data).with('test')
81
+ .and_return('<vnstat />')
82
+
83
+ described_class.open('test')
84
+ end
85
+ end
86
+
87
+ describe '#reload' do
88
+ let :reloaded_data do
89
+ <<-XML
90
+ <vnstat version="1.12" xmlversion="1">
91
+ <interface id="eth0">
92
+ <id>eth0</id>
93
+ <nick>New Ethernet</nick>
94
+ </interface>
95
+ </vnstat>
96
+ XML
97
+ end
98
+
99
+ before :each do
100
+ allow(described_class).to receive(:load_data).and_return('<test />')
101
+ end
102
+
103
+ it 'returns self' do
104
+ expect(subject.reload).to eq subject
105
+ end
106
+
107
+ it 'calls .load_data with #id as argument' do
108
+ expect(described_class).to receive(:load_data).with('eth0')
109
+ .and_return(reloaded_data)
110
+
111
+ subject.reload
112
+ end
113
+
114
+ it 'calls #data= with the result from .load_data' do
115
+ allow(described_class).to receive(:load_data).and_return('<vnstat />')
116
+
117
+ expect(subject).to receive(:data=).with('<vnstat />')
118
+
119
+ subject.reload
120
+ end
121
+
122
+ it 'resets #nick from reloaded data' do
123
+ allow(described_class).to receive(:load_data).with('eth0')
124
+ .and_return(reloaded_data)
125
+
126
+ expect { subject.reload }
127
+ .to change { subject.nick }
128
+ .from('Ethernet').to('New Ethernet')
129
+ end
130
+ end
131
+
132
+ describe '#id' do
133
+ it 'returns id from the interface node' do
134
+ expect(subject.id).to eq 'eth0'
135
+ end
136
+ end
137
+
138
+ %w(nick name).each do |method_name|
139
+ describe "##{method_name}" do
140
+ it 'returns value from the nick node' do
141
+ expect(subject.public_send(method_name)).to eq 'Ethernet'
142
+ end
143
+ end
144
+ end
145
+
146
+ describe '#created_on' do
147
+ it 'returns the Date from the created node' do
148
+ date = Date.new(2015, 10, 21)
149
+ expect(subject.created_on).to eq date
150
+ end
151
+ end
152
+
153
+ describe '#updated_at' do
154
+ it 'returns the DateTime from the updated node' do
155
+ datetime = DateTime.new(2015, 10, 21, 22, 58)
156
+ expect(subject.updated_at).to eq datetime
157
+ end
158
+ end
159
+
160
+ describe '#total' do
161
+ it 'returns a Vnstat::Result' do
162
+ expect(subject.total).to be_a Vnstat::Result
163
+ end
164
+
165
+ it 'returns a result with the correct bytes received' do
166
+ expect(subject.total.bytes_received).to eq(1000 * 1024)
167
+ end
168
+
169
+ it 'returns a result with the correct bytes sent' do
170
+ expect(subject.total.bytes_sent).to eq(2000 * 1024)
171
+ end
172
+ end
173
+
174
+ describe '#hours' do
175
+ it 'returns a Vnstat::Traffic::Hourly' do
176
+ expect(subject.hours).to be_a Vnstat::Traffic::Hourly
177
+ end
178
+
179
+ it 'stores subject in Vnstat::Traffic::Hourly#interface' do
180
+ expect(subject.hours.interface).to eq subject
181
+ end
182
+ end
183
+
184
+ describe '#days' do
185
+ it 'returns a Vnstat::Traffic::Daily' do
186
+ expect(subject.days).to be_a Vnstat::Traffic::Daily
187
+ end
188
+
189
+ it 'stores subject in Vnstat::Traffic::Daily#interface' do
190
+ expect(subject.days.interface).to eq subject
191
+ end
192
+ end
193
+
194
+ describe '#months' do
195
+ it 'returns a Vnstat::Traffic::Monthly' do
196
+ expect(subject.months).to be_a Vnstat::Traffic::Monthly
197
+ end
198
+
199
+ it 'stores subject in Vnstat::Traffic::Monthly#interface' do
200
+ expect(subject.months.interface).to eq subject
201
+ end
202
+ end
203
+
204
+ describe '#tops' do
205
+ it 'returns a Vnstat::Traffic::Tops' do
206
+ expect(subject.tops).to be_a Vnstat::Traffic::Tops
207
+ end
208
+
209
+ it 'stores subject in Vnstat::Traffic::Tops#interface' do
210
+ expect(subject.tops.interface).to eq subject
211
+ end
212
+ end
213
+ end