kat 2.0.0 → 2.0.1
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/kat.gemspec +3 -2
- data/lib/kat/app.rb +34 -27
- data/lib/kat/field_map.rb +23 -112
- data/lib/kat/search.rb +4 -3
- data/lib/kat/version.rb +1 -1
- data/test/kat/test_app.rb +14 -7
- data/test/kat/test_colour.rb +6 -0
- metadata +17 -5
- data/test/kat/test_field_map.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a787a58459b2ef07e56e8ea0dcbd2303b88d1a2e
|
4
|
+
data.tar.gz: 8044118ce50282384b57378d82c117b633a1f372
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47b3095fe9256337fe71d7248d0f36d3cee42f78794d66da09bedea20bb8120efe9bed80bfa24f45469b7bf25361ee236155f4d36bab343b8b4691640aec14ce
|
7
|
+
data.tar.gz: 5bfe7bd174afabe76d089625bc35054da1be957781061932db125c1e6fa74fc84be2fc22b7e3323cc8570d6f1d9f3fd99496b6e8855d99f203834bea89820e47
|
data/README.md
CHANGED
@@ -22,11 +22,11 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
### Quick search
|
24
24
|
|
25
|
-
Kat.
|
25
|
+
Kat.quick_search('game of thrones')
|
26
26
|
|
27
27
|
### Search for torrents
|
28
28
|
|
29
|
-
kat = Kat.
|
29
|
+
kat = Kat.search('game of thrones', { :category => 'tv' })
|
30
30
|
kat.search
|
31
31
|
|
32
32
|
### Specifying pages
|
data/kat.gemspec
CHANGED
@@ -16,11 +16,12 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.description = 'A Ruby interface to Kickass Torrents'
|
17
17
|
|
18
18
|
s.files = `git ls-files`.split $/
|
19
|
-
s.executables = s.files.grep(%r{^bin/}) {|f| File.basename f }
|
19
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename f }
|
20
20
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
21
|
-
s.require_paths = [
|
21
|
+
s.require_paths = ['lib']
|
22
22
|
|
23
23
|
s.add_runtime_dependency 'nokogiri', '~> 1.6'
|
24
24
|
s.add_runtime_dependency 'highline', '~> 1.6'
|
25
25
|
s.add_runtime_dependency 'trollop', '~> 2.0'
|
26
|
+
s.add_runtime_dependency 'andand', '~> 1.3'
|
26
27
|
end
|
data/lib/kat/app.rb
CHANGED
@@ -18,6 +18,7 @@ module Kat
|
|
18
18
|
|
19
19
|
class App
|
20
20
|
MIN_WIDTH = 80
|
21
|
+
CONFIG = File.join ENV['HOME'], '.katrc'
|
21
22
|
|
22
23
|
# The current page number (0-based)
|
23
24
|
attr_accessor :page
|
@@ -46,20 +47,22 @@ module Kat
|
|
46
47
|
# Initialise the app's options
|
47
48
|
#
|
48
49
|
def init_options(args = nil)
|
49
|
-
@options = {}
|
50
50
|
@args = case args
|
51
51
|
when nil then []
|
52
52
|
when String then args.split
|
53
53
|
else args
|
54
54
|
end
|
55
55
|
|
56
|
-
load_config
|
56
|
+
@options = load_config || {}
|
57
57
|
|
58
58
|
Kat.options(@args).tap { |o|
|
59
59
|
@options.merge!(o) { |k, ov, nv| o["#{ k }_given".intern] ? nv : ov }
|
60
60
|
}
|
61
61
|
|
62
62
|
Kat::Colour.colour = @options[:colour]
|
63
|
+
rescue NoMethodError => e
|
64
|
+
@options = {}
|
65
|
+
warn "Wrong config file format: #{ e }"
|
63
66
|
end
|
64
67
|
|
65
68
|
#
|
@@ -144,28 +147,26 @@ module Kat
|
|
144
147
|
}
|
145
148
|
].map { |w| Thread.new { w.call } }.each(&:join)
|
146
149
|
|
147
|
-
|
148
|
-
puts "\rNo results ".red
|
149
|
-
puts @kat.error[:error] if @kat.error
|
150
|
-
return false
|
151
|
-
end
|
150
|
+
puts (res = format_results)
|
152
151
|
|
153
|
-
|
152
|
+
if res.size > 1
|
153
|
+
case (answer = prompt)
|
154
|
+
when 'i' then @show_info = !@show_info
|
155
|
+
when 'n' then @page += 1 if next?
|
156
|
+
when 'p' then @page -= 1 if prev?
|
157
|
+
when 'q' then return false
|
158
|
+
else
|
159
|
+
if (1..@kat.results[@page].size).include? (answer = answer.to_i)
|
160
|
+
print "\nDownloading".yellow <<
|
161
|
+
": #{ @kat.results[@page][answer - 1][:title] }... "
|
162
|
+
puts download @kat.results[@page][answer - 1]
|
163
|
+
end
|
164
|
+
end
|
154
165
|
|
155
|
-
|
156
|
-
when 'i' then @show_info = !@show_info
|
157
|
-
when 'n' then @page += 1 if next?
|
158
|
-
when 'p' then @page -= 1 if prev?
|
159
|
-
when 'q' then return false
|
166
|
+
true
|
160
167
|
else
|
161
|
-
|
162
|
-
print "\nDownloading".yellow <<
|
163
|
-
": #{ @kat.results[@page][answer - 1][:title] }... "
|
164
|
-
puts download @kat.results[@page][answer - 1]
|
165
|
-
end
|
168
|
+
false
|
166
169
|
end
|
167
|
-
|
168
|
-
true
|
169
170
|
end
|
170
171
|
|
171
172
|
#
|
@@ -195,6 +196,12 @@ module Kat
|
|
195
196
|
def format_results
|
196
197
|
main_width = @window_width - (!hide_info? || @show_info ? 42 : 4)
|
197
198
|
|
199
|
+
if @kat.error
|
200
|
+
return ["\rConnection failed".red]
|
201
|
+
elsif !@kat.results[@page]
|
202
|
+
return ["\rNo results ".red]
|
203
|
+
end
|
204
|
+
|
198
205
|
buf = ["\r%-#{ main_width + 5 }s#{ ' Size Age Seeds Leeches' if !hide_info? || @show_info }" %
|
199
206
|
"Page #{ page + 1 } of #{ @kat.pages }", nil].yellow!
|
200
207
|
|
@@ -242,7 +249,9 @@ module Kat
|
|
242
249
|
# Download the torrent to either the output directory or the working directory
|
243
250
|
#
|
244
251
|
def download(torrent)
|
245
|
-
|
252
|
+
return [:failed, 'no download link available'].red unless torrent[:download]
|
253
|
+
|
254
|
+
uri = URI(URI.encode torrent[:download])
|
246
255
|
uri.query = nil
|
247
256
|
file = "#{ @options[:output] || '.' }/" <<
|
248
257
|
"#{ torrent[:title].tr(' ', ?.).gsub(/[^a-z0-9()_.-]/i, '') }.torrent"
|
@@ -259,16 +268,14 @@ module Kat
|
|
259
268
|
end
|
260
269
|
|
261
270
|
#
|
262
|
-
# Load options from
|
271
|
+
# Load options from CONFIG if it exists
|
263
272
|
#
|
264
273
|
def load_config
|
265
|
-
|
266
|
-
|
267
|
-
@options = (symbolise = -> h {
|
274
|
+
(symbolise = -> h {
|
268
275
|
Hash === h ? Hash[h.map { |k, v| [k.intern, symbolise[v]] }] : h
|
269
|
-
})[YAML.load_file
|
276
|
+
})[YAML.load_file CONFIG] if File.readable? CONFIG
|
270
277
|
rescue => e
|
271
|
-
warn "Failed to load #{
|
278
|
+
warn "Failed to load #{ CONFIG }: #{ e }"
|
272
279
|
end
|
273
280
|
|
274
281
|
end
|
data/lib/kat/field_map.rb
CHANGED
@@ -1,122 +1,33 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
1
|
module Kat
|
4
2
|
|
5
|
-
FIELD_MAP =
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
else h
|
10
|
-
end
|
11
|
-
})[YAML.load(<<-FIELD_MAP
|
12
|
-
---
|
13
|
-
exact:
|
14
|
-
type: string
|
15
|
-
desc: Exact phrase
|
16
|
-
|
17
|
-
or:
|
18
|
-
type: string
|
19
|
-
desc: Optional words
|
20
|
-
multi: true
|
21
|
-
|
22
|
-
without:
|
23
|
-
type: string
|
24
|
-
desc: Without this word
|
25
|
-
multi: true
|
26
|
-
|
27
|
-
sort:
|
28
|
-
type: string
|
29
|
-
desc: Sort field (size, files, added, seeds, leeches)
|
30
|
-
|
31
|
-
asc:
|
32
|
-
desc: Ascending sort order (descending is default)
|
33
|
-
|
34
|
-
category:
|
35
|
-
select: categories
|
36
|
-
type: string
|
37
|
-
desc: Category
|
38
|
-
short: c
|
39
|
-
|
40
|
-
added:
|
41
|
-
select: times
|
42
|
-
sort: time_add
|
43
|
-
type: string
|
44
|
-
desc: Age of the torrent
|
45
|
-
id: age
|
46
|
-
short: a
|
47
|
-
|
48
|
-
size:
|
49
|
-
sort: size
|
50
|
-
|
51
|
-
user:
|
52
|
-
input: true
|
53
|
-
type: string
|
54
|
-
desc: Uploader
|
55
|
-
|
56
|
-
files:
|
57
|
-
input: true
|
58
|
-
sort: files_count
|
59
|
-
type: int
|
60
|
-
desc: Number of files
|
61
|
-
|
62
|
-
imdb:
|
63
|
-
input: true
|
64
|
-
type: int
|
65
|
-
desc: IMDB ID
|
66
|
-
|
67
|
-
seeds:
|
68
|
-
input: true
|
69
|
-
sort: seeders
|
70
|
-
type: int
|
71
|
-
desc: Min no of seeders
|
72
|
-
short: s
|
73
|
-
|
74
|
-
leeches:
|
75
|
-
sort: leechers
|
76
|
-
|
77
|
-
season:
|
78
|
-
input: true
|
79
|
-
type: int
|
80
|
-
desc: Television season
|
81
|
-
|
82
|
-
episode:
|
83
|
-
input: true
|
84
|
-
type: int
|
85
|
-
desc: Television episode
|
86
|
-
short: e
|
3
|
+
FIELD_MAP = {
|
4
|
+
exact: { type: :string, desc: 'Exact phrase' },
|
5
|
+
or: { type: :string, desc: 'Optional words', multi: true },
|
6
|
+
without: { type: :string, desc: 'Without this word', multi: true },
|
87
7
|
|
88
|
-
|
89
|
-
|
90
|
-
type: int
|
91
|
-
desc: Language
|
92
|
-
id: lang_id
|
8
|
+
sort: { type: :string, desc: 'Sort field (size, files, added, seeds, leeches)' },
|
9
|
+
asc: { desc: 'Ascending sort order (descending is default)' },
|
93
10
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
desc: Game platform
|
98
|
-
id: platform_id
|
11
|
+
category: { type: :string, desc: 'Category', select: :categories, short: :c },
|
12
|
+
added: { type: :string, desc: 'Age of the torrent', select: :times, short: :a, sort: :time_add, id: :age },
|
13
|
+
size: { sort: :size },
|
99
14
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
short:
|
15
|
+
user: { type: :string, desc: 'Uploader', input: true },
|
16
|
+
files: { type: :int, desc: 'Number of files', input: true, sort: :files_count },
|
17
|
+
imdb: { type: :int, desc: 'IMDB ID', input: true },
|
18
|
+
seeds: { type: :int, desc: 'Min no of seeders', input: true, sort: :seeders, short: :s },
|
19
|
+
leeches: { sort: :leechers },
|
20
|
+
season: { type: :int, desc: 'Television season', input: true },
|
21
|
+
episode: { type: :int, desc: 'Television episode', input: true, short: :e },
|
104
22
|
|
105
|
-
|
106
|
-
|
107
|
-
desc: Verified torrent
|
108
|
-
short: none
|
23
|
+
language: { type: :int, desc: 'Language', select: :languages, id: :lang_id },
|
24
|
+
platform: { type: :int, desc: 'Game platform', select: :platforms, id: :platform_id },
|
109
25
|
|
110
|
-
|
111
|
-
|
112
|
-
desc: Directory to save torrents in
|
113
|
-
short: o
|
26
|
+
safe: { desc: 'Family safe filter', check: true, short: :none},
|
27
|
+
verified: { desc: 'Verified torrent', check: true, short: :none },
|
114
28
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
short: none
|
119
|
-
FIELD_MAP
|
120
|
-
)].freeze
|
29
|
+
output: { type: :string, desc: 'Directory to save torrents in', short: :o },
|
30
|
+
colour: { desc: 'Output with colour', short: :none }
|
31
|
+
}.freeze
|
121
32
|
|
122
33
|
end
|
data/lib/kat/search.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/field_map'
|
2
2
|
require 'nokogiri'
|
3
3
|
require 'net/http'
|
4
|
+
require 'andand'
|
4
5
|
|
5
6
|
module Kat
|
6
7
|
|
@@ -197,10 +198,10 @@ module Kat
|
|
197
198
|
doc = Nokogiri::HTML(res.body)
|
198
199
|
|
199
200
|
@results[page] = doc.css('td.torrentnameCell').map { |node|
|
200
|
-
{ path: node.css('a.normalgrey').first.
|
201
|
+
{ path: node.css('a.normalgrey').first.andand.attr('href'),
|
201
202
|
title: node.css('a.normalgrey').text,
|
202
|
-
magnet: node.css('a.imagnet').first.
|
203
|
-
download: node.css('a.idownload').last.
|
203
|
+
magnet: node.css('a.imagnet').first.andand.attr('href'),
|
204
|
+
download: node.css('a.idownload').last.andand.attr('href'),
|
204
205
|
size: (node = node.next_element).text,
|
205
206
|
files: (node = node.next_element).text.to_i,
|
206
207
|
age: (node = node.next_element).text,
|
data/lib/kat/version.rb
CHANGED
data/test/kat/test_app.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
2
|
require File.dirname(__FILE__) + '/../../lib/kat/app'
|
3
3
|
|
4
|
-
app = Kat::App.new %w(
|
4
|
+
app = Kat::App.new %w(aliens -c movies -o .)
|
5
5
|
app.kat.go(1).go(app.kat.pages - 1)
|
6
6
|
|
7
7
|
describe Kat::App do
|
@@ -14,7 +14,7 @@ describe Kat::App do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
it 're-initialises options' do
|
17
|
-
k = Kat::App.new %w(
|
17
|
+
k = Kat::App.new %w(aliens)
|
18
18
|
k.init_options %w(bible -c books)
|
19
19
|
k.options.must_be_instance_of Hash
|
20
20
|
k.options[:category].must_equal 'books'
|
@@ -42,11 +42,13 @@ describe Kat::App do
|
|
42
42
|
|
43
43
|
prev?.must_equal true
|
44
44
|
next?.wont_equal true
|
45
|
+
# Skip the test if there's no results. We really only want to test in ideal
|
46
|
+
# network conditions and no results here are an indication that's not the case
|
45
47
|
validation_regex.must_equal(
|
46
48
|
/^([pq]|[1-#{ [9, n].min }]#{
|
47
|
-
|
48
|
-
|
49
|
-
)
|
49
|
+
"|1[0-#{ [9, n - 10].min }]" if n > 9
|
50
|
+
}#{ "|2[0-#{ n - 20 }]" if n > 19 })$/
|
51
|
+
) if n > 0
|
50
52
|
|
51
53
|
@page = 0
|
52
54
|
}
|
@@ -83,6 +85,7 @@ describe Kat::App do
|
|
83
85
|
|
84
86
|
it 'formats a list of torrents' do
|
85
87
|
Kat::Colour.colour = false
|
88
|
+
|
86
89
|
app.instance_exec {
|
87
90
|
set_window_width
|
88
91
|
list = format_results
|
@@ -90,8 +93,10 @@ describe Kat::App do
|
|
90
93
|
list.must_be_instance_of Array
|
91
94
|
list.wont_be_empty
|
92
95
|
|
93
|
-
list
|
94
|
-
|
96
|
+
list[1].must_be_nil
|
97
|
+
list.last.must_be_nil
|
98
|
+
|
99
|
+
(2..list.size - 2).each { |i|
|
95
100
|
list[i].must_match /^(\s[1-9]|[12][0-9])\. .*/
|
96
101
|
}
|
97
102
|
}
|
@@ -99,6 +104,7 @@ describe Kat::App do
|
|
99
104
|
|
100
105
|
it 'downloads data from a URL' do
|
101
106
|
Kat::Colour.colour = false
|
107
|
+
|
102
108
|
app.instance_exec {
|
103
109
|
s = 'foobar'
|
104
110
|
result = download({ download: 'http://google.com', title: s })
|
@@ -110,6 +116,7 @@ describe Kat::App do
|
|
110
116
|
|
111
117
|
it 'returns an error message when a download fails' do
|
112
118
|
Kat::Colour.colour = false
|
119
|
+
|
113
120
|
app.instance_exec {
|
114
121
|
result = download({ download: 'http://foo.bar', title: 'foobar' })
|
115
122
|
result.must_be_instance_of Array
|
data/test/kat/test_colour.rb
CHANGED
@@ -23,6 +23,8 @@ describe Kat::Colour do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'colours strings' do
|
26
|
+
Kat::Colour.colour = true
|
27
|
+
|
26
28
|
colours.each_with_index { |c, i|
|
27
29
|
str = 'foobar'
|
28
30
|
result = "\e[0;#{ 30 + i }mfoobar\e[0m"
|
@@ -55,6 +57,8 @@ describe Kat::Colour do
|
|
55
57
|
end
|
56
58
|
|
57
59
|
it 'colours symbols' do
|
60
|
+
Kat::Colour.colour = true
|
61
|
+
|
58
62
|
colours.each_with_index { |c, i|
|
59
63
|
sym = :foobar
|
60
64
|
result = "\e[0;#{ 30 + i }mfoobar\e[0m"
|
@@ -85,6 +89,8 @@ describe Kat::Colour do
|
|
85
89
|
end
|
86
90
|
|
87
91
|
it 'colours arrays of strings and symbols' do
|
92
|
+
Kat::Colour.colour = true
|
93
|
+
|
88
94
|
colours.each_with_index { |c, i|
|
89
95
|
s = ['foobar', :foobar, nil, ['foobar', :foobar, nil]]
|
90
96
|
t = ['foobar', :foobar, nil, ['foobar', :foobar, nil]]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fission Xuiptz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: andand
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.3'
|
55
69
|
description: A Ruby interface to Kickass Torrents
|
56
70
|
email: fissionxuiptz@softwaremojo.com
|
57
71
|
executables:
|
@@ -76,7 +90,6 @@ files:
|
|
76
90
|
- lib/kat/version.rb
|
77
91
|
- test/kat/test_app.rb
|
78
92
|
- test/kat/test_colour.rb
|
79
|
-
- test/kat/test_field_map.rb
|
80
93
|
- test/kat/test_options.rb
|
81
94
|
- test/kat/test_search.rb
|
82
95
|
homepage: http://github.com/fissionxuiptz/kat
|
@@ -99,13 +112,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
112
|
version: '0'
|
100
113
|
requirements: []
|
101
114
|
rubyforge_project:
|
102
|
-
rubygems_version: 2.0.
|
115
|
+
rubygems_version: 2.0.14
|
103
116
|
signing_key:
|
104
117
|
specification_version: 4
|
105
118
|
summary: Kickass Torrents Interface
|
106
119
|
test_files:
|
107
120
|
- test/kat/test_app.rb
|
108
121
|
- test/kat/test_colour.rb
|
109
|
-
- test/kat/test_field_map.rb
|
110
122
|
- test/kat/test_options.rb
|
111
123
|
- test/kat/test_search.rb
|
data/test/kat/test_field_map.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
require File.dirname(__FILE__) + '/../../lib/kat/field_map'
|
3
|
-
|
4
|
-
describe Kat do
|
5
|
-
describe 'field map' do
|
6
|
-
let(:f) { Kat::FIELD_MAP }
|
7
|
-
|
8
|
-
it 'is a hash' do
|
9
|
-
f.must_be_instance_of Hash
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'has symbolised keys' do
|
13
|
-
f.keys.wont_be_empty
|
14
|
-
f.keys.each { |key|
|
15
|
-
key.must_be_instance_of Symbol
|
16
|
-
}
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'has a hash for values' do
|
20
|
-
f.values.wont_be_empty
|
21
|
-
f.values.each { |value|
|
22
|
-
value.must_be_instance_of Hash
|
23
|
-
}
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'has symbolised keys in each value' do
|
27
|
-
f.values.each { |value|
|
28
|
-
value.keys.wont_be_empty
|
29
|
-
value.keys.each { |key|
|
30
|
-
key.must_be_instance_of Symbol
|
31
|
-
case key
|
32
|
-
when :desc
|
33
|
-
value[key].must_be_instance_of String
|
34
|
-
when :multi, :check, :input
|
35
|
-
value[key].must_equal true
|
36
|
-
when :short
|
37
|
-
value[key].must_be_instance_of Symbol
|
38
|
-
value[key].must_match /\A([a-z]|none)\Z/
|
39
|
-
else
|
40
|
-
value[key].must_be_instance_of Symbol
|
41
|
-
end
|
42
|
-
}
|
43
|
-
}
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|