rack-weixin 0.0.3.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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+ source 'http://ruby.taobao.org'
3
+
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,44 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rack-weixin (0.0.3.1)
5
+ multi_xml (>= 0.5.2)
6
+ rack
7
+ roxml
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ remote: http://ruby.taobao.org/
12
+ specs:
13
+ activesupport (3.2.12)
14
+ i18n (~> 0.6)
15
+ multi_json (~> 1.0)
16
+ diff-lcs (1.1.3)
17
+ i18n (0.6.2)
18
+ multi_json (1.6.1)
19
+ multi_xml (0.5.3)
20
+ nokogiri (1.5.6)
21
+ rack (1.5.2)
22
+ rack-test (0.6.2)
23
+ rack (>= 1.0)
24
+ rake (10.0.3)
25
+ roxml (3.3.1)
26
+ activesupport (>= 2.3.0)
27
+ nokogiri (>= 1.3.3)
28
+ rspec (2.12.0)
29
+ rspec-core (~> 2.12.0)
30
+ rspec-expectations (~> 2.12.0)
31
+ rspec-mocks (~> 2.12.0)
32
+ rspec-core (2.12.2)
33
+ rspec-expectations (2.12.1)
34
+ diff-lcs (~> 1.1.3)
35
+ rspec-mocks (2.12.2)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ rack-test
42
+ rack-weixin!
43
+ rake
44
+ rspec (>= 2.0.0)
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ 微信公众平台 开放消息接口 Rack Middlware
2
+ ========================================
3
+
4
+ * 验证微信请求 with 'weixin/middleware'
5
+ * 解析推送消息 with 'weixin/model'
6
+ * 生成回复消息 with 'weixin/model'
7
+
8
+
9
+ Installation
10
+ ------------
11
+ ```
12
+ $ gem install rack-weixin
13
+ ```
14
+
15
+
16
+ Usage
17
+ -----
18
+
19
+ A sinatra demo
20
+
21
+ ```ruby
22
+ # -*- encoding : utf-8 -*-
23
+ require 'sinatra'
24
+ require 'weixin/middleware'
25
+ require 'weixin/model'
26
+
27
+ use Weixin::Middleware, 'your api token', '/your_app_root'
28
+
29
+ configure do
30
+ set :wx_id, 'your_weixin_account'
31
+ end
32
+
33
+ helpers do
34
+
35
+ def create_message(from, to, type, content, flag=0)
36
+ msg = Weixin::TextReplyMessage.new
37
+ msg.ToUserName = to
38
+ msg.FromUserName = from
39
+ msg.Content = content
40
+ msg.to_xml
41
+ end
42
+ end
43
+
44
+ get '/your_app_root' do
45
+ params[:echostr]
46
+ end
47
+
48
+ post '/your_app_root' do
49
+ content_type :xml, 'charset' => 'utf-8'
50
+
51
+ message = request.env[Weixin::Middleware::WEIXIN_MSG]
52
+ logger.info "message: #{request.env[Weixin::Middleware::WEIXIN_MSG_RAW]}"
53
+
54
+ from = message.FromUserName
55
+ if message.class == Weixin::TextMessage
56
+ content = message.Content
57
+ if content == 'Hello2BizUser'
58
+ reply_msg_content = "感谢关注!#{reply_msg_content}"
59
+ end
60
+ end
61
+
62
+ create_message(settings.wx_id, from, 'text', reply_msg_content)
63
+ end
64
+ ```
65
+
66
+ TODO
67
+ ----
68
+
69
+ Copyright
70
+ ---------
71
+
72
+ Permission is hereby granted, free of charge, to any person obtaining a copy
73
+ of this software and associated documentation files (the "Software"), to
74
+ deal in the Software without restriction, including without limitation the
75
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
76
+ sell copies of the Software, and to permit persons to whom the Software is
77
+ furnished to do so, subject to the following conditions:
78
+
79
+ The above copyright notice and this permission notice shall be included in
80
+ all copies or substantial portions of the Software.
81
+
82
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
83
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
84
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
85
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
86
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
87
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ desc 'Run all the tests'
5
+ task :default => [:test]
6
+
7
+ desc "Run all the tests"
8
+ task :test do
9
+ sh "rspec -Ilib/*:spec spec"
10
+ end
11
+
@@ -0,0 +1,62 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'digest/sha1'
3
+
4
+ module Weixin
5
+
6
+ class Middleware
7
+
8
+ POST_BODY = 'rack.input'.freeze
9
+ WEIXIN_MSG = 'weixin.msg'.freeze
10
+ WEIXIN_MSG_RAW = 'weixin.msg.raw'.freeze
11
+
12
+ def initialize(app, app_token, path)
13
+ @app = app
14
+ @app_token = app_token
15
+ @path = path
16
+ end
17
+
18
+ def call(env)
19
+ dup._call(env)
20
+ end
21
+
22
+ def _call(env)
23
+ if @path == env['PATH_INFO'].to_s && ['GET', 'POST'].include?(env['REQUEST_METHOD'])
24
+
25
+ @req = Rack::Request.new(env)
26
+ return invalid_request! unless request_is_valid?
27
+ return [
28
+ 200,
29
+ { 'Content-type' => 'text/plain', 'Content-length' => @req.params['echostr'].length.to_s },
30
+ [ @req.params['echostr'] ]
31
+ ] if @req.get?
32
+
33
+ raw_msg = env[POST_BODY].read
34
+ begin
35
+ env.update WEIXIN_MSG => Weixin::Message.factory(raw_msg), WEIXIN_MSG_RAW => env[POST_BODY]
36
+ @app.call(env)
37
+ rescue Exception => e
38
+ return [500, { 'Content-typ' => 'text/html' }, ["Message parsing error: #{e.to_s}"]]
39
+ end
40
+ else
41
+ @app.call(env)
42
+ end
43
+
44
+ end
45
+
46
+ def invalid_request!
47
+ [401, { 'Content-type' => 'text/html', 'Content-Length' => '0'}, []]
48
+ end
49
+
50
+ def request_is_valid?
51
+ begin
52
+ param_array = [@app_token, @req.params['timestamp'], @req.params['nonce']]
53
+ sign = Digest::SHA1.hexdigest( param_array.sort.join )
54
+ sign == @req.params['signature'] ? true : false
55
+ rescue
56
+ false
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+
@@ -0,0 +1,146 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'roxml'
3
+ require 'multi_xml'
4
+ require 'ostruct'
5
+
6
+ module Weixin
7
+
8
+ class Message
9
+
10
+ def initialize(hash)
11
+ @source = OpenStruct.new(hash)
12
+ end
13
+
14
+ def method_missing(method, *args, &block)
15
+ @source.send(method, *args, &block)
16
+ end
17
+
18
+ def CreateTime
19
+ @source.CreateTime.to_i
20
+ end
21
+
22
+ def MsgId
23
+ @source.MsgId.to_i
24
+ end
25
+
26
+ def Message.factory(xml)
27
+ hash = MultiXml.parse(xml)['xml']
28
+ case hash['MsgType']
29
+ when 'text'
30
+ TextMessage.new(hash)
31
+ when 'image'
32
+ ImageMessage.new(hash)
33
+ when 'location'
34
+ LocationMessage.new(hash)
35
+ when 'link'
36
+ LinkMessage.new(hash)
37
+ when 'event'
38
+ EventMessage.new(hash)
39
+ else
40
+ raise ArgumentError, 'Unknown Message'
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ TextMessage = Class.new(Message)
47
+ ImageMessage = Class.new(Message)
48
+ LinkMessage = Class.new(Message)
49
+
50
+ class LocationMessage < Message
51
+
52
+ def Location_X
53
+ @source.Location_X.to_f
54
+ end
55
+
56
+ def Location_Y
57
+ @source.Location_Y.to_f
58
+ end
59
+
60
+ def Scale
61
+ @source.Scale.to_i
62
+ end
63
+ end
64
+
65
+ class EventMessage < Message
66
+
67
+ def Latitude
68
+ @source.Latitude.to_f
69
+ end
70
+
71
+ def Longitude
72
+ @source.Longitude.to_f
73
+ end
74
+
75
+ def Precision
76
+ @source.Precision.to_f
77
+ end
78
+ end
79
+
80
+ class ReplyMessage
81
+ include ROXML
82
+ xml_name :xml
83
+ #xml_convention :camelcase
84
+
85
+ xml_accessor :ToUserName, :cdata => true
86
+ xml_accessor :FromUserName, :cdata => true
87
+ xml_reader :CreateTime, :as => Integer
88
+ xml_reader :MsgType, :cdata => true
89
+ xml_accessor :FuncFlag, :as => Integer
90
+
91
+ def initialize
92
+ @CreateTime = Time.now.to_i
93
+ @FuncFlag = 0
94
+ end
95
+
96
+ def to_xml
97
+ super.to_xml(:encoding => 'UTF-8', :indent => 0, :save_with => 0)
98
+ end
99
+ end
100
+
101
+ class TextReplyMessage < ReplyMessage
102
+ xml_accessor :Content, :cdata => true
103
+
104
+ def initialize
105
+ super
106
+ @MsgType = 'text'
107
+ end
108
+ end
109
+
110
+ class Music
111
+ include ROXML
112
+
113
+ xml_accessor :Title, :cdata => true
114
+ xml_accessor :Description, :cdata => true
115
+ xml_accessor :MusicUrl, :cdata => true
116
+ xml_accessor :HQMusicUrl, :cdata => true
117
+ end
118
+
119
+ class MusicReplyMessage < ReplyMessage
120
+ xml_accessor :Music, :as => Music
121
+
122
+ def initialize
123
+ super
124
+ @MsgType = 'music'
125
+ end
126
+ end
127
+
128
+ class Item
129
+ include ROXML
130
+
131
+ xml_accessor :Title, :cdata => true
132
+ xml_accessor :Description, :cdata => true
133
+ xml_accessor :PicUrl, :cdata => true
134
+ xml_accessor :Url, :cdata => true
135
+ end
136
+
137
+ class NewsReplyMessage < ReplyMessage
138
+ xml_accessor :ArticleCount, :as => Integer
139
+ xml_accessor :Articles, :as => [Item], :in => 'Articles', :from => 'Item'
140
+
141
+ def initialize
142
+ super
143
+ @MsgType = 'news'
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,3 @@
1
+ module Weixin
2
+ VERSION = "0.0.3.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "weixin/version"
3
+
4
+ Gem::Specification.new do |s|
5
+
6
+ s.name = 'rack-weixin'
7
+ s.version = Weixin::VERSION
8
+
9
+ s.description = 'Rack middleware for Weixin apps: message validation/parser/generator'
10
+ s.summary = 'Rack middleware for Weixin apps'
11
+
12
+ s.authors = ['wolfg1969']
13
+ s.email = 'wolfg1969@gmail.com'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = s.files.select {|path| path =~ /^spec\/.*_spec\.rb/}
17
+
18
+ s.add_dependency 'rack'
19
+ s.add_dependency 'multi_xml', '>= 0.5.2'
20
+ s.add_dependency 'roxml'
21
+ s.add_development_dependency 'rake'
22
+ s.add_development_dependency 'rack-test'
23
+ s.add_development_dependency 'rspec', '>= 2.0.0'
24
+
25
+ s.homepage = 'https://github.com/wolfg1969/rack-weixin'
26
+ s.require_paths = %w[lib]
27
+ end
@@ -0,0 +1,104 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'rspec'
3
+ require 'weixin/model'
4
+
5
+ describe 'weixin/model' do
6
+
7
+ context 'Weixin:Message' do
8
+ it 'is a text message' do
9
+ msg = Weixin::Message.factory(%(
10
+ <xml>
11
+ <ToUserName><![CDATA[to]]></ToUserName>
12
+ <FromUserName><![CDATA[from]]></FromUserName>
13
+ <CreateTime>1360391199</CreateTime>
14
+ <MsgType><![CDATA[text]]></MsgType>
15
+ <Content><![CDATA[Hello2BizUser]]></Content>
16
+ <MsgId>5842835709471227904</MsgId>
17
+ </xml>
18
+ ))
19
+ msg.class.should == Weixin::TextMessage
20
+ msg.MsgType.should == 'text'
21
+ msg.ToUserName.should == 'to'
22
+ msg.FromUserName.should == 'from'
23
+ msg.CreateTime.should == 1360391199
24
+ msg.Content.should == 'Hello2BizUser'
25
+ msg.MsgId.should == 5842835709471227904
26
+ end
27
+
28
+ it 'is a image message' do
29
+ msg = Weixin::Message.factory(%(
30
+ <xml>
31
+ <ToUserName><![CDATA[to]]></ToUserName>
32
+ <FromUserName><![CDATA[from]]></FromUserName>
33
+ <CreateTime>1360391199</CreateTime>
34
+ <MsgType><![CDATA[image]]></MsgType>
35
+ <PicUrl><![CDATA[http://mmsns.qpic.cn/mmsns/Leiaa5NQF4FOTOSo3hXrEsGsodU2jHcWZiaInTxmTh6GaCIJ8hBHicIDA/0]]></PicUrl>
36
+ <MsgId>5842835709471227904</MsgId>
37
+ </xml>
38
+ ))
39
+ msg.class.should == Weixin::ImageMessage
40
+ msg.MsgType.should == 'image'
41
+ msg.ToUserName.should == 'to'
42
+ msg.FromUserName.should == 'from'
43
+ msg.CreateTime.should == 1360391199
44
+ msg.MsgId.should == 5842835709471227904
45
+ msg.PicUrl.should_not nil
46
+ end
47
+
48
+ it 'is a location message' do
49
+ msg = Weixin::Message.factory(%(
50
+ <xml>
51
+ <ToUserName><![CDATA[to]]></ToUserName>
52
+ <FromUserName><![CDATA[from]]></FromUserName>
53
+ <CreateTime>1360391199</CreateTime>
54
+ <MsgType><![CDATA[location]]></MsgType>
55
+ <Location_X>69.866013</Location_X>
56
+ <Location_Y>136.269449</Location_Y>
57
+ <Scale>15</Scale>
58
+ <Label><![CDATA[somewhere]]></Label>
59
+ <MsgId>5842835709471227904</MsgId>
60
+ </xml>
61
+ ))
62
+ msg.class.should == Weixin::LocationMessage
63
+ msg.MsgType.should == 'location'
64
+ msg.ToUserName.should == 'to'
65
+ msg.FromUserName.should == 'from'
66
+ msg.CreateTime.should == 1360391199
67
+ msg.MsgId.should == 5842835709471227904
68
+ msg.Label.should_not nil
69
+ msg.Scale.should == 15
70
+ msg.Location_X.should == 69.866013
71
+ msg.Location_Y.should == 136.269449
72
+ end
73
+
74
+ end
75
+
76
+ context 'Weixin::ReplyMessage' do
77
+
78
+ it 'is a text reply message' do
79
+ msg = Weixin::TextReplyMessage.new
80
+ msg.ToUserName = 'to'
81
+ msg.FromUserName = 'from'
82
+ msg.Content = 'blah'
83
+ msg.MsgType.should == 'text'
84
+ end
85
+
86
+ it 'is a news reply message' do
87
+ msg = Weixin::NewsReplyMessage.new
88
+ msg.ToUserName = 'to'
89
+ msg.FromUserName = 'from'
90
+ item1 = Weixin::Item.new
91
+ item1.Title = 'title1'
92
+ item1.Description = 'blah'
93
+ item2 = Weixin::Item.new
94
+ item2.Title = 'title2'
95
+ item2.Description = 'blah blah'
96
+ msg.Articles = [item1, item2]
97
+ msg.ArticleCount = 2
98
+ msg.MsgType.should == 'news'
99
+ #puts msg.to_xml
100
+ end
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,55 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'rspec'
3
+ require 'rack'
4
+ require 'rack/test'
5
+ require 'rack/mock'
6
+ require 'digest/sha1'
7
+ require 'weixin/middleware'
8
+
9
+ describe "Weixin::Middleware" do
10
+
11
+ include Rack::Test::Methods
12
+
13
+ before(:all) do
14
+ @app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ['hello']] }
15
+ @app_token = 'mytoken'
16
+ @context_path = '/'
17
+ end
18
+
19
+ def middleware()
20
+ Weixin::Middleware.new @app, @app_token, @context_path
21
+ end
22
+
23
+ def mock_env(echostr, token = @app_token, path = '/')
24
+ timestamp = Time.now.to_i.to_s
25
+ nonce = '123456'
26
+ param_array = [token, timestamp, nonce]
27
+ sign = Digest::SHA1.hexdigest( param_array.sort.join )
28
+ url = "#{path}?echostr=#{echostr}&timestamp=#{timestamp}&nonce=#{nonce}&signature=#{sign}"
29
+ Rack::MockRequest.env_for(url)
30
+ end
31
+
32
+ it 'does not match the path info' do
33
+ app = middleware
34
+ echostr = '123'
35
+ status, headers, body = app.call mock_env(echostr, @app_token, '/not_weixin_app')
36
+ status.should eq(200)
37
+ body.should_not == [echostr]
38
+ end
39
+
40
+ it 'is valid weixin request' do
41
+ app = middleware
42
+ echostr = '123'
43
+ status, headers, body = app.call mock_env(echostr)
44
+ status.should eq(200)
45
+ body.should == [echostr]
46
+ end
47
+
48
+ it 'is invalid weixin request' do
49
+ app = middleware
50
+ status, headers, body = app.call mock_env('123', 'wrong_token')
51
+ status.should eq(401)
52
+ body.should == []
53
+ end
54
+
55
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-weixin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - wolfg1969
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: multi_xml
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.5.2
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.5.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: roxml
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rack-test
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: 2.0.0
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 2.0.0
110
+ description: ! 'Rack middleware for Weixin apps: message validation/parser/generator'
111
+ email: wolfg1969@gmail.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - .gitignore
117
+ - Gemfile
118
+ - Gemfile.lock
119
+ - README.md
120
+ - Rakefile
121
+ - lib/weixin/middleware.rb
122
+ - lib/weixin/model.rb
123
+ - lib/weixin/version.rb
124
+ - rack-weixin.gemspec
125
+ - spec/model_spec.rb
126
+ - spec/rack_middleware_spec.rb
127
+ homepage: https://github.com/wolfg1969/rack-weixin
128
+ licenses: []
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ segments:
140
+ - 0
141
+ hash: -95414285
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ! '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ segments:
149
+ - 0
150
+ hash: -95414285
151
+ requirements: []
152
+ rubyforge_project:
153
+ rubygems_version: 1.8.24
154
+ signing_key:
155
+ specification_version: 3
156
+ summary: Rack middleware for Weixin apps
157
+ test_files:
158
+ - spec/model_spec.rb
159
+ - spec/rack_middleware_spec.rb