motion-wechat 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a409062ddf8847ec76ba44d7ae20f22a348dd64
4
- data.tar.gz: 05889ac31ada26aeff34b322394079af15225937
3
+ metadata.gz: a19b83c93b6167b56d74f08b976f73c5156af0f7
4
+ data.tar.gz: 8cbb139358b5a526d01faa0f29bedb8556a4268b
5
5
  SHA512:
6
- metadata.gz: 0dc82eb924adcabbb06435befe515d61abcc3d5f8d83f90ee66e4e7a626ee90329f4385f88fed4bb9e30a2b3b6e40d7daa78a36131a0b418643a2e4085b04322
7
- data.tar.gz: af65871c625af76f838d17f8dbcc745ffa1cc544364fefa59773365d955ad2673a69abd2f85d7976870f26765f1b3c19388ee8ec9939ff7ef7b5aa273be75d08
6
+ metadata.gz: a9eed5cd35a4eedb2e812f4f99b0eb033ffe5e93a70105b5081e7de0d90f9cbe90959417351c303c4d1bd88e8a471d7748b1c6bf9364273de299c7fd4c2f1149
7
+ data.tar.gz: 4f99a7c8c256c55415292459e0b40253fa7c44ba9fa76dd17e40d1bd7105033c7fa829c887359d1e7b40f7fa413120247f6573a7f50814d6475e6abc1fd43eba
data/README.md CHANGED
@@ -24,6 +24,12 @@ Then setup configuration in your Rakefile:
24
24
  MotionWechat::Config.setup(app, 'app_key', 'app_secret')
25
25
  ```
26
26
 
27
+ Then register app in your `app_delegate.rb`
28
+ ```ruby
29
+ MotionWechat::API.instance.registerApp @key
30
+ ```
31
+
32
+
27
33
  Usage
28
34
  ==========
29
35
 
@@ -34,8 +40,41 @@ MotionWechat::API.instance.send_webpage "http://www.rubymotion.com", \
34
40
  title: "Ruby Motion", description: "Awesome way to write ios/osx app"
35
41
  ```
36
42
 
43
+ Wechat Authorization:
44
+
45
+ #### 1. authorize, this will direct user to wechat app
46
+ ```ruby
47
+ MotionWechat::API.instance.authorize
48
+ ```
49
+
50
+ #### 2. then make sure you have weixin delegate set up in `app_delegate.rb`
51
+ ```ruby
52
+ def application(application, handleOpenURL:url)
53
+ WXApi.handleOpenURL(url, delegate:self)
54
+ end
55
+
56
+ def application(application, openURL:url, sourceApplication:sourceApplication, annotation:annotation)
57
+ WXApi.handleOpenURL(url, delegate:self)
58
+ end
59
+ ```
60
+
61
+ #### 3. set up `onResp` in `app_delegate.rb`. this is when it comes back from wexin app
62
+ ```ruby
63
+ def onResp(resp)
64
+ if resp.is_a? SendAuthResp
65
+ # token lang country code
66
+ MotionWechat::API.instance.registerClient resp.code
67
+ MotionWechat::API.instance.get_user_info do |info|
68
+ p info
69
+ end
70
+ end
71
+ end
72
+ ```
73
+
74
+
37
75
  ## TODO
38
76
  - delegate helpers, i.e. `WXApi.handleOpenURL(url, delegate:self)`
77
+ - state & key validating in `application(application, handleOpenURL:url)`
39
78
 
40
79
  ## Contributions
41
80
 
data/lib/motion-wechat.rb CHANGED
@@ -2,6 +2,8 @@ unless defined?(Motion::Project::Config)
2
2
  raise "This file must be required within a RubyMotion project Rakefile."
3
3
  end
4
4
 
5
+ require 'afmotion'
6
+ require 'bubble-wrap/core'
5
7
  require_relative 'motion-wechat/config'
6
8
 
7
9
  Motion::Project::App.setup do |app|
@@ -11,4 +13,9 @@ Motion::Project::App.setup do |app|
11
13
 
12
14
  app.vendor_project 'vendor/SDKExport', :static
13
15
 
16
+ app.pods ||= Motion::Project::CocoaPods.new(app)
17
+ app.pods do
18
+ pod 'AFNetworking'
19
+ end
20
+
14
21
  end
@@ -0,0 +1,86 @@
1
+ module MotionWechat
2
+ class AccessToken
3
+ attr_reader :client, :token, :expires_in, :openid, :params
4
+ attr_accessor :options, :refresh_token
5
+
6
+ class << self
7
+
8
+ # Initializes an AccessToken from a Hash
9
+ #
10
+ # Example:
11
+ # MotionWechat::AccessToken.from_hash @client, {}
12
+ #
13
+ # Arguments:
14
+ # client: (MotionWechat::Client)
15
+ # hash: (Hash)
16
+ #
17
+ def from_hash(client, hash)
18
+ new(client, hash.delete('access_token') || hash.delete(:access_token), hash)
19
+ end
20
+
21
+ end
22
+
23
+ # Initalize an AccessToken
24
+ #
25
+ # Example:
26
+ # MotionWechat::AccessToken.new @client, 'token', {}
27
+ #
28
+ # Arguments:
29
+ # client: (MotionWechat::Client)
30
+ # token: (String)
31
+ # hash: (Hash)
32
+ #
33
+ def initialize(client, token, opts = {})
34
+ @client = client
35
+ @token = token.to_s
36
+ [:refresh_token, :expires_in, :openid].each do |arg|
37
+ instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
38
+ end
39
+ @expires_in ||= opts.delete('expires')
40
+ @expires_at ||= Time.now.to_i + @expires_in.to_i
41
+ @params = opts
42
+ end
43
+
44
+ # Get information using access token
45
+ #
46
+ # Example:
47
+ # @token.get "/sns/userinfo", { |res| ... }
48
+ #
49
+ # Arguments:
50
+ # path: (String)
51
+ # opts: (hash)
52
+ #
53
+ def get(path, opts = {}, &block)
54
+ @client.get path, opts.merge({access_token: @token}), &block
55
+ end
56
+
57
+ # Get user information
58
+ #
59
+ # Example:
60
+ # @token.get_user_info { |info| ... }
61
+ #
62
+ def get_user_info(&block)
63
+ get "/sns/userinfo", openid: @openid do |info|
64
+ block.call oauth2_wrapp(info)
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ # Wrap with OAuth2 structure
71
+ def oauth2_wrapp(info)
72
+ {
73
+ uid: @openid,
74
+ provider: "weixin",
75
+ info: {
76
+ image: info['headimgurl'],
77
+ nickname: info['nickname'],
78
+ name: info['nickname']
79
+ },
80
+ extra: info
81
+ }
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -2,13 +2,80 @@ module MotionWechat
2
2
  module API
3
3
 
4
4
  InvalidMediaObject = Class.new StandardError
5
+ InvalidClientError = Class.new ArgumentError
6
+
5
7
  def wx; WXApi; end
6
8
 
7
- # initialize weixin API using key and secret
9
+ # Initialize weixin API using key and secret
10
+ #
11
+ # Example:
12
+ # MtionWechat::API.new 'key', 'secret'
13
+ #
14
+ # Arguments:
15
+ # key: (String)
16
+ # secret: (String)
17
+ # options: (Hash)
18
+ #
8
19
  def initialize(key, secret, options={})
9
20
  @key = key
10
21
  @secret = secret
11
- WXApi.registerApp(@key)
22
+ end
23
+
24
+ # Register weixin app, usually put in `app_delegate.rb`
25
+ #
26
+ # Example:
27
+ # MotionWechat::API.instance.registerApp
28
+ #
29
+ def registerApp
30
+ WXApi.registerApp @key
31
+ end
32
+
33
+ # Register client
34
+ #
35
+ # Example:
36
+ # MotionWechat::API.instance.registerClient "code"
37
+ #
38
+ # Arguments:
39
+ # code: (String)
40
+ #
41
+ def registerClient(code)
42
+ @client ||= MotionWechat::Client.new @key, @secret, code
43
+ end
44
+
45
+ # Get user information
46
+ #
47
+ # Example:
48
+ # MotionWechat::API.instance.get_user_info { |info| ... }
49
+ #
50
+ def get_user_info(&block)
51
+ raise InvalidClientError if @client.nil?
52
+ if @token.nil?
53
+ @client.get_token do |token|
54
+ @token = token
55
+ @token.get_user_info { |info| block.call info }
56
+ end
57
+ else
58
+ @token.get_user_info { |info| block.call info }
59
+ end
60
+ end
61
+
62
+ # Sends authorization request
63
+ #
64
+ # Example:
65
+ # MotionWechat::API.instance.authorize
66
+ #
67
+ # Arguments:
68
+ # opts: (Hash), state: "myapp"
69
+ #
70
+ def authorize(opts={})
71
+ options = {
72
+ scope: "snsapi_userinfo",
73
+ state: "motion-wechat-app"
74
+ }.merge(opts)
75
+ req = SendAuthReq.alloc.init
76
+ req.scope = options[:scope]
77
+ req.state = options[:state]
78
+ WXApi.sendReq(req)
12
79
  end
13
80
 
14
81
  # Returns singleton instance of MotionWechat
@@ -20,6 +87,11 @@ module MotionWechat
20
87
  @instance ||= new config["key"], config["secret"]
21
88
  end
22
89
 
90
+ # Returns info_plist_key of MotionWechat
91
+ #
92
+ # Example:
93
+ # MotionWechat::API.config
94
+ #
23
95
  def self.config
24
96
  NSBundle.mainBundle.objectForInfoDictionaryKey MotionWechat::Config.info_plist_key
25
97
  end
@@ -0,0 +1,75 @@
1
+ module MotionWechat
2
+ class Client
3
+ attr_reader :id, :secret, :site
4
+ attr_accessor :options
5
+
6
+ # Instantiate a new OAuth 2.0 client using the Client ID
7
+ # and Client Secret registered to your application.
8
+ #
9
+ # Example:
10
+ # MtionWechat::Client.new 'key', 'secret', 'code', {}
11
+ #
12
+ # Arguments:
13
+ # key: (String)
14
+ # secret: (String)
15
+ # code: (String)
16
+ # options: (Hash)
17
+ #
18
+ def initialize(client_id, client_secret, code, options = {})
19
+ opts = options.dup
20
+ @id = client_id
21
+ @secret = client_secret
22
+ @code = code
23
+ @site = opts[:site] || "https://api.weixin.qq.com"
24
+ @options = {
25
+ token_url: '/sns/oauth2/access_token'
26
+ }.merge(opts)
27
+ end
28
+
29
+ # Returns token url to get access token
30
+ #
31
+ # Example:
32
+ # @client.token_url
33
+ #
34
+ def token_url
35
+ @site + @options[:token_url]
36
+ end
37
+
38
+ # Returns token url to get access token
39
+ #
40
+ # Example:
41
+ # @client.get_token do |token|
42
+ # p token
43
+ # end
44
+ #
45
+ def get_token(&block)
46
+ params = "appid=#{@id}&secret=#{@secret}&code=#{@code}&grant_type=authorization_code"
47
+ AFMotion::HTTP.get(token_url + "?" + params) do |res|
48
+ hash = to_hash res.body.to_s
49
+ token = AccessToken.from_hash self, hash
50
+ block.call token
51
+ end
52
+ end
53
+
54
+ # Request information
55
+ #
56
+ # Example:
57
+ # @client.get '/sns/userinfo', openid: "openid" do |info|
58
+ # p info
59
+ # end
60
+ #
61
+ def get(path, opts = {}, &block)
62
+ AFMotion::HTTP.get(@site + path, opts) do |res|
63
+ hash = to_hash res.body.to_s
64
+ block.call hash
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def to_hash(string)
71
+ BW::JSON.parse string.dataUsingEncoding(NSString.defaultCStringEncoding)
72
+ end
73
+
74
+ end
75
+ end
@@ -2,9 +2,8 @@ module MotionWechat
2
2
  module Config
3
3
  extend self
4
4
 
5
- def info_plist_key
6
- "WechatConfig"
7
- end
5
+ def info_plist_key; "WechatConfig"; end
6
+ def key; @key; end
8
7
 
9
8
  # Setup wechat information to rubymotion app, i.e. key, secret...
10
9
  #
@@ -20,13 +19,11 @@ module MotionWechat
20
19
  # Wechat key: (String)
21
20
  # Wechat secret: (String)
22
21
  # other options: (Hash)
23
- def setup(app, key, secret, opts={})
24
- options = {
25
- key: key,
26
- secret: secret
27
- }.merge(opts)
22
+ def setup(app, key, secret)
23
+ @key = key
24
+ @secret = secret
28
25
 
29
- app.info_plist[info_plist_key] = options.select { |k| k == :key || k == :secret }
26
+ app.info_plist[info_plist_key] = { key: @key, secret: @secret }
30
27
 
31
28
  bundle_url_types = [
32
29
  { 'CFBundleURLName' => "weixin",
@@ -34,8 +31,8 @@ module MotionWechat
34
31
  ]
35
32
 
36
33
  app.info_plist['CFBundleURLTypes'] ||= []
37
- app.info_plist['CFBundleURLTypes'] << bundle_url_types
38
-
34
+ app.info_plist['CFBundleURLTypes'] += bundle_url_types
35
+
39
36
  end
40
37
 
41
38
  end
@@ -1,3 +1,3 @@
1
1
  module MotionWechat
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
@@ -0,0 +1,69 @@
1
+ describe MotionWechat::AccessToken do
2
+ extend WebStub::SpecHelpers
3
+
4
+ before do
5
+ disable_network_access!
6
+ hash = {
7
+ access_token: "access_token",
8
+ expires_in:7200,
9
+ refresh_token:"refresh_token",
10
+ openid:"openid",
11
+ scope:"snsapi_userinfo"
12
+ }
13
+ @client = MotionWechat::Client.new "appid", "appsecret", "code"
14
+ @token = MotionWechat::AccessToken.from_hash @client, hash
15
+ end
16
+
17
+ it "should get client, token and expires" do
18
+ @token.client.should.be.kind_of MotionWechat::Client
19
+ @token.token.should == "access_token"
20
+ @token.expires_in.should == 7200
21
+ @token.openid.should == "openid"
22
+ end
23
+
24
+ context "get info" do
25
+
26
+ before do
27
+ @res = File.read NSBundle.mainBundle.resourcePath.stringByAppendingPathComponent("user.json")
28
+ @url = "https://api.weixin.qq.com/sns/userinfo?access_token=access_token&openid=openid"
29
+ end
30
+
31
+ it "gets user info using get" do
32
+ stub_request(:get, @url).
33
+ to_return(json: @res)
34
+ @res = nil
35
+
36
+ @token.get("/sns/userinfo", openid: "openid") do |info|
37
+ @info = info
38
+ resume
39
+ end
40
+
41
+ wait_max 1.0 do
42
+ @info.should.be.kind_of Hash
43
+ @info["nickname"].should == "monkey"
44
+ end
45
+ end
46
+
47
+ it "gets user info using get" do
48
+ stub_request(:get, @url).
49
+ to_return(json: @res)
50
+ @res = nil
51
+
52
+ @token.get_user_info do |info|
53
+ @info = info
54
+ resume
55
+ end
56
+
57
+ wait_max 1.0 do
58
+ @info.should.be.kind_of Hash
59
+ @info[:uid].should == "openid"
60
+ @info[:provider].should == "weixin"
61
+ @info[:info][:image].should == "http://wx.qlogo.cn/mmopen/0"
62
+ @info[:info][:nickname].should == "monkey"
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+
69
+ end
@@ -4,7 +4,7 @@ describe MotionWechat::API do
4
4
  WXApi.stub!(:registerApp) do |key|
5
5
  key.should == "app_key"
6
6
  end
7
- MotionWechat::API.instance
7
+ MotionWechat::API.instance.registerApp
8
8
  end
9
9
 
10
10
  it "should return WXApi as wx" do
@@ -15,6 +15,14 @@ describe MotionWechat::API do
15
15
 
16
16
  before { @mv = MotionWechat::API.instance }
17
17
 
18
+ it "sends auth message" do
19
+ WXApi.stub!(:sendReq) do |req|
20
+ req.should.be.kind_of(SendAuthReq)
21
+ end
22
+
23
+ @mv.authorize
24
+ end
25
+
18
26
  it 'sends web page url' do
19
27
  WXApi.stub!(:sendReq) do |req|
20
28
  req.should.be.kind_of(SendMessageToWXReq)
@@ -23,7 +31,7 @@ describe MotionWechat::API do
23
31
  req.message.mediaObject.webpageUrl.should == "http://www.motion-wechat.com"
24
32
  end
25
33
 
26
- MotionWechat::API.instance.send_webpage "http://www.motion-wechat.com", \
34
+ @mv.send_webpage "http://www.motion-wechat.com", \
27
35
  title: "title", description: "description"
28
36
  end
29
37
 
@@ -33,7 +41,7 @@ describe MotionWechat::API do
33
41
  req.message.mediaObject.videoUrl.should == "http://www.youtube.com/1"
34
42
  end
35
43
 
36
- MotionWechat::API.instance.send_video "http://www.youtube.com/1", \
44
+ @mv.send_video "http://www.youtube.com/1", \
37
45
  title: "title", description: "description"
38
46
  end
39
47
 
@@ -43,7 +51,7 @@ describe MotionWechat::API do
43
51
  req.message.mediaObject.musicUrl.should == "http://www.pandora.com/1"
44
52
  end
45
53
 
46
- MotionWechat::API.instance.send_music "http://www.pandora.com/1", \
54
+ @mv.send_music "http://www.pandora.com/1", \
47
55
  title: "title", description: "description"
48
56
  end
49
57
 
@@ -52,7 +60,7 @@ describe MotionWechat::API do
52
60
  req.message.mediaObject.should.be.kind_of(WXImageObject)
53
61
  end
54
62
 
55
- MotionWechat::API.instance.send_image NSData.dataWithContentsOfFile("dummy"), \
63
+ @mv.send_image NSData.dataWithContentsOfFile("dummy"), \
56
64
  title: "title", description: "description"
57
65
  end
58
66
 
@@ -62,7 +70,7 @@ describe MotionWechat::API do
62
70
  req.text.should == "hello"
63
71
  end
64
72
 
65
- MotionWechat::API.instance.send_text "hello"
73
+ @mv.send_text "hello"
66
74
  end
67
75
 
68
76
  describe "Scene type" do
@@ -73,7 +81,7 @@ describe MotionWechat::API do
73
81
  req.scene.should == WXSceneSession
74
82
  end
75
83
 
76
- MotionWechat::API.instance.send_webpage "http://www.motion-wechat.com", \
84
+ @mv.send_webpage "http://www.motion-wechat.com", \
77
85
  title: "title", description: "description"
78
86
  end
79
87
 
@@ -83,7 +91,7 @@ describe MotionWechat::API do
83
91
  req.scene.should == WXSceneTimeline
84
92
  end
85
93
 
86
- MotionWechat::API.instance.send_webpage "http://www.motion-wechat.com", \
94
+ @mv.send_webpage "http://www.motion-wechat.com", \
87
95
  title: "title", description: "description", scene: WXSceneTimeline
88
96
  end
89
97
 
@@ -0,0 +1,70 @@
1
+ describe MotionWechat::Client do
2
+ extend WebStub::SpecHelpers
3
+
4
+ before do
5
+ disable_network_access!
6
+ @client = MotionWechat::Client.new "appid", "appsecret", "code"
7
+ end
8
+
9
+ it "should get id, secret and site" do
10
+ @client.id.should == "appid"
11
+ @client.secret.should == "appsecret"
12
+ @client.site == "https://api.weixin.qq.com"
13
+ end
14
+
15
+ it "should get token url" do
16
+ @client.token_url.should == "https://api.weixin.qq.com/sns/oauth2/access_token"
17
+ end
18
+
19
+ context "access token" do
20
+
21
+ before do
22
+ @res = File.read NSBundle.mainBundle.resourcePath.stringByAppendingPathComponent("token.json")
23
+ @url = "https://api.weixin.qq.com/sns/oauth2/access_token" + \
24
+ "?appid=appid&secret=appsecret&code=code&grant_type=authorization_code"
25
+ end
26
+
27
+ it "gets token" do
28
+ stub_request(:get, @url).
29
+ to_return(json: @res)
30
+ @res = nil
31
+
32
+ @client.get_token do |token|
33
+ @token = token
34
+ resume
35
+ end
36
+
37
+ wait_max 1.0 do
38
+ @token.should.be.kind_of MotionWechat::AccessToken
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ context "get info" do
45
+
46
+ before do
47
+ @res = File.read NSBundle.mainBundle.resourcePath.stringByAppendingPathComponent("user.json")
48
+ @url = "https://api.weixin.qq.com/sns/user_info?access_token=token&openid=openid"
49
+ end
50
+
51
+ it "gets user info" do
52
+ stub_request(:get, @url).
53
+ to_return(json: @res)
54
+ @res = nil
55
+
56
+ @client.get("/sns/user_info", access_token: "token", openid: "openid") do |info|
57
+ @info = info
58
+ resume
59
+ end
60
+
61
+ wait_max 1.0 do
62
+ @info.should.be.kind_of Hash
63
+ @info["nickname"].should == "monkey"
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+
70
+ end
metadata CHANGED
@@ -1,27 +1,59 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion-wechat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Qi He
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-11 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2014-09-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: afmotion
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 2.3.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 2.3.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bubble-wrap
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.7.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 1.7.0
13
41
  description: Rubymotion gem to easily use WeChatSDK
14
42
  email: qhe@heyook.com
15
43
  executables: []
16
44
  extensions: []
17
45
  extra_rdoc_files: []
18
46
  files:
47
+ - lib/motion-wechat/access_token.rb
19
48
  - lib/motion-wechat/api.rb
49
+ - lib/motion-wechat/client.rb
20
50
  - lib/motion-wechat/config.rb
21
51
  - lib/motion-wechat/version.rb
22
52
  - lib/motion-wechat.rb
23
53
  - README.md
54
+ - spec/motion-wechat/access_token_spec.rb
24
55
  - spec/motion-wechat/api_spec.rb
56
+ - spec/motion-wechat/client_spec.rb
25
57
  - spec/motion-wechat/config_spec.rb
26
58
  homepage: http://github.com/he9qi/motion-wechat
27
59
  licenses:
@@ -48,5 +80,7 @@ signing_key:
48
80
  specification_version: 4
49
81
  summary: Rubymotion wrapper for WeChatSDK
50
82
  test_files:
83
+ - spec/motion-wechat/access_token_spec.rb
51
84
  - spec/motion-wechat/api_spec.rb
85
+ - spec/motion-wechat/client_spec.rb
52
86
  - spec/motion-wechat/config_spec.rb