ruby-jss 0.6.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby-jss might be problematic. Click here for more details.

Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +7 -0
  3. data/CHANGES.md +112 -0
  4. data/LICENSE.txt +174 -0
  5. data/README.md +426 -0
  6. data/THANKS.md +6 -0
  7. data/bin/cgrouper +485 -0
  8. data/bin/subnet-update +400 -0
  9. data/lib/jss-api.rb +2 -0
  10. data/lib/jss.rb +190 -0
  11. data/lib/jss/api_connection.rb +410 -0
  12. data/lib/jss/api_object.rb +616 -0
  13. data/lib/jss/api_object/advanced_search.rb +389 -0
  14. data/lib/jss/api_object/advanced_search/advanced_computer_search.rb +95 -0
  15. data/lib/jss/api_object/advanced_search/advanced_mobile_device_search.rb +96 -0
  16. data/lib/jss/api_object/advanced_search/advanced_user_search.rb +95 -0
  17. data/lib/jss/api_object/building.rb +92 -0
  18. data/lib/jss/api_object/category.rb +147 -0
  19. data/lib/jss/api_object/computer.rb +852 -0
  20. data/lib/jss/api_object/creatable.rb +98 -0
  21. data/lib/jss/api_object/criteriable.rb +189 -0
  22. data/lib/jss/api_object/criteriable/criteria.rb +231 -0
  23. data/lib/jss/api_object/criteriable/criterion.rb +228 -0
  24. data/lib/jss/api_object/department.rb +93 -0
  25. data/lib/jss/api_object/distribution_point.rb +560 -0
  26. data/lib/jss/api_object/extendable.rb +221 -0
  27. data/lib/jss/api_object/extension_attribute.rb +466 -0
  28. data/lib/jss/api_object/extension_attribute/computer_extension_attribute.rb +362 -0
  29. data/lib/jss/api_object/extension_attribute/mobile_device_extension_attribute.rb +189 -0
  30. data/lib/jss/api_object/extension_attribute/user_extension_attribute.rb +117 -0
  31. data/lib/jss/api_object/group.rb +380 -0
  32. data/lib/jss/api_object/group/computer_group.rb +124 -0
  33. data/lib/jss/api_object/group/mobile_device_group.rb +139 -0
  34. data/lib/jss/api_object/group/user_group.rb +139 -0
  35. data/lib/jss/api_object/ldap_server.rb +535 -0
  36. data/lib/jss/api_object/locatable.rb +286 -0
  37. data/lib/jss/api_object/matchable.rb +97 -0
  38. data/lib/jss/api_object/mobile_device.rb +556 -0
  39. data/lib/jss/api_object/netboot_server.rb +148 -0
  40. data/lib/jss/api_object/network_segment.rb +414 -0
  41. data/lib/jss/api_object/osx_configuration_profile.rb +262 -0
  42. data/lib/jss/api_object/package.rb +839 -0
  43. data/lib/jss/api_object/peripheral.rb +335 -0
  44. data/lib/jss/api_object/peripheral_type.rb +295 -0
  45. data/lib/jss/api_object/policy.rb +898 -0
  46. data/lib/jss/api_object/purchasable.rb +316 -0
  47. data/lib/jss/api_object/removable_macaddr.rb +98 -0
  48. data/lib/jss/api_object/scopable.rb +136 -0
  49. data/lib/jss/api_object/scopable/scope.rb +621 -0
  50. data/lib/jss/api_object/script.rb +631 -0
  51. data/lib/jss/api_object/self_servable.rb +356 -0
  52. data/lib/jss/api_object/site.rb +93 -0
  53. data/lib/jss/api_object/software_update_server.rb +109 -0
  54. data/lib/jss/api_object/updatable.rb +117 -0
  55. data/lib/jss/api_object/uploadable.rb +138 -0
  56. data/lib/jss/api_object/user.rb +272 -0
  57. data/lib/jss/client.rb +504 -0
  58. data/lib/jss/compatibility.rb +66 -0
  59. data/lib/jss/composer.rb +185 -0
  60. data/lib/jss/configuration.rb +306 -0
  61. data/lib/jss/db_connection.rb +298 -0
  62. data/lib/jss/exceptions.rb +95 -0
  63. data/lib/jss/ruby_extensions.rb +35 -0
  64. data/lib/jss/ruby_extensions/filetest.rb +43 -0
  65. data/lib/jss/ruby_extensions/hash.rb +79 -0
  66. data/lib/jss/ruby_extensions/ipaddr.rb +91 -0
  67. data/lib/jss/ruby_extensions/pathname.rb +77 -0
  68. data/lib/jss/ruby_extensions/string.rb +59 -0
  69. data/lib/jss/ruby_extensions/time.rb +63 -0
  70. data/lib/jss/server.rb +108 -0
  71. data/lib/jss/utility.rb +478 -0
  72. data/lib/jss/version.rb +31 -0
  73. metadata +187 -0
@@ -0,0 +1,79 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ###
26
+ class Hash
27
+
28
+ ###
29
+ ### Convert Hash values to nil.
30
+ ###
31
+ ### With no block, values equalling the String, or any member of the Array, given
32
+ ### will be converted to nil. Equality is evaluated with == and Array#include?
33
+ ###
34
+ ### With a block, if the result of the block evaluates to true, the value is converted to nil.
35
+ ###
36
+ ### Subhashes are ignored unless recurse is true.
37
+ ###
38
+ ### @param to_nils[String,Array] Hash values equal to (==) these become nil. Defaults to empty string
39
+ ###
40
+ ### @param recurse[Boolean] should sub-Hashes be nillified?
41
+ ###
42
+ ### @yield [value] Hash values for which the block returns true will become nil.
43
+ ###
44
+ ### @return [Hash] the hash with the desired values converted to nil
45
+ ###
46
+ ### @example
47
+ ### hash = {:foo => '', :bar => {:baz => '' }}
48
+ ### hash.jss_nillify! # {:foo => nil, :bar => {:baz => '' }}
49
+ ###
50
+ ### hash = {:foo => '', :bar => {:baz => '' }}
51
+ ### hash.jss_nillify! '', :recurse # {:foo => nil, :bar => {:baz => nil }}
52
+ ###
53
+ ### hash = {:foo => 123, :bar => {:baz => '', :bim => "123" }}
54
+ ### hash.jss_nillify! ['', 123], :recurse # {:foo => nil, :bar => {:baz => nil, :bim => "123" }}
55
+ ###
56
+ ### hash = {:foo => 123, :bar => {:baz => '', :bim => "123" }}
57
+ ### hash.jss_nillify!(:anything, :recurse){|v| v.to_i == 123 } # {:foo => nil, :bar => {:baz => '', :bim => nil }}
58
+ ###
59
+ def jss_nillify!(to_nils = '', recurse = false, &block )
60
+
61
+ nillify_these = [] << to_nils
62
+ nillify_these.flatten!
63
+
64
+ self.each_pair do |k,v|
65
+ if v.class == Hash
66
+ v.jss_nillify!(to_nils, recurse, &block)
67
+ next
68
+ end
69
+ do_it = if block_given?
70
+ yield v
71
+ else
72
+ nillify_these.include? v
73
+ end
74
+ self[k] = nil if do_it
75
+ end # each pair
76
+ end # def nillify
77
+ end # class
78
+
79
+
@@ -0,0 +1,91 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ############################################
26
+ ### A few augmentations to IPAddr handling.
27
+ ###
28
+ class IPAddr
29
+
30
+ ### Convert starting and ending IPv4 IP addresses (either Strings or IPAddrs)
31
+ ### into a single masked IPv4 IPAddr
32
+ ###
33
+ ### @param starting[Strings, IPAddr] the starting IP address
34
+ ###
35
+ ### @param ending[Strings, IPAddr] the ending IP address
36
+ ###
37
+ ### @return [IPAddr] the IP address range represented as a masked IPv4 address
38
+ ###
39
+ ### @example
40
+ ### IPAddr.jss_masked_v4addr '10.0.0.0', '10.0.0.255' # => #<IPAddr: IPv4:10.0.0.0/255.255.255.0>
41
+ ###
42
+ def self.jss_masked_v4addr(starting,ending)
43
+ IPAddr.new "#{starting}/#{self.jss_cidr_from_ends(starting,ending)}"
44
+ end #self.jss_masked_v4addr(starting,ending)
45
+
46
+ ### Given starting and ending IPv4 IP addresses (either Strings or IPAddrs)
47
+ ### return the CIDR notation routing prefix mask
48
+ ###
49
+ ### @param starting[Strings, IPAddr] the starting IP address
50
+ ###
51
+ ### @param ending[Strings, IPAddr] the ending IP address
52
+ ###
53
+ ### @return [FixNum] the CIDR notation routing prefix mask
54
+ ###
55
+ ### @example
56
+ ### IPAddr.jss_cidr_from_ends '10.0.0.0', '10.0.0.255' # => 24
57
+ ###
58
+ def self.jss_cidr_from_ends(starting,ending)
59
+
60
+ starting = IPAddr.new(starting) unless starting.kind_of? IPAddr
61
+ ending = IPAddr.new(ending) unless ending.kind_of? IPAddr
62
+
63
+ ### how many possible addresses in the range?
64
+ num_addrs = ending.to_i - starting.to_i + 1
65
+
66
+ ### convert the number of possible addresses to
67
+ ### binary then subtract the number of bits from
68
+ ### the full length of an IPv4 addr
69
+ ### (32 bits) and that gives the CIDR prefix
70
+ return 32 - num_addrs.to_s(2).length + 1
71
+
72
+ end #self.get_cidr(starting,ending)
73
+
74
+ ### Convert a starting address (either String or IPAddr) and a
75
+ ### CIDR notation routing prefix mask into the IPv4 address
76
+ ### of at the end of the range of addresses.
77
+ ###
78
+ ### @param starting[Strings, IPAddr] the starting IP address
79
+ ###
80
+ ### @param cidr[FixNum] the CIDR mask
81
+ ###
82
+ ### @return [IPAddr] the ending IP address of the range.
83
+ ###
84
+ ### @example
85
+ ### IPAddr.jss_ending_address '10.0.0.0', 24 # => #<IPAddr: IPv4:10.0.0.255>
86
+ ###
87
+ def self.jss_ending_address(starting, cidr)
88
+ IPAddr.new( "#{starting}/#{cidr}").to_range.max
89
+ end # ending_address
90
+
91
+ end # Class IPAddr
@@ -0,0 +1,77 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ############################################
26
+ ### Some handy additions to the Pathname class.
27
+ ### Why aren't they there already?
28
+ ###
29
+ class Pathname
30
+
31
+ ### Is this a real file rather than a symlink?
32
+ ### @see FileTest.real_file
33
+ def jss_real_file?
34
+ FileTest.jss_real_file? self
35
+ end # real_file?
36
+
37
+ ### Copy a path to a destination
38
+ ### @see FileUtils.cp
39
+ def jss_cp(dest, options = {})
40
+ FileUtils.cp @path, dest.to_s, options
41
+ end # cp
42
+
43
+ ### Recursively copy this path to a destination
44
+ ### @see FileUtils.cp_r
45
+ def jss_cp_r(dest, options = {})
46
+ FileUtils.cp_r @path, dest.to_s, options
47
+ end # cp
48
+
49
+ ### Write some string content to a file.
50
+ ###
51
+ ### Simpler than always using an open('w') block
52
+ ### *CAUTION* this overwrites files!
53
+ ###
54
+ def jss_save(content)
55
+ self.open('w'){|f| f.write content.to_s}
56
+ end
57
+
58
+ ### Append some string content to a file.
59
+ ###
60
+ ### Simpler than always using an open('a') block
61
+ ###
62
+ def jss_append(content)
63
+ self.open('a'){|f| f.write content.to_s}
64
+ end
65
+
66
+ ### Touching can be good
67
+ ###
68
+ ### @see FileUtils.touch
69
+ def jss_touch
70
+ FileUtils.touch @path
71
+ end
72
+
73
+ ### Pathname should use FileUtils.chown, not File.chown
74
+ def jss_chown(u,g)
75
+ FileUtils.chown u, g, @path
76
+ end
77
+ end # class Pathname
@@ -0,0 +1,59 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ###
26
+ class String
27
+
28
+ ### Convert the strings "true" and "false"
29
+ ### (after stripping whitespace and downcasing)
30
+ ### to TrueClass and FalseClass respectively
31
+ ###
32
+ ### Return nil if any other string.
33
+ ###
34
+ ### @return [Boolean,nil] the boolean value
35
+ ###
36
+ def jss_to_bool
37
+ case self.strip.downcase
38
+ when "true" then return true
39
+ when "false" then return false
40
+ else return nil
41
+ end # case
42
+ end # to bool
43
+
44
+ ### Convert a string to a Time object
45
+ ###
46
+ ### returns nil if not parsable by JSS::parse_datetime
47
+ ###
48
+ ### @return [Time] the time represented by the string.
49
+ ###
50
+ def jss_to_time
51
+ begin
52
+ JSS.parse_time self
53
+ rescue
54
+ return nil
55
+ end
56
+ end
57
+
58
+
59
+ end # class
@@ -0,0 +1,63 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ############################################
26
+ ### The JSS API can take incoming timestamp data in three formats:
27
+ ### - plain date: YYYY-mm-dd HH:MM:SS, which is assumed to be in the local timezone
28
+ ### - UTC date: YYYY-mm-ddTHH:MM:SS.mmm [+-]zzzz, the local time with milliseconds and the timezone offset
29
+ ### - Unix epoch with milliseconds, nnnnnnnnnnnnn, which like all epoch times, is UTC
30
+ ###
31
+ ### Here we extend Time to return the three formats for use in XML data to pass
32
+ ### back to the API. The API xml works with any of the formats, in appropriate elements.
33
+ ###
34
+ ### Generally this gem uses the epoch format for passing values back to the API, just to help avoid
35
+ ### timezone weirdness, but any should work.
36
+ ###
37
+ ### When reading from the API, all three formats are returned in different xml elements,
38
+ ### and the APIObjects in the gem will use the epoch format and the JSS.epoch_to_time method (q.v.) to store
39
+ ### the data as a Ruby Time.
40
+ ###
41
+ class Time
42
+
43
+ ### @return [Integer] the milliseconds of the Time
44
+ def jss_msec
45
+ (self.usec/1000.0).round
46
+ end
47
+
48
+ ### @return [Integer] The Time as a unix epoch with milliseconds appended
49
+ def to_jss_epoch
50
+ (self.strftime('%s') + self.jss_msec.to_s.ljust(3,'0')).to_i
51
+ end
52
+
53
+ ### @return [String] the Time formatted for a plain JSS XML date element
54
+ def to_jss_date
55
+ self.strftime '%Y-%m-%d %H:%M:%S'
56
+ end
57
+
58
+ ### @return [String] the Time formatted for a JSS UTC XML date element
59
+ def to_jss_utc
60
+ self.strftime("%Y-%m-%dT%H:%M:%S.#{self.jss_msec}%z")
61
+ end
62
+
63
+ end
@@ -0,0 +1,108 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ###
26
+ module JSS
27
+
28
+ ### A class representing a JSS Server.
29
+ ###
30
+ ### The {JSS::APIConnection} instance has a JSS::Server instance in its @server attribute.
31
+ ### It is created fresh every time {APIConnection#connect} is called.
32
+ ###
33
+ ### That's the only time it should be instantiated, and all access should be through
34
+ ### {JSS::API.server}
35
+ ###
36
+ class Server
37
+
38
+ #####################################
39
+ ### Attributes
40
+ #####################################
41
+
42
+ ### @return [String] the organization to which the server is licensed
43
+ attr_reader :organization
44
+
45
+
46
+ ### @return [String] the activation code for the server licence
47
+ attr_reader :activation_code
48
+
49
+ ### @return [String] the type of server licence
50
+ attr_reader :license_type
51
+
52
+ ### @return [String] the license product name
53
+ attr_reader :product
54
+
55
+ ### @return [String] The version of the JSS. See the method JSS.parse_jss_version
56
+ attr_reader :version
57
+
58
+ ### @return [Integer]
59
+ attr_reader :major_version
60
+
61
+ ### @return [Integer]
62
+ attr_reader :minor_version
63
+
64
+ ### @return [Integer]
65
+ attr_reader :revision_version
66
+
67
+ ### @return [String]
68
+ attr_reader :raw_version
69
+
70
+ #####################################
71
+ ### Instance Methods
72
+ #####################################
73
+
74
+ ###
75
+ ### Initialize!
76
+ ###
77
+ def initialize
78
+ begin
79
+
80
+ # the jssuser resource is readable by anyone with a JSS acct
81
+ # regardless of their permissions.
82
+ # However, it's marked as 'deprecated'. Hopefully jamf will
83
+ # keep this basic level of info available for basic authentication
84
+ # and JSS version checking.
85
+ ju = JSS::API.get_rsrc('jssuser')[:user]
86
+ @license_type = ju[:license_type]
87
+ @product = ju[:product]
88
+ @raw_version = ju[:version]
89
+ parsed = JSS.parse_jss_version(@raw_version)
90
+ @major_version = parsed[:major]
91
+ @minor_version = parsed[:minor]
92
+ @revision_version = parsed[:revision]
93
+ @version = parsed[:version]
94
+
95
+ rescue RestClient::Request::Unauthorized
96
+ raise JSS::AuthenticationError, "Incorrect JSS username or password for '#{JSS::API.jss_user}@#{JSS::API.server_host}'."
97
+ end
98
+
99
+ end
100
+
101
+
102
+ ##### Aliases
103
+ alias institution organization
104
+ alias product_name product
105
+
106
+ end # class server
107
+
108
+ end # module