ruby-jss 1.1.0b5 → 1.1.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.
- checksums.yaml +4 -4
- data/CHANGES.md +6 -3
- data/lib/jss/api_connection.rb +22 -16
- data/lib/jss/api_object.rb +4 -4
- data/lib/jss/api_object/computer/application_installs.rb +3 -4
- data/lib/jss/api_object/creatable.rb +1 -1
- data/lib/jss/api_object/distribution_point.rb +3 -2
- data/lib/jss/api_object/ldap_server.rb +3 -3
- data/lib/jss/api_object/matchable.rb +1 -1
- data/lib/jss/api_object/package.rb +1 -1
- data/lib/jss/api_object/patch_policy.rb +1 -1
- data/lib/jss/api_object/script.rb +1 -1
- data/lib/jss/api_object/updatable.rb +1 -1
- data/lib/jss/ruby_extensions/string.rb +8 -4
- data/lib/jss/version.rb +1 -1
- data/test/README.md +2 -2
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90a0bdaae94b5d0d1cb5ccad691ecf57daa75e205ceda668813655484ee232c8
|
4
|
+
data.tar.gz: 6c8e0211b2397df59d576b8fec2901f22003dd2d562c7a755209ebd0026493ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de40656a561b6624eaa28b4c851314de7bb4a84e151f656d1c021a784255c5c95ce76b2e7013553cc02f3094707934481e05059bf931b3fd9c659eedb8e05415
|
7
|
+
data.tar.gz: 8fdef7af00dc52b1ce253288415eade80d8d3812ed5470ad84f0e3c9292c95e32b1080b7c56e7457ce21e927bb0782e63124df1bb5efa2423e95694c620a148e
|
data/CHANGES.md
CHANGED
@@ -4,19 +4,20 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
-
## \[
|
7
|
+
## \[1.1.3] - 2019-09-23
|
8
8
|
### Added
|
9
9
|
- MobileDeviceExtensionAttribute now has a `.history` class method matching that of ComputerExtensionAttribute. Requires direct MySQL database access. Thanks @aurica!
|
10
10
|
- JSS::AmbiguousError exception class
|
11
11
|
- More caching of API data to improve general speed
|
12
12
|
- The hashes created by `APIObject.map_all_ids_to(blah)`
|
13
13
|
- ExtensionAttribute definitions when used by extendable classes
|
14
|
-
- Implemented Ruby2.4's `String#casecmp?` in older Rubies
|
15
14
|
- APIObject.fetch can take the search term `:random` and you'll get a randomly selected object. Example: `a_random_computer = JSS::Computer.fetch :random`
|
16
15
|
- Keys of the hash returned by `Computer#hardware` are now available as instance methods on Computer objects. So as well as `a_computer.hardware[:total_ram]` you can also do `a_computer.total_ram`
|
17
16
|
- Policy now recognizes the frequency Symbol `:once_per_user_per_computer`
|
18
17
|
- Attribute reader :management_status added to Computer class
|
19
|
-
- Implemented some useful String methods from newer versions of Ruby into older Rubies: casecmp
|
18
|
+
- Implemented some useful String methods from newer versions of Ruby into older Rubies: `casecmp?`, `delete_prefix`, & `delete_suffix`
|
19
|
+
- master_distribution_point class method in APIConnection & DistribtutionPoint now raise an error when no dist. point is 'master'
|
20
|
+
- The error states that the cloud dist. point may be the master, and there's no classic API access to it.
|
20
21
|
|
21
22
|
|
22
23
|
### Fixed
|
@@ -30,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
30
31
|
- Script#name= now works again, no longer uses a constant from an ancient version. Thanks @shahn
|
31
32
|
- Computer#asset_tag= now accepts nil to erase the value
|
32
33
|
- APIConnection.my_distribution_point & DistributionPoint.my_distribution_point now return the master_distribution_point object if there isn't one assigned to the current network segment.
|
34
|
+
- RestClient no longer warns about calling 'to_i' on Responses when calling APIConnection#put_rsrc & #post_rsrc
|
33
35
|
|
34
36
|
### Changed
|
35
37
|
- Monkey Patches are being moved to a better, more traceable technique, see https://www.justinweiss.com/articles/3-ways-to-monkey-patch-without-making-a-mess/
|
@@ -39,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
39
41
|
- Removed deprecated VALID_DATA_KEYS constants from APIObject subclasses
|
40
42
|
- Various changes in APIObject and its subclasses to try making `.fetch` and other lookup-methods faster.
|
41
43
|
- All of the NetworkSegment-related methods in APIConnection have been moved back to NetworkSegment. The methods in APIConnection still work, but are marked deprecated and will go away eventually.
|
44
|
+
- Removed last call to deprecated `URI.encode`, replaced with `CGI.escape`
|
42
45
|
|
43
46
|
|
44
47
|
## \[1.0.4] - 2019-05-06
|
data/lib/jss/api_connection.rb
CHANGED
@@ -539,7 +539,8 @@ module JSS
|
|
539
539
|
# Get an arbitrary JSS resource
|
540
540
|
#
|
541
541
|
# The first argument is the resource to get (the part of the API url
|
542
|
-
# after the 'JSSResource/' )
|
542
|
+
# after the 'JSSResource/' ) The resource must be properly URL escaped
|
543
|
+
# beforehand. Note: URL.encode is deprecated, use CGI.escape
|
543
544
|
#
|
544
545
|
# By default we get the data in JSON, and parse it
|
545
546
|
# into a ruby data structure (arrays, hashes, strings, etc)
|
@@ -559,11 +560,9 @@ module JSS
|
|
559
560
|
|
560
561
|
raise JSS::InvalidDataError, 'format must be :json or :xml' unless %i[json xml].include? format
|
561
562
|
|
562
|
-
# TODO: fix what rubocop is complaining about in the line below.
|
563
|
-
# (I doubt we want to CGI.escape the whole resource)
|
564
|
-
rsrc = URI.encode rsrc
|
565
563
|
begin
|
566
564
|
@last_http_response = @cnx[rsrc].get(accept: format)
|
565
|
+
@last_http_response.body
|
567
566
|
rescue RestClient::ExceptionWithResponse => e
|
568
567
|
handle_http_error e
|
569
568
|
end
|
@@ -588,6 +587,7 @@ module JSS
|
|
588
587
|
|
589
588
|
# send the data
|
590
589
|
@last_http_response = @cnx[rsrc].put(xml, content_type: 'text/xml')
|
590
|
+
@last_http_response.body
|
591
591
|
rescue RestClient::ExceptionWithResponse => e
|
592
592
|
handle_http_error e
|
593
593
|
end
|
@@ -607,7 +607,8 @@ module JSS
|
|
607
607
|
xml.gsub!(/\r/, ' ') if xml
|
608
608
|
|
609
609
|
# send the data
|
610
|
-
@last_http_response = @cnx[rsrc].post
|
610
|
+
@last_http_response = @cnx[rsrc].post(xml, content_type: 'text/xml', accept: :json)
|
611
|
+
@last_http_response.body
|
611
612
|
rescue RestClient::ExceptionWithResponse => e
|
612
613
|
handle_http_error e
|
613
614
|
end # post_rsrc
|
@@ -627,6 +628,7 @@ module JSS
|
|
627
628
|
|
628
629
|
# delete the resource
|
629
630
|
@last_http_response = @cnx[rsrc].delete
|
631
|
+
@last_http_response.body
|
630
632
|
rescue RestClient::ExceptionWithResponse => e
|
631
633
|
handle_http_error e
|
632
634
|
end # delete_rsrc
|
@@ -886,17 +888,19 @@ module JSS
|
|
886
888
|
@master_distribution_point = nil if refresh
|
887
889
|
return @master_distribution_point if @master_distribution_point
|
888
890
|
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
raise JSS::NoSuchItemError, 'No distribution points defined'
|
895
|
-
when 1
|
896
|
-
JSS::DistributionPoint.fetch id: all_dps.first[:id], api: self
|
897
|
-
else
|
898
|
-
JSS::DistributionPoint.fetch id: :master, api: self
|
891
|
+
JSS::DistributionPoint.all_ids.each do |dp_id|
|
892
|
+
dp = JSS::DistributionPoint.fetch id: dp_id, api: self
|
893
|
+
if dp.master?
|
894
|
+
@master_distribution_point = dp
|
895
|
+
break
|
899
896
|
end
|
897
|
+
end
|
898
|
+
|
899
|
+
return @master_distribution_point if @master_distribution_point
|
900
|
+
|
901
|
+
# If we're here, the Cloud DP might be master, but there's no
|
902
|
+
# access to it in the API :/
|
903
|
+
raise JSS::NoSuchItemError, 'No Master Distribtion Point defined. It could be the Cloud Dist Point, which is not available in the classic API'
|
900
904
|
end
|
901
905
|
|
902
906
|
# Get the DistributionPoint instance for the machine running
|
@@ -912,9 +916,11 @@ module JSS
|
|
912
916
|
return @my_distribution_point if @my_distribution_point
|
913
917
|
|
914
918
|
my_net_seg_id = my_network_segments[0]
|
919
|
+
|
915
920
|
if my_net_seg_id
|
916
921
|
my_net_seg = JSS::NetworkSegment.fetch(id: my_net_seg_id, api: self)
|
917
|
-
|
922
|
+
my_dp_name = my_net_seg.distribution_point
|
923
|
+
@my_distribution_point = JSS::DistributionPoint.fetch(name: my_dp_name) if my_dp_name
|
918
924
|
end # if my_net_seg_id
|
919
925
|
|
920
926
|
@my_distribution_point ||= master_distribution_point refresh
|
data/lib/jss/api_object.rb
CHANGED
@@ -586,7 +586,7 @@ module JSS
|
|
586
586
|
all(refresh, api: api) if refresh
|
587
587
|
|
588
588
|
# it its a valid id, return it
|
589
|
-
return identifier if all_ids.include? identifier
|
589
|
+
return identifier if all_ids(api: api).include? identifier
|
590
590
|
|
591
591
|
keys_to_check = lookup_keys(no_aliases: true)
|
592
592
|
keys_to_check.delete :id # we've already checked :id
|
@@ -829,7 +829,7 @@ module JSS
|
|
829
829
|
|
830
830
|
# does this lookup key have a fetch_rsrc_key?
|
831
831
|
fetch_rsrc_key = fetch_rsrc_key(fetch_key)
|
832
|
-
return new fetch_rsrc: "#{self::RSRC_BASE}/#{fetch_rsrc_key}/#{fetch_val}", api: api if fetch_rsrc_key
|
832
|
+
return new fetch_rsrc: "#{self::RSRC_BASE}/#{fetch_rsrc_key}/#{CGI.escape fetch_val.to_s}", api: api if fetch_rsrc_key
|
833
833
|
end
|
834
834
|
|
835
835
|
# if we'ere here, we need to get the id from either the lookup key/val or
|
@@ -1116,7 +1116,7 @@ module JSS
|
|
1116
1116
|
def delete
|
1117
1117
|
return nil unless @in_jss
|
1118
1118
|
@api.delete_rsrc @rest_rsrc
|
1119
|
-
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
|
1119
|
+
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name.to_s}"
|
1120
1120
|
@id = nil
|
1121
1121
|
@in_jss = false
|
1122
1122
|
@need_to_update = false
|
@@ -1444,7 +1444,7 @@ module JSS
|
|
1444
1444
|
@init_data = args
|
1445
1445
|
@name = args[:name]
|
1446
1446
|
@in_jss = false
|
1447
|
-
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
|
1447
|
+
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name.to_s}"
|
1448
1448
|
@need_to_update = true
|
1449
1449
|
end
|
1450
1450
|
|
@@ -96,11 +96,10 @@ module JSS
|
|
96
96
|
def self.application_installs(appname, fields: [], version: nil, ids_only: false, api: JSS.api)
|
97
97
|
fields = [fields] unless fields.is_a? Array
|
98
98
|
|
99
|
-
rsrc = "#{COMPUTER_APPLICATIONS_RSRC}/#{appname}"
|
100
|
-
rsrc << "/version/#{version}" if version
|
101
|
-
rsrc << "/inventory/#{fields.join
|
99
|
+
rsrc = "#{COMPUTER_APPLICATIONS_RSRC}/#{CGI.escape appname.to_s}"
|
100
|
+
rsrc << "/version/#{CGI.escape version.to_s}" if version
|
101
|
+
rsrc << "/inventory/#{CGI.escape fields.join(',')}" unless ids_only || fields.empty?
|
102
102
|
|
103
|
-
# get_rsrc will URI.encode the rsrc
|
104
103
|
result = api.get_rsrc(rsrc)[:computer_applications]
|
105
104
|
|
106
105
|
return result[:unique_computers].map { |c| c[:id] } if ids_only
|
@@ -103,7 +103,7 @@ module JSS
|
|
103
103
|
orig_id = @id
|
104
104
|
@id = nil
|
105
105
|
orig_rsrc = @rest_rsrc
|
106
|
-
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape new_name}"
|
106
|
+
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape new_name.to_s}"
|
107
107
|
orig_api = @api
|
108
108
|
@api = api
|
109
109
|
|
@@ -323,7 +323,7 @@ module JSS
|
|
323
323
|
# drop down below to try the password for mounting.
|
324
324
|
# we'll escape all the chars that aren't unreserved
|
325
325
|
# reserved_chars = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}]")
|
326
|
-
user_pass = "#{CGI.escape @http_username}:#{CGI.escape ro_pw}@"
|
326
|
+
user_pass = "#{CGI.escape @http_username.to_s}:#{CGI.escape ro_pw.to_s}@"
|
327
327
|
url = @http_url.sub "://#{@ip_address}", "://#{user_pass}#{@ip_address}"
|
328
328
|
else
|
329
329
|
url = @http_url
|
@@ -410,7 +410,7 @@ module JSS
|
|
410
410
|
|
411
411
|
username = access == :ro ? @read_only_username : @read_write_username
|
412
412
|
|
413
|
-
safe_pw = CGI.escape password
|
413
|
+
safe_pw = CGI.escape password.to_s
|
414
414
|
|
415
415
|
@mount_url = "#{@connection_type.downcase}://#{username}:#{safe_pw}@#{@ip_address}/#{@share_name}"
|
416
416
|
@mnt_cmd = case @connection_type.downcase
|
@@ -463,6 +463,7 @@ module JSS
|
|
463
463
|
#### aliases
|
464
464
|
alias hostname ip_address
|
465
465
|
alias umount unmount
|
466
|
+
alias master? is_master
|
466
467
|
|
467
468
|
# Private Instance Methods
|
468
469
|
######################################
|
@@ -159,7 +159,7 @@ module JSS
|
|
159
159
|
def self.check_membership(ldap_server, user, group, api: JSS.api)
|
160
160
|
ldap_server_id = valid_id ldap_server
|
161
161
|
raise JSS::NoSuchItemError, "No LDAPServer matching #{ldap_server}" unless ldap_server_id
|
162
|
-
rsrc = "#{RSRC_BASE}/id/#{ldap_server_id}/group/#{CGI.escape group}/user/#{CGI.escape user}"
|
162
|
+
rsrc = "#{RSRC_BASE}/id/#{ldap_server_id}/group/#{CGI.escape group.to_s}/user/#{CGI.escape user.to_s}"
|
163
163
|
member_check = api.get_rsrc rsrc
|
164
164
|
return false if member_check[:ldap_users].empty?
|
165
165
|
true
|
@@ -302,7 +302,7 @@ module JSS
|
|
302
302
|
#
|
303
303
|
def find_user(user, exact = false)
|
304
304
|
raise JSS::NoSuchItemError, 'LDAPServer not yet saved in the JSS' unless @in_jss
|
305
|
-
raw = api.get_rsrc("#{RSRC_BASE}/id/#{@id}/user/#{user}")[:ldap_users]
|
305
|
+
raw = api.get_rsrc("#{RSRC_BASE}/id/#{@id}/user/#{CGI.escape user.to_s}")[:ldap_users]
|
306
306
|
exact ? raw.select { |u| u[:username] == user } : raw
|
307
307
|
end
|
308
308
|
|
@@ -314,7 +314,7 @@ module JSS
|
|
314
314
|
#
|
315
315
|
def find_group(group, exact = false)
|
316
316
|
raise JSS::NoSuchItemError, 'LDAPServer not yet saved in the JSS' unless @in_jss
|
317
|
-
raw = api.get_rsrc("#{RSRC_BASE}/id/#{@id}/group/#{group}")[:ldap_groups]
|
317
|
+
raw = api.get_rsrc("#{RSRC_BASE}/id/#{@id}/group/#{CGI.escape group.to_s}")[:ldap_groups]
|
318
318
|
exact ? raw.select { |u| u[:groupname] == group } : raw
|
319
319
|
end
|
320
320
|
|
@@ -88,7 +88,7 @@ module JSS
|
|
88
88
|
###
|
89
89
|
def match(term, api: JSS.api )
|
90
90
|
raise JSS::InvalidDataError, "Match term may not be empty" if term.to_s.empty?
|
91
|
-
rsrc = "#{self::RSRC_BASE}/#{JSS::Matchable::MATCH_RSRC}/#{term}"
|
91
|
+
rsrc = "#{self::RSRC_BASE}/#{JSS::Matchable::MATCH_RSRC}/#{CGI.escape term.to_s}"
|
92
92
|
api.get_rsrc(rsrc)[self::RSRC_LIST_KEY]
|
93
93
|
end
|
94
94
|
|
@@ -899,7 +899,7 @@ module JSS
|
|
899
899
|
raise JSS::InvaldDatatError, 'Incorrect password for http access to distribution point.' unless mdp.check_pw(:http, ro_pw)
|
900
900
|
# insert the name and pw into the uri
|
901
901
|
# reserved_chars = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}]") # we'll escape all the chars that aren't unreserved
|
902
|
-
src_path = src_path.sub(%r{(https?://)(\S)}, "#{Regexp.last_match(1)}#{CGI.escape mdp.http_username}:#{CGI.escape ro_pw}@#{Regexp.last_match(2)}")
|
902
|
+
src_path = src_path.sub(%r{(https?://)(\S)}, "#{Regexp.last_match(1)}#{CGI.escape mdp.http_username.to_s}:#{CGI.escape ro_pw.to_s}@#{Regexp.last_match(2)}")
|
903
903
|
end
|
904
904
|
|
905
905
|
# or with filesharing?
|
@@ -510,7 +510,7 @@ module JSS
|
|
510
510
|
# TODO: prepare for more cases where the POST rsrc is
|
511
511
|
# different from the PUT/GET/DELETE.
|
512
512
|
orig_rsrc = @rest_rsrc
|
513
|
-
@rest_rsrc = "#{RSRC_BY_PATCH_TITLE}#{patch_title_id}"
|
513
|
+
@rest_rsrc = "#{RSRC_BY_PATCH_TITLE}#{CGI.escape patch_title_id.to_s}"
|
514
514
|
super
|
515
515
|
@rest_rsrc = orig_rsrc
|
516
516
|
refetch_version_info
|
@@ -184,7 +184,7 @@ module JSS
|
|
184
184
|
@name = new_val
|
185
185
|
|
186
186
|
### if our REST resource is based on the name, update that too
|
187
|
-
@rest_rsrc = "#{RSRC_BASE}/name/#{CGI.escape @name}" if @rest_rsrc.include? '/name/'
|
187
|
+
@rest_rsrc = "#{RSRC_BASE}/name/#{CGI.escape @name.to_s}" if @rest_rsrc.include? '/name/'
|
188
188
|
@need_to_update = true
|
189
189
|
end # name=
|
190
190
|
|
@@ -81,7 +81,7 @@ module JSS
|
|
81
81
|
raise JSS::AlreadyExistsError, "A #{self.class::RSRC_OBJECT_KEY} named '#{newname}' already exsists in the JSS" \
|
82
82
|
if self.class.all_names(:refresh, api: @api).include? newname
|
83
83
|
@name = newname
|
84
|
-
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}" if @rest_rsrc.include? '/name/'
|
84
|
+
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name.to_s}" if @rest_rsrc.include? '/name/'
|
85
85
|
@need_to_update = true
|
86
86
|
end # name=(newname)
|
87
87
|
|
@@ -21,11 +21,15 @@
|
|
21
21
|
# KIND, either express or implied. See the Apache License for the specific
|
22
22
|
# language governing permissions and limitations under the Apache License.
|
23
23
|
|
24
|
-
|
25
24
|
require 'jss/ruby_extensions/string/conversions.rb'
|
26
25
|
require 'jss/ruby_extensions/string/predicates.rb'
|
27
26
|
require 'jss/ruby_extensions/string/backports.rb'
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
# include the modules loaded above
|
29
|
+
class String
|
30
|
+
|
31
|
+
include JSSRubyExtensions::String::Predicates
|
32
|
+
include JSSRubyExtensions::String::Conversions
|
33
|
+
include JSSRubyExtensions::String::BackPorts
|
34
|
+
|
35
|
+
end
|
data/lib/jss/version.rb
CHANGED
data/test/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
For years, I've attempted to use some ruby testing framework to automate testing of ruby-jss.
|
4
4
|
|
5
|
-
Every time I've run into walls because unit testing suites are designed for unit testing, and that's not really what we need to do.
|
6
|
-
scaffolding that needs to happen before tests can even start - and that scaffolding itself is testing the code. And the details of the scaffolding will vary in different environments.
|
5
|
+
Every time I've run into walls because unit testing suites are designed for unit testing, and that's not really what we need to do. Yes we need to test individual methods of classes and so on, but when working with a REST API, there's a lot of
|
6
|
+
scaffolding that needs to happen before tests can even start - and that scaffolding itself is testing the code. And the details of the scaffolding will vary in different environments. We need to run integration tests.
|
7
7
|
|
8
8
|
For example, the tests *must* be very interactive from the start - you have to tell them what server to connect with, what credentials to use, and so on.
|
9
9
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-jss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Lasell
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-09-
|
12
|
+
date: 2019-09-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: plist
|
@@ -288,9 +288,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
288
288
|
version: 2.0.0
|
289
289
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
290
290
|
requirements:
|
291
|
-
- - "
|
291
|
+
- - ">="
|
292
292
|
- !ruby/object:Gem::Version
|
293
|
-
version:
|
293
|
+
version: '0'
|
294
294
|
requirements: []
|
295
295
|
rubyforge_project:
|
296
296
|
rubygems_version: 2.7.8
|