stackmob 0.0.0

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.
@@ -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