russian_post 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +29 -0
- data/README.md +29 -0
- data/Rakefile +10 -0
- data/lib/russian_post/captcha/characters.dat +0 -0
- data/lib/russian_post/captcha/pattern.rb +26 -0
- data/lib/russian_post/captcha/patterns.rb +67 -0
- data/lib/russian_post/captcha.rb +99 -0
- data/lib/russian_post/russian_post.rb +11 -0
- data/lib/russian_post/version.rb +3 -0
- data/russian_post.gemspec +28 -0
- data/test/fixtures/captcha.png +0 -0
- data/test/russian_post/captcha/pattern_test.rb +19 -0
- data/test/russian_post/captcha/patterns_test.rb +20 -0
- data/test/russian_post/captcha_test.rb +40 -0
- data/test/test_helper.rb +23 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2b004d7e8725e3d084956ead13b4e170b135309a
|
4
|
+
data.tar.gz: 1e9a5e5a24281d852341963fc6d7270602cc2822
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cc8680522729c6b95a06754a8a213610af79a77cc2ab12f9d6e4106cde61dd5e4b0e864b150f228ae3ba3198c214602d8d256ac17ddf0a953e9c1bcfaede08c9
|
7
|
+
data.tar.gz: f62d87d7981d586d74b5968e3f3e6a845bfd3503366b38ff5af093dca3057701dcf55dfc9ce7ef72e162b2f4e670389fe77b6443177fd36b7b7464589fd98365
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
russian_post (0.0.1)
|
5
|
+
chunky_png (~> 1.2)
|
6
|
+
excon (~> 0.20)
|
7
|
+
nokogiri (~> 1.5)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
addressable (2.3.3)
|
13
|
+
chunky_png (1.2.7)
|
14
|
+
crack (0.3.2)
|
15
|
+
excon (0.20.1)
|
16
|
+
nokogiri (1.5.9)
|
17
|
+
rake (10.0.3)
|
18
|
+
webmock (1.11.0)
|
19
|
+
addressable (>= 2.2.7)
|
20
|
+
crack (>= 0.3.2)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
bundler (~> 1.3)
|
27
|
+
rake
|
28
|
+
russian_post!
|
29
|
+
webmock (~> 1.11)
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Russian Post Tools
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
gem 'russian_post'
|
8
|
+
|
9
|
+
And then execute:
|
10
|
+
|
11
|
+
$ bundle
|
12
|
+
|
13
|
+
Or install it yourself as:
|
14
|
+
|
15
|
+
$ gem install russian_post
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
### Captcha recognizer
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'russian_post'
|
23
|
+
|
24
|
+
RussianPost::Captcha.for_url(captcha_url).text
|
25
|
+
# => 96950
|
26
|
+
|
27
|
+
RussianPost::Captcha.for_data(png_blob).text
|
28
|
+
# => 43455
|
29
|
+
```
|
data/Rakefile
ADDED
Binary file
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
class RussianPost::Captcha::Pattern
|
3
|
+
|
4
|
+
TARGET_MATCH_RATE = 1.0
|
5
|
+
|
6
|
+
attr_reader :points, :character
|
7
|
+
|
8
|
+
def initialize(points, character)
|
9
|
+
@points, @character = points, character
|
10
|
+
end
|
11
|
+
|
12
|
+
def match?(image, x, y)
|
13
|
+
matching = 0
|
14
|
+
|
15
|
+
points.each do |px, py|
|
16
|
+
cx, cy = x + px, y + py
|
17
|
+
|
18
|
+
break if cx >= image.width || cy >= image.height || image[cx, cy] == 255
|
19
|
+
|
20
|
+
matching += 1
|
21
|
+
end
|
22
|
+
|
23
|
+
matching / points.size >= TARGET_MATCH_RATE
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'russian_post/captcha/pattern'
|
2
|
+
|
3
|
+
class RussianPost::Captcha::Patterns
|
4
|
+
attr_reader :patterns
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def built_in
|
9
|
+
@builtin_patterns ||= RussianPost::Captcha::Patterns.new(File.expand_path('characters.dat', File.dirname(__FILE__)))
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(file)
|
15
|
+
@patterns = load_patterns(file)
|
16
|
+
end
|
17
|
+
|
18
|
+
def each(&block)
|
19
|
+
@patterns.each(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(pattern)
|
23
|
+
@patterns << pattern
|
24
|
+
end
|
25
|
+
|
26
|
+
def find(image, x, y)
|
27
|
+
each do |pattern|
|
28
|
+
return pattern if pattern.match?(image, x, y)
|
29
|
+
end
|
30
|
+
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def serialize(patterns)
|
37
|
+
array_hash = Hash.new{ |hash, key| hash[key] = []}
|
38
|
+
|
39
|
+
data = patterns.inject(array_hash) do |result, pattern|
|
40
|
+
result[pattern.character] << pattern.points
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
Marshal.dump(data)
|
45
|
+
end
|
46
|
+
|
47
|
+
def deserialize(data)
|
48
|
+
raw_data = Marshal.load(data)
|
49
|
+
|
50
|
+
raw_data.inject([]) do |result, data|
|
51
|
+
character, patterns = data
|
52
|
+
|
53
|
+
result += patterns.map { |points| RussianPost::Captcha::Pattern.new(points, character) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def save_patterns(file, patterns)
|
58
|
+
File.open(file, 'wb') { |file| file.write(serialize(patterns)) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def load_patterns(file)
|
62
|
+
raise "Specified patterns file not exists (#{file})" unless File.exists?(file)
|
63
|
+
|
64
|
+
deserialize(File.open(file, 'rb') { |file| file.read })
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'excon'
|
2
|
+
require 'chunky_png'
|
3
|
+
|
4
|
+
module RussianPost
|
5
|
+
|
6
|
+
class Captcha
|
7
|
+
|
8
|
+
require 'russian_post/captcha/patterns'
|
9
|
+
|
10
|
+
attr_reader :patterns
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def for_url(url, patterns = nil)
|
15
|
+
RussianPost::Captcha.new(url, nil, patterns)
|
16
|
+
end
|
17
|
+
|
18
|
+
def for_data(data, patterns = nil)
|
19
|
+
RussianPost::Captcha.new(nil, data, patterns)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def image
|
25
|
+
@data ||= fetch_image
|
26
|
+
end
|
27
|
+
|
28
|
+
def text
|
29
|
+
@recognized_text ||= prepare_text(recognize)
|
30
|
+
end
|
31
|
+
|
32
|
+
def valid?
|
33
|
+
text.size == 5
|
34
|
+
end
|
35
|
+
|
36
|
+
def recognize
|
37
|
+
@recognize ||= recognize!
|
38
|
+
end
|
39
|
+
|
40
|
+
def recognize!
|
41
|
+
|
42
|
+
captcha_image = grayscale(image)
|
43
|
+
|
44
|
+
results = {}
|
45
|
+
|
46
|
+
for x in 0..captcha_image.width - 2
|
47
|
+
for y in 0..captcha_image.height - 2
|
48
|
+
result = patterns.find(captcha_image, x, y)
|
49
|
+
|
50
|
+
results[x] = result if result
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Hash[results.sort].values
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def initialize(url = nil, data = nil, patterns = nil) # private constructor
|
60
|
+
@url = url
|
61
|
+
@data = ChunkyPNG::Image.from_blob(data) if data
|
62
|
+
@patterns ||= RussianPost::Captcha::Patterns.built_in
|
63
|
+
end
|
64
|
+
|
65
|
+
def prepare_text(results)
|
66
|
+
results.map(&:character).join('')
|
67
|
+
end
|
68
|
+
|
69
|
+
def grayscale(captcha_image)
|
70
|
+
captcha_image.pixels.map! { |color| color = ChunkyPNG::Color.grayscale_teint(color); color = (color >= 255) ? 255 : 0 ; color }
|
71
|
+
|
72
|
+
captcha_image
|
73
|
+
end
|
74
|
+
|
75
|
+
def fetch_image
|
76
|
+
raise "URL not specified" unless @url
|
77
|
+
|
78
|
+
data = Excon.get(@url).body
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
unless data.start_with?("\x89PNG".force_encoding("ASCII-8BIT")) # not png header
|
83
|
+
if data =~ /<input id=\"key\" name=\"key\" value=\"([0-9]+)\"\/>/ # tough security huh
|
84
|
+
data = Excon.post(@url, body: "key=#{$1}").body
|
85
|
+
end
|
86
|
+
|
87
|
+
if data =~ /Object moved/ # faulty captcha
|
88
|
+
raise "Russian Post captcha service error"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
File.open('test.png', 'wb') { |f| f.write(data) }
|
93
|
+
|
94
|
+
ChunkyPNG::Image.from_blob(data)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'russian_post/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "russian_post"
|
9
|
+
spec.version = RussianPost::VERSION
|
10
|
+
spec.authors = ["t3hk0d3"]
|
11
|
+
spec.email = ["clouster@yandex.ru"]
|
12
|
+
spec.description = %q{RussianPost tools}
|
13
|
+
spec.summary = %q{RussianPost tools}
|
14
|
+
spec.homepage = "http://github.com/t3hk0d3/russian_post"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "excon", "~> 0.20"
|
22
|
+
spec.add_dependency "chunky_png", "~> 1.2"
|
23
|
+
spec.add_dependency "nokogiri", "~> 1.5"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "webmock", "~> 1.11"
|
28
|
+
end
|
Binary file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RussianPost::Captcha::PatternTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@image = load_captcha_image
|
7
|
+
|
8
|
+
@points = [[5, 0], [6, 0], [7, 0], [8, 0], [10, 0], [4, 1], [10, 1], [10, 2], [10, 3], [5, 4], [10, 4], [1, 5], [5, 5], [10, 5],
|
9
|
+
[5, 6], [10, 6], [0, 7], [5, 7], [0, 8], [3, 8], [4, 8], [0, 9], [11, 9], [11, 10], [0, 11], [1, 11], [2, 11], [3, 11], [5, 11],
|
10
|
+
[5, 12], [10, 13], [5, 14], [6, 14], [7, 14], [8, 14], [9, 14]]
|
11
|
+
|
12
|
+
@pattern = RussianPost::Captcha::Pattern.new(@points, '4')
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_find_pattern
|
16
|
+
assert @pattern.match?(@image, 4, 3)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RussianPost::Captcha::PatternsTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@image = load_captcha_image
|
7
|
+
|
8
|
+
@patterns = RussianPost::Captcha::Patterns.built_in # @FIXME use mock patterns instead
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_builtin_patterns
|
13
|
+
assert @patterns.patterns.size > 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_find
|
17
|
+
assert_equal '4', @patterns.find(@image, 4, 3).character
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module RussianPost
|
4
|
+
|
5
|
+
class CaptchaTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@url = 'http://www.russianpost.ru/CaptchaService/CaptchaImage.ashx?Id=361256433'
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_recognize
|
12
|
+
captcha = RussianPost::Captcha.for_data(load_captcha_file)
|
13
|
+
|
14
|
+
assert_equal '49396', captcha.text
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_fetch
|
18
|
+
stub_get = stub_request(:get, @url).to_return(:body => load_captcha_file, :headers => { 'Content-Type' => 'image/png' })
|
19
|
+
|
20
|
+
RussianPost::Captcha.for_url(@url).recognize!
|
21
|
+
|
22
|
+
assert_requested(stub_get)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_cunning_fetch
|
26
|
+
@tough_security = "<html><head></head><body onload=\"document.myform.submit();\"><form method=\"post\" name=\"myform\" style=\"visibility:hidden;\"><input id=\"key\" name=\"key\" value=\"117413\"/><input type=\"submit\"/></form></body></html>\r\n\r\n"
|
27
|
+
|
28
|
+
stub_get = stub_request(:get, @url).to_return(:body => @tough_security, :headers => { 'Content-Type' => 'text/html' })
|
29
|
+
stub_post = stub_request(:post, @url).with(:body => 'key=117413').to_return(:body => load_captcha_file, :headers => { 'Content-Type' => 'image/png' })
|
30
|
+
|
31
|
+
RussianPost::Captcha.for_url(@url).recognize!
|
32
|
+
|
33
|
+
assert_requested(stub_get)
|
34
|
+
assert_requested(stub_post)
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.unshift File.expand_path("../lib/russian_post/", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'webmock/test_unit'
|
5
|
+
|
6
|
+
require 'russian_post'
|
7
|
+
|
8
|
+
class Test::Unit::TestCase
|
9
|
+
|
10
|
+
def load_captcha_file
|
11
|
+
File.open('test/fixtures/captcha.png', 'rb') { |f| f.read }
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def load_captcha_image
|
16
|
+
image = ChunkyPNG::Image.from_file('test/fixtures/captcha.png')
|
17
|
+
image.pixels.map! { |color| color = ChunkyPNG::Color.grayscale_teint(color); color = (color >= 255) ? 255 : 0 ; color }
|
18
|
+
|
19
|
+
image
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: russian_post
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- t3hk0d3
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: excon
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.20'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.20'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: chunky_png
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: webmock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.11'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.11'
|
97
|
+
description: RussianPost tools
|
98
|
+
email:
|
99
|
+
- clouster@yandex.ru
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- Gemfile
|
106
|
+
- Gemfile.lock
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- lib/russian_post/captcha.rb
|
110
|
+
- lib/russian_post/captcha/characters.dat
|
111
|
+
- lib/russian_post/captcha/pattern.rb
|
112
|
+
- lib/russian_post/captcha/patterns.rb
|
113
|
+
- lib/russian_post/russian_post.rb
|
114
|
+
- lib/russian_post/version.rb
|
115
|
+
- russian_post.gemspec
|
116
|
+
- test/fixtures/captcha.png
|
117
|
+
- test/russian_post/captcha/pattern_test.rb
|
118
|
+
- test/russian_post/captcha/patterns_test.rb
|
119
|
+
- test/russian_post/captcha_test.rb
|
120
|
+
- test/test_helper.rb
|
121
|
+
homepage: http://github.com/t3hk0d3/russian_post
|
122
|
+
licenses:
|
123
|
+
- MIT
|
124
|
+
metadata: {}
|
125
|
+
post_install_message:
|
126
|
+
rdoc_options: []
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubyforge_project:
|
141
|
+
rubygems_version: 2.0.3
|
142
|
+
signing_key:
|
143
|
+
specification_version: 4
|
144
|
+
summary: RussianPost tools
|
145
|
+
test_files:
|
146
|
+
- test/fixtures/captcha.png
|
147
|
+
- test/russian_post/captcha/pattern_test.rb
|
148
|
+
- test/russian_post/captcha/patterns_test.rb
|
149
|
+
- test/russian_post/captcha_test.rb
|
150
|
+
- test/test_helper.rb
|
151
|
+
has_rdoc:
|