stackmob 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "stackmob"
18
+ gem.homepage = "http://github.com/stackmob/stackmob-ruby"
19
+ gem.license = "MIT"
20
+ gem.summary = "Support Gem for StackMob Heroku Add-On"
21
+ gem.description = "Support Gem for StackMob Heroku Add-On"
22
+ gem.email = "jordan@stackmob.com"
23
+ gem.authors = ["StackMob"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ require 'rcov/rcovtask'
30
+ namespace :test do
31
+
32
+
33
+ Rake::TestTask.new(:unit) do |test|
34
+ test.libs << 'lib' << 'test'
35
+ test.pattern = 'test/unit/**/test_*.rb'
36
+ test.verbose = true
37
+ end
38
+
39
+ Rake::TestTask.new(:integration) do |test|
40
+ test.libs << 'lib' << 'test'
41
+ test.pattern = 'test/integration/**/test_*.rb'
42
+ test.verbose = true
43
+ end
44
+
45
+ Rcov::RcovTask.new do |test|
46
+ test.libs << 'test'
47
+ test.pattern = 'test/**/test_*.rb'
48
+ test.verbose = true
49
+ test.rcov_opts << '--exclude "gems/*"'
50
+ end
51
+
52
+ desc "Run Unit & Integration Tests"
53
+ task :all => [:unit, :integration]
54
+ end
55
+
56
+ task :default => "test:all"
57
+
58
+ require 'yard'
59
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,67 @@
1
+ # Copyright 2011 StackMob
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'oauth'
16
+ require 'yajl'
17
+ require 'yaml'
18
+
19
+ require 'stackmob/client'
20
+ require 'stackmob/data_store'
21
+ require 'stackmob/push'
22
+ require 'stackmob/rack/simple_oauth_provider'
23
+ require 'stackmob/helpers'
24
+
25
+ module StackMob
26
+
27
+ SANDBOX = 0
28
+ PRODUCTION = 1
29
+
30
+ def self.config
31
+ @config ||= YAML.load_file("config/stackmob.yml")
32
+ end
33
+
34
+ def self.secret
35
+ StackMob.config[sm_env_str]['secret']
36
+ end
37
+
38
+ def self.key
39
+ StackMob.config[sm_env_str]['key']
40
+ end
41
+
42
+ def self.app_name
43
+ StackMob.config['sm_app_name']
44
+ end
45
+
46
+ def self.client_name
47
+ StackMob.config['sm_client_name']
48
+ end
49
+
50
+ def self.dev_url
51
+ "http://#{StackMob.client_name}.mob2.stackmob.com"
52
+ end
53
+
54
+ def self.env
55
+ (is_production?) ? PRODUCTION : SANDBOX
56
+ end
57
+
58
+ def self.sm_env_str
59
+ env == PRODUCTION ? "production" : "development"
60
+ end
61
+
62
+ def self.is_production?
63
+ ENV["RACK_ENV"] == "production"
64
+ end
65
+
66
+
67
+ end
@@ -0,0 +1,90 @@
1
+ # Copyright 2011 StackMob
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'oauth'
16
+ require 'yajl'
17
+
18
+ module StackMob
19
+ class Client
20
+
21
+ VALID_METHODS = [:get, :post, :put, :delete]
22
+
23
+ class InvalidRequestMethod < ArgumentError; end # invalid HTTP Method/Verb is passed to the client
24
+ class RequestError < RuntimeError; end # response with code other than 200 received
25
+ class BadResponseBody < RuntimeError; end # Client receives response with invalid JSON
26
+
27
+ attr_accessor :app_name, :app_vsn
28
+
29
+ def initialize(base_url, app_name, app_vsn, oauth_key, oauth_secret)
30
+ self.app_name = app_name
31
+ self.app_vsn = app_vsn
32
+
33
+ create_oauth_client(oauth_key, oauth_secret, base_url)
34
+ end
35
+
36
+ def request(method, service, path, params = {})
37
+ request_path, request_body = generate_path_and_body(method, service, path, params)
38
+
39
+ response = @oauth_client.send(method, request_path, request_body)
40
+
41
+ rcode = response.code.to_i
42
+ if rcode >= 200 && rcode <= 299
43
+ parse_response(response) if method != :delete
44
+ else
45
+ raise RequestError.new("\nReq Method: #{method}\nReq. Path: #{request_path}\nReq. Body: #{request_body}\nResp. Code: #{rcode}, Resp Body: #{response.respond_to?(:body) ? response.body : 'unknown'}")
46
+ end
47
+ end
48
+
49
+ def create_oauth_client(key, secret, url)
50
+ @oauth_client = OAuth::AccessToken.new(OAuth::Consumer.new(key, secret, :site => url))
51
+ end
52
+ private :create_oauth_client
53
+
54
+ def generate_path_and_body(method, service, path, params)
55
+ intermediate_path = full_path(service, path)
56
+ case method
57
+ when :get, :delete
58
+ [intermediate_path + "?" + params_to_qs(params), ""]
59
+ when :post, :put
60
+ [intermediate_path, Yajl::Encoder.encode(params)]
61
+ else
62
+ raise InvalidRequestMethod
63
+ end
64
+ end
65
+ private :generate_path_and_body
66
+
67
+ def params_to_qs(params)
68
+ params.to_a.map { |pair| pair.join("=") }.join("&")
69
+ end
70
+ private :params_to_qs
71
+
72
+ def parse_response(r)
73
+ Yajl::Parser.parse(r.body)
74
+ rescue Yajl::ParseError
75
+ raise BadResponseBody.new("#{r.body} is not valid JSON")
76
+ end
77
+ private :parse_response
78
+
79
+ def full_path(service, requested_path)
80
+ "/#{service}/#{self.app_vsn}/#{self.app_name}/#{strip_prepending_slash(requested_path)}"
81
+ end
82
+ private :full_path
83
+
84
+ def strip_prepending_slash(path)
85
+ path.gsub(/^\//, "")
86
+ end
87
+ private :strip_prepending_slash
88
+
89
+ end
90
+ end
@@ -0,0 +1,74 @@
1
+ # Copyright 2011 StackMob
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module StackMob
16
+ class DataStore
17
+
18
+ attr_accessor :client
19
+
20
+ API_SVC = :api
21
+
22
+ def initialize(cl)
23
+ self.client = cl
24
+ end
25
+
26
+ def api_schema
27
+ self.client.request(:get, API_SVC, "/listapi")
28
+ end
29
+
30
+ def create(obj_name, params)
31
+ create!(obj_name, params)
32
+ rescue Client::RequestError
33
+ false
34
+ end
35
+
36
+ def create!(obj_name, params)
37
+ self.client.request(:post, API_SVC, obj_name_to_path(obj_name), params)
38
+ end
39
+
40
+ def delete(obj_name, params)
41
+ delete!(obj_name, params); true
42
+ rescue Client::RequestError
43
+ false
44
+ end
45
+
46
+ def delete!(obj_name, params)
47
+ self.client.request(:delete, API_SVC, obj_name_to_path(obj_name), params)
48
+ end
49
+
50
+ def get(obj_name, params = {})
51
+ self.client.request(:get, API_SVC, obj_name_to_path(obj_name), params)
52
+ end
53
+
54
+ def get_one(obj_name, params)
55
+ get(obj_name, params).first
56
+ end
57
+
58
+ def update(obj_name, obj_id, params)
59
+ update!(obj_name, obj_id, params); true
60
+ rescue Client::RequestError
61
+ false
62
+ end
63
+
64
+ def update!(obj_name, obj_id, params)
65
+ self.client.request(:put, API_SVC, obj_name_to_path(obj_name) + "/#{obj_id}", params)
66
+ end
67
+
68
+ def obj_name_to_path(obj_name)
69
+ "/#{obj_name}"
70
+ end
71
+ private :obj_name_to_path
72
+
73
+ end
74
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright 2011 StackMob
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'stackmob'
16
+
17
+ module StackMob
18
+ class Deployer
19
+
20
+ attr_accessor :client
21
+
22
+ APP_PATH = "heroku/app"
23
+
24
+ def initialize(cl)
25
+ self.client = cl
26
+ end
27
+
28
+ def register(hostname)
29
+ self.client.request(:post, :api, APP_PATH, :hostname => hostname)
30
+ end
31
+
32
+ def fetch
33
+ self.client.request(:get, :api, APP_PATH)
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,63 @@
1
+ # Copyright 2011 StackMob
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module StackMob
16
+ module Helpers
17
+ def sm_datastore
18
+ @sm_datastore ||= begin
19
+ client = StackMob::Client.new(sm_api_host, sm_app_name, StackMob.env, StackMob.key, StackMob.secret)
20
+ StackMob::DataStore.new(client)
21
+ end
22
+ end
23
+
24
+ def sm_push
25
+ @sm_push ||= begin
26
+ client = StackMob::Client.new(sm_push_host, sm_app_name, StackMob.env, StackMob.key, StackMob.secret)
27
+ StackMob::Push.new(client)
28
+ end
29
+ end
30
+
31
+ def sm_api_host
32
+ sm_normalize_host(sm_hostname_from_header_or_config('HTTP_X_STACKMOB_API'))
33
+ end
34
+ private :sm_api_host
35
+
36
+ def sm_push_host
37
+ sm_normalize_host(sm_hostname_from_header_or_config('HTTP_X_STACKMOB_PUSH'))
38
+ end
39
+ private :sm_push_host
40
+
41
+ def sm_hostname_from_header_or_config(header_str)
42
+ hostname = request.env[header_str]
43
+ (hostname.nil? || hostname == "") ? StackMob.dev_url : hostname
44
+ end
45
+ private :sm_hostname_from_header_or_config
46
+
47
+ def sm_app_name
48
+ StackMob.app_name
49
+ end
50
+ private :sm_app_name
51
+
52
+ def sm_app_version
53
+ (Rails.env.production?) ? StackMob::PRODUCTION : StackMob::SANDBOX
54
+ end
55
+ private :sm_app_version
56
+
57
+ def sm_normalize_host(host_str)
58
+ "http://#{StackMob.client_name}.#{host_str}"
59
+ end
60
+ private :sm_normalize_host
61
+
62
+ end
63
+ end
@@ -0,0 +1,63 @@
1
+ # Copyright 2011 StackMob
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module StackMob
16
+ class Push
17
+
18
+ attr_accessor :client
19
+
20
+ PUSH_SVC = :push
21
+ DEVICE_TOKEN_PATH = "/device_tokens"
22
+ BROADCAST_PATH = "/push_broadcast"
23
+ PUSH_PATH = "/push"
24
+
25
+ DEFAULT_BADGE = 0
26
+ DEFAULT_SOUND = ""
27
+
28
+ def initialize(cl)
29
+ self.client = cl
30
+ end
31
+
32
+ def register(user_id, device_token)
33
+ self.client.request(:post, PUSH_SVC, DEVICE_TOKEN_PATH, :userId => user_id, :token => device_token) # using non-convential symbols to conform to StackMob Push API
34
+ end
35
+
36
+ def broadcast(opts)
37
+ aps_data = generate_aps_data(opts)
38
+ payload = {:recipients => [], :aps => aps_data, :areRecipientsDeviceTokens => true, :exclude_tokens => []}
39
+
40
+ self.client.request(:post, PUSH_SVC, BROADCAST_PATH, payload)
41
+ end
42
+
43
+ def send_message(to, opts)
44
+ aps_data = generate_aps_data(opts)
45
+ recipients_are_device_tokens = (opts[:recipients_are_users]) ? false : true
46
+ payload = {:recipients => Array(to), :aps => aps_data, :areRecipientsDeviceTokens => recipients_are_device_tokens, :exclude_tokens => []}
47
+
48
+ self.client.request(:post, PUSH_SVC, PUSH_PATH, payload)
49
+ end
50
+
51
+ def generate_aps_data(opts)
52
+ alert = opts[:alert] || (raise ArgumentError.new("Push requires alert message"))
53
+ badge = opts[:badge] || DEFAULT_BADGE
54
+ sound = opts[:sound] || DEFAULT_SOUND
55
+
56
+ {:badge => badge, :sound => sound, :alert => alert}
57
+ end
58
+ private :generate_aps_data
59
+
60
+
61
+
62
+ end
63
+ end