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,56 @@
1
+ module Vnstat
2
+ class Result
3
+ ##
4
+ # A class representing a tracking result for a specific hour.
5
+ #
6
+ # @attr_reader [Date] date The date the result was captured on.
7
+ # @attr_reader [Integer] hour The hour the result was captured at.
8
+ class Hour < Result
9
+ include DateDelegation
10
+ include TimeComparable
11
+
12
+ attr_reader :date, :hour
13
+
14
+ ##
15
+ # Initializes the {Hour}.
16
+ #
17
+ # @param [Date] date The date the result was captured on.
18
+ # @param [Integer] hour The hour the result was captured at.
19
+ # @param [Integer] bytes_received The received bytes.
20
+ # @param [Integer] bytes_sent The sent bytes.
21
+ def initialize(date, hour, bytes_received, bytes_sent)
22
+ @date = date
23
+ @hour = hour
24
+ super(bytes_received, bytes_sent)
25
+ end
26
+
27
+ ##
28
+ # Initializes a {Hour} using the the data contained in the given XML
29
+ # element.
30
+ #
31
+ # @param [Nokogiri::XML::Element] element The XML element.
32
+ # @return [Hour]
33
+ def self.extract_from_xml_element(element)
34
+ date = Parser.extract_date_from_xml_element(element)
35
+ hour = Integer(element.attr('id').to_s)
36
+ new(date, hour, *Parser.extract_transmitted_bytes_from_xml_element(element))
37
+ end
38
+
39
+ ##
40
+ # @return [Integer, nil]
41
+ def <=>(other)
42
+ return nil unless other.respond_to?(:bytes_transmitted)
43
+ return nil unless other.respond_to?(:time)
44
+ [time, bytes_transmitted] <=> [other.time, other.bytes_transmitted]
45
+ end
46
+
47
+ ##
48
+ # The time the result was captured.
49
+ #
50
+ # @return [DateTime]
51
+ def time
52
+ DateTime.new(year, month, day, hour)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,62 @@
1
+ module Vnstat
2
+ class Result
3
+ ##
4
+ # A class representing a tracking result for a specific minute.
5
+ #
6
+ # @attr_reader [DateTime] time The time the result was captured at.
7
+ class Minute < Result
8
+ include DateDelegation
9
+ include TimeComparable
10
+
11
+ attr_reader :time
12
+
13
+ ##
14
+ # Initializes the {Minute}.
15
+ #
16
+ # @param [DateTime] time The time the result was captured at.
17
+ # @param [Integer] bytes_received The received bytes.
18
+ # @param [Integer] bytes_sent The sent bytes.
19
+ def initialize(time, bytes_received, bytes_sent)
20
+ @time = time
21
+ super(bytes_received, bytes_sent)
22
+ end
23
+
24
+ ##
25
+ # Initializes a {Minute} using the the data contained in the given XML
26
+ # element.
27
+ #
28
+ # @param [Nokogiri::XML::Element] element The XML element.
29
+ # @return [Minute]
30
+ def self.extract_from_xml_element(element)
31
+ new(
32
+ Parser.extract_datetime_from_xml_element(element),
33
+ *Parser.extract_transmitted_bytes_from_xml_element(element)
34
+ )
35
+ end
36
+
37
+ ##
38
+ # The date the result was captured.
39
+ #
40
+ # @return [Date]
41
+ def date
42
+ time.to_date
43
+ end
44
+
45
+ ##
46
+ # The hour the result was captured.
47
+ #
48
+ # @return [Integer]
49
+ def hour
50
+ time.hour
51
+ end
52
+
53
+ ##
54
+ # The minute the result was captured.
55
+ #
56
+ # @return [Integer]
57
+ def minute
58
+ time.minute
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,47 @@
1
+ module Vnstat
2
+ class Result
3
+ ##
4
+ # A class representing a tracking result for a specific month.
5
+ #
6
+ # @attr_reader [Integer] year The year the result was captured in.
7
+ # @attr_reader [Integer] month The month the result was captured in.
8
+ class Month < Result
9
+ attr_reader :year, :month
10
+
11
+ ##
12
+ # Initializes the {Month}.
13
+ #
14
+ # @param [Integer] year The year the result was captured in.
15
+ # @param [Integer] month The month the result was captured in.
16
+ # @param [Integer] bytes_received The received bytes.
17
+ # @param [Integer] bytes_sent The sent bytes.
18
+ def initialize(year, month, bytes_received, bytes_sent)
19
+ @year = year
20
+ @month = month
21
+ super(bytes_received, bytes_sent)
22
+ end
23
+
24
+ ##
25
+ # Initializes a {Month} using the the data contained in the given XML
26
+ # element.
27
+ #
28
+ # @param [Nokogiri::XML::Element] element The XML element.
29
+ # @return [Month]
30
+ def self.extract_from_xml_element(element)
31
+ new(
32
+ *Parser.extract_month_from_xml_element(element),
33
+ *Parser.extract_transmitted_bytes_from_xml_element(element)
34
+ )
35
+ end
36
+
37
+ ##
38
+ # @return [Integer, nil]
39
+ def <=>(other)
40
+ return nil unless other.respond_to?(:bytes_transmitted)
41
+ return nil if !other.respond_to?(:year) || !other.respond_to?(:month)
42
+ [year, month, bytes_transmitted] <=>
43
+ [other.year, other.month, other.bytes_transmitted]
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,16 @@
1
+ module Vnstat
2
+ class Result
3
+ ##
4
+ # A module that is included by result types that can be compared based
5
+ # on their particular time information.
6
+ module TimeComparable
7
+ ##
8
+ # @return [Integer, nil]
9
+ def <=>(other)
10
+ return nil unless other.respond_to?(:bytes_transmitted)
11
+ return nil unless other.respond_to?(:time)
12
+ [time, bytes_transmitted] <=> [other.time, other.bytes_transmitted]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,90 @@
1
+ require 'open3'
2
+
3
+ module Vnstat
4
+ ##
5
+ # A class responsible for communication with command line interfaces.
6
+ #
7
+ # @attr_reader [Array] args
8
+ # @attr_reader [String, nil] success_result The STDOUT output of the called
9
+ # command.
10
+ # @attr_reader [String, nil] error_result The STDERR output of the called
11
+ # command.
12
+ # @attr_reader [Process::Status, nil] exit_status The exit status of the
13
+ # called command.
14
+ class SystemCall
15
+ attr_reader :args, :success_result, :error_result, :exit_status
16
+
17
+ ##
18
+ # Initializes the {SystemCall}.
19
+ #
20
+ # @param [Array] args The command line arguments.
21
+ def initialize(*args)
22
+ @args = args.flatten
23
+ end
24
+
25
+ ##
26
+ # Initializes and calls the {SystemCall}.
27
+ #
28
+ # @param [Array] args The command line arguments.
29
+ # @return [SystemCall]
30
+ def self.call(*args)
31
+ new(*args).call
32
+ end
33
+
34
+ ##
35
+ # Calls the command.
36
+ #
37
+ # @return [SystemCall]
38
+ def call
39
+ Open3.popen3(*args) do |_, stdout, stderr, wait_thr|
40
+ @success_result = readlines(stdout)
41
+ @error_result = readlines(stderr)
42
+ @exit_status = wait_thr.value
43
+ end
44
+ self
45
+ end
46
+
47
+ ##
48
+ # Determines whether the command has been called.
49
+ #
50
+ # @return [true, false]
51
+ def called?
52
+ !exit_status.nil?
53
+ end
54
+
55
+ ##
56
+ # Returns the command result.
57
+ #
58
+ # @return [String] Returns result from {#success_result} when command exited
59
+ # successfully. Otherwise returns result from {#error_result}.
60
+ # @raise [RuntimeError] Raises when the command has not yet been called.
61
+ def result
62
+ success? ? success_result : error_result
63
+ end
64
+
65
+ ##
66
+ # Indicates whether the command has been executed successfully.
67
+ #
68
+ # @raise [RuntimeError] Raises when the command has not yet been called.
69
+ # @return [true, false]
70
+ def success?
71
+ fail 'Command not invoked' unless called?
72
+ exit_status.success?
73
+ end
74
+
75
+ ##
76
+ # Indicates whether the command has not been executed successfully.
77
+ #
78
+ # @raise [RuntimeError] Raises when the command has not yet been called.
79
+ # @return [true, false]
80
+ def error?
81
+ !success?
82
+ end
83
+
84
+ private
85
+
86
+ def readlines(io)
87
+ io.readlines.join.chomp
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,11 @@
1
+ module Vnstat
2
+ ##
3
+ # A namespace incorporating all kinds of traffic collections.
4
+ module Traffic
5
+ autoload :Base, 'vnstat/traffic/base'
6
+ autoload :Daily, 'vnstat/traffic/daily'
7
+ autoload :Hourly, 'vnstat/traffic/hourly'
8
+ autoload :Monthly, 'vnstat/traffic/monthly'
9
+ autoload :Tops, 'vnstat/traffic/tops'
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ module Vnstat
2
+ module Traffic
3
+ ##
4
+ # An abstract implementation for a traffic collection.
5
+ #
6
+ # @attr_reader [Interface] interface The tracked interface.
7
+ class Base
8
+ include Enumerable
9
+
10
+ attr_reader :interface
11
+
12
+ ##
13
+ # Initializes the traffic collection.
14
+ #
15
+ # @param [Interface] interface The tracked interface.
16
+ def initialize(interface)
17
+ @interface = interface
18
+ end
19
+
20
+ ##
21
+ # Iterates over all results in the collection.
22
+ #
23
+ # @overload each
24
+ # @return [Enumerator]
25
+ #
26
+ # @overload each(&block)
27
+ # @yield [result]
28
+ # @yieldparam [Result] result
29
+ # @return [Base]
30
+ def each(&block)
31
+ entries_hash.values.each(&block)
32
+ end
33
+
34
+ private
35
+
36
+ def traffic_data
37
+ interface.data.xpath('//traffic')
38
+ end
39
+
40
+ def entries_hash
41
+ fail NotImplementedError,
42
+ "Please override #{self.class.name}##{__method__}"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,40 @@
1
+ module Vnstat
2
+ module Traffic
3
+ ##
4
+ # A class encapsulating daily tracking information.
5
+ class Daily < Base
6
+ ##
7
+ # Fetches a single {Result::Day} from the collection.
8
+ #
9
+ # @return [Result::Day]
10
+ #
11
+ # @overload [](date)
12
+ # @param [Date] date
13
+ #
14
+ # @overload [](year, month, day)
15
+ # @param [Integer] year
16
+ # @param [Integer] month
17
+ # @param [Integer] day
18
+ def [](*args)
19
+ date = case args.count
20
+ when 1 then args.first
21
+ when 3 then Date.new(*args)
22
+ else
23
+ fail ArgumentError, 'wrong number of arguments ' \
24
+ "(#{args.count} for 1 or 3)"
25
+ end
26
+ entries_hash[date]
27
+ end
28
+
29
+ private
30
+
31
+ def entries_hash
32
+ elements = traffic_data.xpath('days/day')
33
+ elements.each_with_object({}) do |element, hash|
34
+ result = Result::Day.extract_from_xml_element(element)
35
+ hash[result.date] = result
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ module Vnstat
2
+ module Traffic
3
+ ##
4
+ # A class encapsulating hourly tracking information.
5
+ class Hourly < Base
6
+ ##
7
+ # Fetches a single {Result::Hour} from the collection.
8
+ #
9
+ # @return [Result::Hour]
10
+ #
11
+ # @overload [](date, hour)
12
+ # @param [Date] date
13
+ # @param [Integer] hour
14
+ #
15
+ # @overload [](year, month, day, hour)
16
+ # @param [Integer] year
17
+ # @param [Integer] month
18
+ # @param [Integer] day
19
+ # @param [Integer] hour
20
+ def [](*args)
21
+ args_count = args.count
22
+ hour = args.pop
23
+ date = case args_count
24
+ when 2 then args.first
25
+ when 4 then Date.new(*args)
26
+ else
27
+ fail ArgumentError, 'wrong number of arguments ' \
28
+ "(#{args_count} for 2 or 4)"
29
+ end
30
+ entries_hash[[date, hour]]
31
+ end
32
+
33
+ private
34
+
35
+ def entries_hash
36
+ elements = traffic_data.xpath('hours/hour')
37
+ elements.each_with_object({}) do |element, hash|
38
+ result = Result::Hour.extract_from_xml_element(element)
39
+ hash[[result.date, result.hour]] = result
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,27 @@
1
+ module Vnstat
2
+ module Traffic
3
+ ##
4
+ # A class encapsulating monthly tracking information.
5
+ class Monthly < Base
6
+ ##
7
+ # Fetches a single {Result::Month} from the collection.
8
+ #
9
+ # @param [Integer] year
10
+ # @param [Integer] month
11
+ # @return [Result::Month]
12
+ def [](year, month)
13
+ entries_hash[[year, month]]
14
+ end
15
+
16
+ private
17
+
18
+ def entries_hash
19
+ elements = traffic_data.xpath('months/month')
20
+ elements.each_with_object({}) do |element, hash|
21
+ result = Result::Month.extract_from_xml_element(element)
22
+ hash[[result.year, result.month]] = result
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end