rack-weixin 0.0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +44 -0
- data/README.md +87 -0
- data/Rakefile +11 -0
- data/lib/weixin/middleware.rb +62 -0
- data/lib/weixin/model.rb +146 -0
- data/lib/weixin/version.rb +3 -0
- data/rack-weixin.gemspec +27 -0
- data/spec/model_spec.rb +104 -0
- data/spec/rack_middleware_spec.rb +55 -0
- metadata +159 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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,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
|
+
|
data/lib/weixin/model.rb
ADDED
@@ -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
|
data/rack-weixin.gemspec
ADDED
@@ -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
|
data/spec/model_spec.rb
ADDED
@@ -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}×tamp=#{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
|