sinatra-wechat 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f3056a6b2bb8a22c428fd91e3ed597dd54063374
4
+ data.tar.gz: a50373853d8165e3aa3e8ee10caf6a7aa747d0fa
5
+ SHA512:
6
+ metadata.gz: 336bfb4a2f8f5fd8dcc5c14555bbd0c24170dcbe5bb57906f733bddf61254343f4fbfe4ecc33b9e8f76f3b7b935beea2e664876234c00fcc607cf5a94057d7a0
7
+ data.tar.gz: 3264fb34bdbdd9bd3bb479fc0ebc5cf0793771655f5dbe29cef37e111e463e15132c761b625ee3b5236fb86a0d41f8a86a09b3d7bef6e50c866a19d29b7eea6c
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Lu, Jun
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Sinatra Wechat extension
2
+ [![Build Status](https://travis-ci.org/luj1985/sinatra-wechat.svg?branch=master)](https://travis-ci.org/luj1985/sinatra-wechat)
3
+
4
+ This extension is used to support [Tencent Wechat](https://mp.weixin.qq.com/) development mode.
5
+
6
+ ## Installation
7
+
8
+ $ gem install sinatra-wechat
9
+
10
+ # Usage
11
+
12
+ > use `disable :message_validation` to prevent message validation, otherwise need to add signature to the URL.
13
+
14
+ ```ruby
15
+ # app.rb
16
+ require 'sinatra'
17
+ require 'sinatra/wechat'
18
+
19
+ disable :message_validation
20
+ set :wechat_token, 'test-token'
21
+
22
+ wechat('/') {
23
+ text(:content => %r{\d+}) {
24
+ content_type 'application/xml'
25
+ erb :hello, :locals => request[:wechat_values]
26
+ }
27
+ }
28
+
29
+ __END__
30
+ @@ hello
31
+ <xml>
32
+ <ToUserName><%= from_user_name %></ToUserName>
33
+ <FromUserName><%= to_user_name %></FromUserName>
34
+ <CreateTime><%= Time.now.to_i %></CreateTime>
35
+ <MsgType>text</MsgType>
36
+ <Content><![CDATA[你好]]></Content>
37
+ </xml>
38
+ ```
39
+
40
+ start server:
41
+ ```sheel
42
+ $ ruby app.rb
43
+ ```
44
+
45
+ Test via [cURL](http://curl.haxx.se):
46
+
47
+ ```shell
48
+ $ curl -X POST --data '<xml>
49
+ <ToUserName>tousername</ToUserName>
50
+ <FromUserName>fromusername</FromUserName>
51
+ <CreateTime>1348831860</CreateTime>
52
+ <MsgType>text</MsgType>
53
+ <Content>1345 match number</Content>
54
+ <MsgId>1234567890123456</MsgId>
55
+ </xml>' http://localhost:4567
56
+ ```
57
+
58
+ The response is like:
59
+ ``` xml
60
+ <xml>
61
+ <ToUserName>fromusername</ToUserName>
62
+ <FromUserName>tousername</FromUserName>
63
+ <CreateTime>1405790652</CreateTime>
64
+ <MsgType>text</MsgType>
65
+ <Content><![CDATA[你好]]></Content>
66
+ </xml>
67
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'bundler/gem_tasks'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = ['--color', '--format', 'documentation']
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,78 @@
1
+ require 'sinatra'
2
+ require 'sinatra/wechat'
3
+ require 'nokogiri'
4
+
5
+ disable :message_validation
6
+ set :wechat_token, 'test-token'
7
+
8
+ location_event_reply = proc {
9
+ content_type 'application/xml'
10
+ values = request[:wechat_values]
11
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
12
+ xml.xml {
13
+ xml.ToUserName values[:from_user_name]
14
+ xml.FromUserName values[:to_user_name]
15
+ xml.CreateTime Time.now.to_i
16
+ xml.MsgType "text"
17
+ xml.Content 'This is a location response'
18
+ }
19
+ end
20
+ builder.to_xml
21
+ }
22
+
23
+ wechat('/wechat') {
24
+ location {
25
+ instance_eval &location_event_reply
26
+ }
27
+ text(:content => %r/\d+/) {
28
+ content_type 'application/xml'
29
+ erb :text_response, :locals => request[:wechat_values]
30
+ }
31
+ image(lambda { |values| values[:from_user_name] == 'test' }) {
32
+ content_type 'application/xml'
33
+ values = request[:wechat_values]
34
+ replies = [{
35
+ :title => 'artitle 1 title',
36
+ :description => 'article 1 description',
37
+ :pic_url => 'http://www.xxxx.com/yyy.jpg',
38
+ :url => 'http://www.xxxx.com/yyy.html'
39
+ }, {
40
+ :title => 'artitle 2 title',
41
+ :description => 'article 2 description',
42
+ :pic_url => 'http://zzz.com/foo.png',
43
+ :url => 'http://zzz.com/bar.html'
44
+ }]
45
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
46
+ xml.xml {
47
+ xml.ToUserName values[:from_user_name]
48
+ xml.FromUserName values[:to_user_name]
49
+ xml.CreateTime Time.now.to_i
50
+ xml.MsgType "news"
51
+ xml.ArticleCount replies.length
52
+ xml.Articles {
53
+ replies.each do |reply|
54
+ xml.item {
55
+ xml.Title reply.title
56
+ xml.Description reply.description
57
+ xml.PicUrl reply.pic_url
58
+ xml.Url reply.url
59
+ }
60
+ end
61
+ }
62
+ }
63
+ end
64
+ builder.to_xml
65
+ }
66
+ }
67
+
68
+
69
+ __END__
70
+ @@ text_response
71
+
72
+ <xml>
73
+ <ToUserName><%= from_user_name %></ToUserName>
74
+ <FromUserName><%= to_user_name %></FromUserName>
75
+ <CreateTime><%= Time.now.to_i %></CreateTime>
76
+ <MsgType>text</MsgType>
77
+ <Content><![CDATA[你好]]></Content>
78
+ </xml>
@@ -0,0 +1,5 @@
1
+ module Sinatra
2
+ module Wechat
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,88 @@
1
+ require 'sinatra/base'
2
+ require 'blankslate'
3
+ require 'nokogiri'
4
+
5
+ module Sinatra
6
+ module Wechat
7
+ module EndpointActions
8
+ class WechatDispatcher < ::BlankSlate
9
+ def initialize
10
+ super
11
+ @message_handlers = {}
12
+ end
13
+
14
+ def method_missing(sym, *args, &block)
15
+ @message_handlers[sym] ||= []
16
+ matchers = args.collect do |v|
17
+ if v.respond_to?(:call) then lambda { |values| v.call(values) }
18
+ # for named parameters
19
+ elsif v.respond_to?(:all?) then lambda { |values| v.all? { |k,v| v === values[k]} }
20
+ else raise TypeError, "\"#{v} (#{v.class})\" is not an acceptable condition"
21
+ end
22
+ end
23
+ matcher = lambda do |values|
24
+ matchers.all? {|m| m.call(values)}
25
+ end
26
+ @message_handlers[sym] << [ matcher, block ]
27
+ end
28
+
29
+ def route!(values)
30
+ type = values[:msg_type].to_sym
31
+ handlers = @message_handlers[type] || []
32
+ _, handler = handlers.find { |m, _| m.call(values) }
33
+ handler
34
+ end
35
+ end
36
+
37
+ def wechat(endpoint = '/', &block)
38
+ dispatcher = WechatDispatcher.new
39
+ dispatcher.instance_eval &block
40
+
41
+ get endpoint do
42
+ halt 403 unless validate_messages
43
+ content_type 'text/plain'
44
+ params[:echostr]
45
+ end
46
+
47
+ post endpoint do
48
+ halt 403 unless validate_messages
49
+
50
+ body = request.body.read || ""
51
+ halt 501 if body.empty?
52
+
53
+ doc = Nokogiri::XML(body).root
54
+ values = doc.element_children.each_with_object(Hash.new) do |e, v|
55
+ name = e.name.gsub(/(.)([A-Z])/,'\1_\2').downcase
56
+ # rename 'Location_X' to 'location__x' then to 'location_x'
57
+ name = name.gsub(/(_{2,})/,'_')
58
+ v[name.to_sym] = e.content
59
+ end
60
+ handler = dispatcher.route!(values)
61
+ halt 501 unless handler
62
+
63
+ request[:wechat_values] = values
64
+ instance_eval(&handler)
65
+ end
66
+ self
67
+ end
68
+ end
69
+
70
+ def self.registered(app)
71
+ app.extend(Wechat::EndpointActions)
72
+
73
+ app.helpers do
74
+ enable :message_validation
75
+
76
+ def validate_messages
77
+ token = settings.wechat_token || ""
78
+ raw = [token, params[:timestamp], params[:nonce]].compact.sort.join
79
+ settings.message_validation ? Digest::SHA1.hexdigest(raw) == params[:signature] : true
80
+ end
81
+ end
82
+ # expose to classic style
83
+ Delegator.delegate(:wechat)
84
+ end
85
+ end
86
+
87
+ register Wechat
88
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sinatra/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sinatra-wechat"
8
+ spec.version = Sinatra::Wechat::VERSION
9
+ spec.authors = ["Lu, Jun"]
10
+ spec.email = ["luj1985@gmail.com"]
11
+ spec.summary = "Sinatra extension for Tencent Wechat"
12
+ spec.description = "Provide Extensible Sinatra API to support Wechat development mode"
13
+ spec.homepage = "https://github.com/luj1985/sinatra-wechat"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0") - %w[.gitignore .travis.yml]
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "bundler", "~> 1.6"
22
+ spec.add_dependency "sinatra", "~> 1.4"
23
+ spec.add_dependency "nokogiri"
24
+ spec.add_dependency "blankslate"
25
+
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "rack-test"
29
+ spec.add_development_dependency "coveralls"
30
+ end
@@ -0,0 +1,10 @@
1
+ require 'rack/test'
2
+ require 'rspec'
3
+
4
+ require 'sinatra'
5
+ require 'sinatra/wechat'
6
+
7
+ require 'coveralls'
8
+ Coveralls.wear!
9
+
10
+ set :environment, :test
@@ -0,0 +1,282 @@
1
+ require File.expand_path '../spec_helper.rb', __FILE__
2
+
3
+ describe Sinatra::Wechat do
4
+ include Rack::Test::Methods
5
+
6
+ it "GET should have message verification" do
7
+
8
+ def app
9
+ @instance = Sinatra.new do
10
+ set :wechat_token, 'test-token'
11
+ register Sinatra::Wechat
12
+ end
13
+ @instance.wechat { }
14
+ end
15
+
16
+ get '/'
17
+ expect(last_response.status).to eq(403)
18
+
19
+ get '/', {:timestamp => '201407191804',
20
+ :nonce => 'nonce',
21
+ :signature => '9a91a1cea1cb60b87a9abb29dae06dce14721258',
22
+ :echostr => 'echo string'}
23
+ expect(last_response.status).to eq(200)
24
+ expect(last_response.body).to eq('echo string')
25
+
26
+ @instance.disable :message_validation
27
+ get '/'
28
+ expect(last_response.status).to eq(200)
29
+ end
30
+
31
+ it "POST should have message verification" do
32
+ def app
33
+ @instance = Sinatra.new do
34
+ set :wechat_token, 'test-token'
35
+ register Sinatra::Wechat
36
+ end
37
+ @instance.wechat {
38
+ text { 'text response' }
39
+ }
40
+ end
41
+ post '/'
42
+ expect(last_response.status).to eq(403)
43
+
44
+ body = <<-EOF
45
+ <xml>
46
+ <ToUserName>tousername</ToUserName>
47
+ <FromUserName>fromusername</FromUserName>
48
+ <CreateTime>1348831860</CreateTime>
49
+ <MsgType>text</MsgType>
50
+ <Content>This is the message content</Content>
51
+ <MsgId>1234567890123456</MsgId>
52
+ </xml>
53
+ EOF
54
+
55
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', body
56
+ expect(last_response.status).to eq(200)
57
+ expect(last_response.body).to eq('text response')
58
+ end
59
+
60
+ it "can switch wechat endpoint" do
61
+ def app
62
+ @instance = Sinatra.new do
63
+ set :wechat_token, 'test-token'
64
+ register Sinatra::Wechat
65
+ end
66
+ @instance.wechat('/wechat') {
67
+ image { 'relocated response' }
68
+ }
69
+ end
70
+ body = <<-EOF
71
+ <xml>
72
+ <ToUserName><![CDATA[toUser]]></ToUserName>
73
+ <FromUserName><![CDATA[fromUser]]></FromUserName>
74
+ <CreateTime>1348831860</CreateTime>
75
+ <MsgType><![CDATA[image]]></MsgType>
76
+ <PicUrl><![CDATA[this is a url]]></PicUrl>
77
+ <MediaId><![CDATA[media_id]]></MediaId>
78
+ <MsgId>1234567890123456</MsgId>
79
+ </xml>
80
+ EOF
81
+
82
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', body
83
+ expect(last_response.status).to eq(404)
84
+
85
+ post '/wechat?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', body
86
+ expect(last_response.status).to eq(200)
87
+ expect(last_response.body).to eq('relocated response')
88
+
89
+ end
90
+
91
+ it "should accept wechat message push" do
92
+ def app
93
+ @instance = Sinatra.new do
94
+ set :wechat_token, 'test-token'
95
+ register Sinatra::Wechat
96
+ end
97
+ @instance.wechat {
98
+ text(:content => %r{regex match}) { 'regex match' }
99
+ text(lambda {|values| values[:content] == 'function match'}) { 'function match' }
100
+ text { 'default match' }
101
+ voice {
102
+ values = request[:wechat_values]
103
+ values[:msg_id]
104
+ }
105
+ location {
106
+ values = request[:wechat_values]
107
+ values[:label]
108
+ }
109
+ }
110
+ end
111
+
112
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
113
+ <xml>
114
+ <ToUserName>tousername</ToUserName>
115
+ <FromUserName>fromusername</FromUserName>
116
+ <CreateTime>1348831860</CreateTime>
117
+ <MsgType>text</MsgType>
118
+ <Content>test regex match ...</Content>
119
+ <MsgId>1234567890123456</MsgId>
120
+ </xml>
121
+ EOF
122
+ expect(last_response.status).to eq(200)
123
+ expect(last_response.body).to eq('regex match')
124
+
125
+
126
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
127
+ <xml>
128
+ <ToUserName>tousername</ToUserName>
129
+ <FromUserName>fromusername</FromUserName>
130
+ <CreateTime>1348831860</CreateTime>
131
+ <MsgType>text</MsgType>
132
+ <Content>function match</Content>
133
+ <MsgId>1234567890123456</MsgId>
134
+ </xml>
135
+ EOF
136
+ expect(last_response.status).to eq(200)
137
+ expect(last_response.body).to eq('function match')
138
+
139
+
140
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
141
+ <xml>
142
+ <ToUserName>tousername</ToUserName>
143
+ <FromUserName>fromusername</FromUserName>
144
+ <CreateTime>1348831860</CreateTime>
145
+ <MsgType>text</MsgType>
146
+ <Content>default match</Content>
147
+ <MsgId>1234567890123456</MsgId>
148
+ </xml>
149
+ EOF
150
+ expect(last_response.status).to eq(200)
151
+ expect(last_response.body).to eq('default match')
152
+
153
+
154
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
155
+ <xml>
156
+ <ToUserName><![CDATA[toUser]]></ToUserName>
157
+ <FromUserName><![CDATA[fromUser]]></FromUserName>
158
+ <CreateTime>1357290913</CreateTime>
159
+ <MsgType><![CDATA[voice]]></MsgType>
160
+ <MediaId><![CDATA[media_id]]></MediaId>
161
+ <Format><![CDATA[Format]]></Format>
162
+ <MsgId>1234567890123456</MsgId>
163
+ </xml>
164
+ EOF
165
+ expect(last_response.status).to eq(200)
166
+ expect(last_response.body).to eq('1234567890123456')
167
+
168
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
169
+ <xml>
170
+ <ToUserName><![CDATA[toUser]]></ToUserName>
171
+ <FromUserName><![CDATA[fromUser]]></FromUserName>
172
+ <CreateTime>1351776360</CreateTime>
173
+ <MsgType><![CDATA[location]]></MsgType>
174
+ <Location_X>23.134521</Location_X>
175
+ <Location_Y>113.358803</Location_Y>
176
+ <Scale>20</Scale>
177
+ <Label><![CDATA[位置信息]]></Label>
178
+ <MsgId>1234567890123456</MsgId>
179
+ </xml>
180
+ EOF
181
+ expect(last_response.status).to eq(200)
182
+ expect(last_response.body).to eq('位置信息')
183
+
184
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
185
+ <xml>
186
+ <MsgType>unknown</MsgType>
187
+ </xml>
188
+ EOF
189
+ expect(last_response.status).to eq(501)
190
+ end
191
+
192
+ it "should accept complex match" do
193
+ def app
194
+ @instance = Sinatra.new do
195
+ set :wechat_token, 'test-token'
196
+ register Sinatra::Wechat
197
+ end
198
+ @instance.wechat {
199
+ future(lambda {|vs| vs[:to_user_name] == 'test' }, :content => %r{future}, :create_time => '1348831860') {
200
+ 'complex match'
201
+ }
202
+ range(:to_user_name => 'tesa'..'testz') { 'range match' }
203
+ }
204
+ end
205
+
206
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
207
+ <xml>
208
+ <ToUserName>test</ToUserName>
209
+ <FromUserName>fromusername</FromUserName>
210
+ <CreateTime>1348831860</CreateTime>
211
+ <MsgType>future</MsgType>
212
+ <Content>Test future message type</Content>
213
+ <MsgId>1234567890123456</MsgId>
214
+ </xml>
215
+ EOF
216
+ expect(last_response.status).to eq(200)
217
+ expect(last_response.body).to eq('complex match')
218
+
219
+
220
+
221
+ post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
222
+ <xml>
223
+ <ToUserName>test</ToUserName>
224
+ <MsgType>range</MsgType>
225
+ </xml>
226
+ EOF
227
+ expect(last_response.status).to eq(200)
228
+ expect(last_response.body).to eq('range match')
229
+ end
230
+
231
+ it "should raise error when invalid condition set" do
232
+ @instance = Sinatra.new do
233
+ set :wechat_token, 'test-token'
234
+ register Sinatra::Wechat
235
+ end
236
+ expect {
237
+ @instance.wechat {
238
+ future('invalid condition') { 'complex match' }
239
+ }
240
+ }.to raise_exception
241
+ end
242
+
243
+ it "can have multiple endpoint" do
244
+ def app
245
+ @instance = Sinatra.new do
246
+ set :wechat_token, 'test-token'
247
+ register Sinatra::Wechat
248
+ end
249
+ @instance.wechat('/wechat1') {
250
+ selector = lambda do |values|
251
+ x = values[:location_x].to_f
252
+ 20 < x && x < 30
253
+ end
254
+ location(selector) { 'matched location range' }
255
+ }
256
+ @instance.wechat('/wechat2') {
257
+ text { 'this is another wechat endpoint' }
258
+ }
259
+ end
260
+
261
+ post '/wechat1?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
262
+ <xml>
263
+ <MsgType><![CDATA[location]]></MsgType>
264
+ <Location_X>23.134521</Location_X>
265
+ <Location_Y>113.358803</Location_Y>
266
+ <Scale>20</Scale>
267
+ </xml>
268
+ EOF
269
+ expect(last_response.status).to eq(200)
270
+ expect(last_response.body).to eq('matched location range')
271
+
272
+
273
+ post '/wechat2?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
274
+ <xml>
275
+ <MsgType>text</MsgType>
276
+ </xml>
277
+ EOF
278
+ expect(last_response.status).to eq(200)
279
+ expect(last_response.body).to eq('this is another wechat endpoint')
280
+
281
+ end
282
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-wechat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Lu, Jun
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sinatra
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: blankslate
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
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: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack-test
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: coveralls
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Provide Extensible Sinatra API to support Wechat development mode
126
+ email:
127
+ - luj1985@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".coveralls.yml"
133
+ - Gemfile
134
+ - LICENSE
135
+ - README.md
136
+ - Rakefile
137
+ - examples/wechat_example.rb
138
+ - lib/sinatra/version.rb
139
+ - lib/sinatra/wechat.rb
140
+ - sinatra-wechat.gemspec
141
+ - spec/spec_helper.rb
142
+ - spec/wechat_spec.rb
143
+ homepage: https://github.com/luj1985/sinatra-wechat
144
+ licenses:
145
+ - MIT
146
+ metadata: {}
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubyforge_project:
163
+ rubygems_version: 2.2.2
164
+ signing_key:
165
+ specification_version: 4
166
+ summary: Sinatra extension for Tencent Wechat
167
+ test_files:
168
+ - spec/spec_helper.rb
169
+ - spec/wechat_spec.rb