zenbe-flareshow 0.2.1
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.
- data/.gitignore +1 -0
- data/Flareshow.gemspec +83 -0
- data/LICENSE +20 -0
- data/README.txt +9 -0
- data/Rakefile +61 -0
- data/TODO +13 -0
- data/VERSION +1 -0
- data/lib/cache.rb +70 -0
- data/lib/comment.rb +12 -0
- data/lib/exceptions.rb +13 -0
- data/lib/file_attachment.rb +24 -0
- data/lib/flareshow.rb +59 -0
- data/lib/flow.rb +56 -0
- data/lib/invitation.rb +3 -0
- data/lib/membership.rb +3 -0
- data/lib/post.rb +37 -0
- data/lib/resource.rb +159 -0
- data/lib/server.rb +2 -0
- data/lib/service.rb +137 -0
- data/lib/user.rb +58 -0
- data/lib/util.rb +13 -0
- data/test/flareshow_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- metadata +137 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/Flareshow.gemspec
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{flareshow}
|
8
|
+
s.version = "0.2.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Will Bailey"]
|
12
|
+
s.date = %q{2009-09-03}
|
13
|
+
s.description = %q{TODO: a ruby gem for interacting with the shareflow collaboration service by Zenbe}
|
14
|
+
s.email = %q{will.bailey@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.txt"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"Flareshow.gemspec",
|
22
|
+
"LICENSE",
|
23
|
+
"README.txt",
|
24
|
+
"Rakefile",
|
25
|
+
"TODO",
|
26
|
+
"VERSION",
|
27
|
+
"lib/cache.rb",
|
28
|
+
"lib/comment.rb",
|
29
|
+
"lib/exceptions.rb",
|
30
|
+
"lib/file_attachment.rb",
|
31
|
+
"lib/flareshow.rb",
|
32
|
+
"lib/flow.rb",
|
33
|
+
"lib/invitation.rb",
|
34
|
+
"lib/membership.rb",
|
35
|
+
"lib/post.rb",
|
36
|
+
"lib/resource.rb",
|
37
|
+
"lib/server.rb",
|
38
|
+
"lib/service.rb",
|
39
|
+
"lib/user.rb",
|
40
|
+
"lib/util.rb",
|
41
|
+
"test/flareshow_test.rb",
|
42
|
+
"test/test_helper.rb"
|
43
|
+
]
|
44
|
+
s.has_rdoc = true
|
45
|
+
s.homepage = %q{http://github.com/willbailey/flareshow}
|
46
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
47
|
+
s.require_paths = ["lib"]
|
48
|
+
s.rubyforge_project = %q{flareshow}
|
49
|
+
s.rubygems_version = %q{1.3.1}
|
50
|
+
s.summary = %q{TODO: a ruby gem for interacting with the shareflow collaboration service}
|
51
|
+
s.test_files = [
|
52
|
+
"test/flareshow_test.rb",
|
53
|
+
"test/test_helper.rb"
|
54
|
+
]
|
55
|
+
|
56
|
+
if s.respond_to? :specification_version then
|
57
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
58
|
+
s.specification_version = 2
|
59
|
+
|
60
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
61
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<json>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<curb>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<facets>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<facets/dictionary>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<uuid>, [">= 0"])
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
69
|
+
s.add_dependency(%q<json>, [">= 0"])
|
70
|
+
s.add_dependency(%q<curb>, [">= 0"])
|
71
|
+
s.add_dependency(%q<facets>, [">= 0"])
|
72
|
+
s.add_dependency(%q<facets/dictionary>, [">= 0"])
|
73
|
+
s.add_dependency(%q<uuid>, [">= 0"])
|
74
|
+
end
|
75
|
+
else
|
76
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
77
|
+
s.add_dependency(%q<json>, [">= 0"])
|
78
|
+
s.add_dependency(%q<curb>, [">= 0"])
|
79
|
+
s.add_dependency(%q<facets>, [">= 0"])
|
80
|
+
s.add_dependency(%q<facets/dictionary>, [">= 0"])
|
81
|
+
s.add_dependency(%q<uuid>, [">= 0"])
|
82
|
+
end
|
83
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Will Bailey
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.txt
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
___ ___ __
|
2
|
+
/'___\/\_ \ /\ \
|
3
|
+
/\ \__/\//\ \ __ _ __ __ ____\ \ \___ ___ __ __ __
|
4
|
+
\ \ ,__\ \ \ \ /'__`\ /\`'__\/'__`\ /',__\\ \ _ `\ / __`\/\ \/\ \/\ \
|
5
|
+
\ \ \_/ \_\ \_/\ \L\.\_\ \ \//\ __//\__, `\\ \ \ \ \/\ \L\ \ \ \_/ \_/ \
|
6
|
+
\ \_\ /\____\ \__/.\_\\ \_\\ \____\/\____/ \ \_\ \_\ \____/\ \___x___/'
|
7
|
+
\/_/ \/____/\/__/\/_/ \/_/ \/____/\/___/ \/_/\/_/\/___/ \/__//__/
|
8
|
+
|
9
|
+
~ Client Library For the Zenbe Shareflow API
|
data/Rakefile
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "flareshow"
|
8
|
+
gem.summary = %Q{TODO: a ruby gem for interacting with the shareflow collaboration service}
|
9
|
+
gem.description = %Q{TODO: a ruby gem for interacting with the shareflow collaboration service by Zenbe}
|
10
|
+
gem.email = "will.bailey@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/willbailey/flareshow"
|
12
|
+
gem.authors = ["Will Bailey"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda"
|
14
|
+
gem.add_development_dependency "json"
|
15
|
+
gem.add_development_dependency "curb"
|
16
|
+
gem.add_development_dependency "facets"
|
17
|
+
gem.add_development_dependency "facets/dictionary"
|
18
|
+
gem.add_development_dependency "uuid"
|
19
|
+
gem.rubyforge_project = "flareshow"
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'rake/testtask'
|
26
|
+
Rake::TestTask.new(:test) do |test|
|
27
|
+
test.libs << 'lib' << 'test'
|
28
|
+
test.pattern = 'test/**/*_test.rb'
|
29
|
+
test.verbose = true
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
require 'rcov/rcovtask'
|
34
|
+
Rcov::RcovTask.new do |test|
|
35
|
+
test.libs << 'test'
|
36
|
+
test.pattern = 'test/**/*_test.rb'
|
37
|
+
test.verbose = true
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
task :rcov do
|
41
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
task :test => :check_dependencies
|
46
|
+
|
47
|
+
task :default => :test
|
48
|
+
|
49
|
+
require 'rake/rdoctask'
|
50
|
+
Rake::RDocTask.new do |rdoc|
|
51
|
+
if File.exist?('VERSION')
|
52
|
+
version = File.read('VERSION')
|
53
|
+
else
|
54
|
+
version = ""
|
55
|
+
end
|
56
|
+
|
57
|
+
rdoc.rdoc_dir = 'rdoc'
|
58
|
+
rdoc.title = "flareshow #{version}"
|
59
|
+
rdoc.rdoc_files.include('README*')
|
60
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
61
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
TODO:
|
2
|
+
write tests with Fakeweb
|
3
|
+
flesh out more resource methods
|
4
|
+
make post requests with embedded file attachments respond with the file attachments for now...later we need to allow the creation of files in the same way as posts as flat
|
5
|
+
namespaced objects
|
6
|
+
handle failure states on requests
|
7
|
+
namespace auth requests to be consistent with other api calls
|
8
|
+
|
9
|
+
TODONE:
|
10
|
+
remove temporary auth setup logging in user automatically
|
11
|
+
|
12
|
+
QUESTIONS:
|
13
|
+
should authentication responses look like query and commit responses namespacing is applied differently in authentication currently?
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.1
|
data/lib/cache.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# provides an interface for various
|
2
|
+
# caches that flareshow might use
|
3
|
+
class Flareshow::CacheManager
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# assimilate the resources provided in the response
|
7
|
+
def assimilate_resources(data)
|
8
|
+
# process each resource key and generate a new object
|
9
|
+
# or merge the object data with an existing object
|
10
|
+
data.each do |resource_pair|
|
11
|
+
resource_key, resources = resource_pair[0], resource_pair[1]
|
12
|
+
klass = Kernel.const_get(Flareshow::ResourceToClassMap[resource_key])
|
13
|
+
next unless klass
|
14
|
+
resources = resources.map do |resource_data|
|
15
|
+
item = cache.get_resource(resource_key, resource_data["id"])
|
16
|
+
if item
|
17
|
+
item.update(resource_data, :server)
|
18
|
+
else
|
19
|
+
item = klass.new(resource_data, :server)
|
20
|
+
end
|
21
|
+
cache.set_resource(resource_key, item.id, item)
|
22
|
+
item
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# get the managed cache object
|
28
|
+
def cache
|
29
|
+
@cache ||= Flareshow::Cache.new
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# a simple in memory cache for Flareshow objects
|
38
|
+
class Flareshow::Cache
|
39
|
+
|
40
|
+
# load a resource from the cache
|
41
|
+
def get_resource(resource_key, id)
|
42
|
+
resource_cache(resource_key)[id]
|
43
|
+
end
|
44
|
+
|
45
|
+
# set a resource in the cache
|
46
|
+
def set_resource(resource_key, id, object)
|
47
|
+
resource_cache(resource_key)[id] = object
|
48
|
+
end
|
49
|
+
|
50
|
+
# remove all cached objects
|
51
|
+
def flush
|
52
|
+
data = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
# number of cached objects
|
56
|
+
def size
|
57
|
+
data.values.inject(0){|m,v| m+=v.size}
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def data
|
62
|
+
@cache ||= {}
|
63
|
+
end
|
64
|
+
|
65
|
+
# get a specific resource dictionary
|
66
|
+
def resource_cache(resource_key)
|
67
|
+
data[resource_key] ||= Dictionary.new
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
data/lib/comment.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
class Comment < Flareshow::Resource
|
2
|
+
|
3
|
+
# permalink to this comment
|
4
|
+
def permalink(mobile=false)
|
5
|
+
if mobile
|
6
|
+
"http://#{Flareshow::Service.server.host}/#{Flareshow::Service.server.domain}/shareflow/mobile/post/#{reply_to}"
|
7
|
+
else
|
8
|
+
"http://#{Flareshow::Service.server.host}/#{Flareshow::Service.server.domain}/shareflow/p/#{reply_to}?comment_id#{id}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
data/lib/exceptions.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Flareshow
|
2
|
+
class ConfigurationException < Exception
|
3
|
+
# exception thrown if the API client is not configured properly
|
4
|
+
end
|
5
|
+
|
6
|
+
class AuthenticationRequired < Exception
|
7
|
+
# exception thrown if a request is made without a logged in user
|
8
|
+
end
|
9
|
+
|
10
|
+
class AuthenticationFailed < Exception
|
11
|
+
# exception thrown if an auth request fails
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class FileAttachment < Flareshow::Resource
|
2
|
+
|
3
|
+
# =================
|
4
|
+
# = Class Methods =
|
5
|
+
# =================
|
6
|
+
class << self
|
7
|
+
# file attachments has a resource key of files
|
8
|
+
# for querying the server
|
9
|
+
def resource_key
|
10
|
+
"files"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# download the file contents
|
15
|
+
def download
|
16
|
+
url = self.url
|
17
|
+
unless url.match(/http/)
|
18
|
+
url = "http://#{Flareshow::Base.server.host}/#{Flareshow::Base.server.domain}#{url}"
|
19
|
+
end
|
20
|
+
Flareshow::Util.log_info("getting #{url}")
|
21
|
+
self.class.http_get(url)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/lib/flareshow.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
ROOT = File.dirname(__FILE__) unless defined? ROOT
|
2
|
+
|
3
|
+
# gems
|
4
|
+
require 'rubygems' #TODO fix
|
5
|
+
require 'json'
|
6
|
+
require 'curb'
|
7
|
+
require 'facets'
|
8
|
+
require 'facets/dictionary'
|
9
|
+
require 'uuid'
|
10
|
+
require 'ruby-debug'
|
11
|
+
|
12
|
+
# std lib
|
13
|
+
require 'ostruct'
|
14
|
+
require 'yaml'
|
15
|
+
require 'logger'
|
16
|
+
|
17
|
+
# Application Constants and configuration
|
18
|
+
module Flareshow
|
19
|
+
# default parameters that are included with query
|
20
|
+
# requests unless they are explicitly overridden
|
21
|
+
DEFAULT_PARAMS = {:order => "created_at desc"} unless defined? DEFAULT_PARAMS
|
22
|
+
|
23
|
+
# mappings to allow easy conversion from the
|
24
|
+
# response keys the server sends back in JSUP
|
25
|
+
# messages
|
26
|
+
ResourceToClassMap = {
|
27
|
+
"flows" => "Flow",
|
28
|
+
"posts" => "Post",
|
29
|
+
"comments" => "Comment",
|
30
|
+
"files" => "FileAttachment",
|
31
|
+
"memberships" => "Membership",
|
32
|
+
"invitations" => "Invitation",
|
33
|
+
"users" => "User"
|
34
|
+
} unless defined? ResourceToClassMap
|
35
|
+
ClassToResourceMap = ResourceToClassMap.invert unless defined? ClassToResourceMap
|
36
|
+
end
|
37
|
+
|
38
|
+
# app
|
39
|
+
require File.join(ROOT, 'service')
|
40
|
+
require File.join(ROOT, 'resource')
|
41
|
+
require File.join(ROOT, 'cache')
|
42
|
+
|
43
|
+
# logging
|
44
|
+
DEFAULT_LOGGER = Logger.new(STDOUT) unless defined?(DEFAULT_LOGGER)
|
45
|
+
|
46
|
+
Dir.glob(File.join(ROOT, "*.rb")).each{|lib| require lib}
|
47
|
+
|
48
|
+
# check for presence of config file
|
49
|
+
config_file_path = File.expand_path("~/.flareshowrc")
|
50
|
+
if File.exists?(config_file_path)
|
51
|
+
data = YAML.load_file(config_file_path)
|
52
|
+
host = data["host"] || "biz.zenbe.com"
|
53
|
+
subdomain = data["subdomain"]
|
54
|
+
Flareshow::Service.configure(subdomain, host)
|
55
|
+
|
56
|
+
if data["login"] && data["password"]
|
57
|
+
User.log_in(data["login"], data["password"])
|
58
|
+
end
|
59
|
+
end
|
data/lib/flow.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
class Flow < Flareshow::Resource
|
2
|
+
|
3
|
+
# =================
|
4
|
+
# = Class Methods =
|
5
|
+
# =================
|
6
|
+
class << self
|
7
|
+
# find a flow by name
|
8
|
+
def find_by_name(name)
|
9
|
+
self.find({:name => name})
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# permalink for this flow
|
14
|
+
def permalink(mobile=false)
|
15
|
+
if mobile
|
16
|
+
"http://#{Flareshow::Base.server.host}/#{Flareshow::Base.server.domain}/shareflow/mobile/flows/#{id}"
|
17
|
+
else
|
18
|
+
"http://#{Flareshow::Base.server.host}/#{Flareshow::Base.server.domain}/shareflow/c/#{id}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#invite/reinvite a user to a flow by email address
|
23
|
+
def send_invitions(email_addresses)
|
24
|
+
self.invite = [email_addresses].flatten
|
25
|
+
self.save
|
26
|
+
end
|
27
|
+
|
28
|
+
# uninvite an invited user from a flow by email address
|
29
|
+
def revoke_invitations(email_addresses)
|
30
|
+
self.uninvite = [email_addresses].flatten
|
31
|
+
self.save
|
32
|
+
end
|
33
|
+
|
34
|
+
# remove a user from a flow
|
35
|
+
# you must be the owner of the flow to perform
|
36
|
+
# this action
|
37
|
+
def remove_members(member_ids)
|
38
|
+
self.remove_members = [email_addresses].flatten
|
39
|
+
self.save
|
40
|
+
end
|
41
|
+
|
42
|
+
# build a new post but don't persist it immediatel
|
43
|
+
def build_post(attributes)
|
44
|
+
post = Post.new
|
45
|
+
post.update(attributes)
|
46
|
+
post.flow_id = id
|
47
|
+
post
|
48
|
+
end
|
49
|
+
|
50
|
+
# create a new post in this flow
|
51
|
+
def create_post(attributes)
|
52
|
+
p=build_post(attributes)
|
53
|
+
p.save
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
data/lib/invitation.rb
ADDED
data/lib/membership.rb
ADDED
data/lib/post.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
class Post < Flareshow::Resource
|
2
|
+
|
3
|
+
# permalink to this post
|
4
|
+
def permalink(mobile=false)
|
5
|
+
if mobile
|
6
|
+
"http://#{Flareshow::Base.server.host}/#{Flareshow::Base.server.domain}/shareflow/mobile/post/#{id}"
|
7
|
+
else
|
8
|
+
"http://#{Flareshow::Base.server.host}/#{Flareshow::Base.server.domain}/shareflow/p/#{id}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# build a new comment but don't immediately persist it
|
13
|
+
def build_comment(attributes={})
|
14
|
+
c = Comment.new(attributes)
|
15
|
+
c.post_id = self.id
|
16
|
+
c
|
17
|
+
end
|
18
|
+
|
19
|
+
# create a new comment on the post
|
20
|
+
def create_comment(attributes={})
|
21
|
+
c = build_comment(attributes)
|
22
|
+
c.save
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_file(file_path)
|
26
|
+
self.files ||= []
|
27
|
+
self.files += [{"part_id" => "file_#{UUID.generate}", "file_path" => file_path}]
|
28
|
+
end
|
29
|
+
|
30
|
+
# upload a file to a post
|
31
|
+
def create_file(file_path)
|
32
|
+
self.files = []
|
33
|
+
self.build_file(file_path)
|
34
|
+
self.save
|
35
|
+
self.files = nil
|
36
|
+
end
|
37
|
+
end
|
data/lib/resource.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
class Flareshow::Resource
|
2
|
+
|
3
|
+
class << self
|
4
|
+
# return the resource key for this resource
|
5
|
+
def resource_key
|
6
|
+
Flareshow::ClassToResourceMap[self.name]
|
7
|
+
end
|
8
|
+
|
9
|
+
# find an existing instance of this object in the client or create a new one
|
10
|
+
def get_from_cache(id)
|
11
|
+
store.get_resource(resource_key, id)
|
12
|
+
end
|
13
|
+
|
14
|
+
# list out the instances in memory
|
15
|
+
def list_cache
|
16
|
+
store.list_resource(resource_key)
|
17
|
+
end
|
18
|
+
|
19
|
+
# store the response resources in the cache
|
20
|
+
def cache_response(response)
|
21
|
+
Flareshow::CacheManager.assimilate_resources(response["resources"])
|
22
|
+
end
|
23
|
+
|
24
|
+
# find a resource by querying the server
|
25
|
+
# store the results in the cache and return
|
26
|
+
# the keyed resources for the model performing the query
|
27
|
+
def find(params={})
|
28
|
+
response = Flareshow::Service.query({resource_key => params})
|
29
|
+
cache_response(response)
|
30
|
+
(response["resources"] || {})[resource_key]
|
31
|
+
end
|
32
|
+
|
33
|
+
# create a resource local and sync it to the server
|
34
|
+
def create(params={})
|
35
|
+
new(params).save
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
def store
|
40
|
+
Flareshow::CacheManager.cache
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# constructor
|
45
|
+
# build a new Flareshow::Base resource
|
46
|
+
def initialize(data={}, source = :client)
|
47
|
+
@data = {}
|
48
|
+
data["id"] = UUID.generate.upcase if source == :client
|
49
|
+
update(data, source)
|
50
|
+
end
|
51
|
+
|
52
|
+
# return the resource key for this resource
|
53
|
+
def resource_key
|
54
|
+
Flareshow::ClassToResourceMap[self.class.name]
|
55
|
+
end
|
56
|
+
|
57
|
+
# store a resource in the cache
|
58
|
+
def cache
|
59
|
+
self.class.store.store.set_resource(resource_key, id, self)
|
60
|
+
end
|
61
|
+
|
62
|
+
# ==============================
|
63
|
+
# = Server Persistence Actions =
|
64
|
+
# ==============================
|
65
|
+
|
66
|
+
# reload the resource from the server
|
67
|
+
def refresh
|
68
|
+
results = self.find({"id" => id})
|
69
|
+
mark_destroyed! if results.empty?
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
# save a resource to the server
|
74
|
+
def save
|
75
|
+
key = Flareshow::ClassToResourceMap[self.class.name]
|
76
|
+
response = Flareshow::Service.commit({resource_key => [(self.changes || {}).merge({"id" => id})] })
|
77
|
+
cache_response(response)
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
# destroy the resource on the server
|
82
|
+
def destroy
|
83
|
+
response = self.class.commit({resource_key => [{"id" => id, "_removed" => true}]})
|
84
|
+
cache_response(response)
|
85
|
+
mark_destroyed!
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
# has this resource been destroyed
|
90
|
+
def destroyed?
|
91
|
+
self._removed || self.frozen?
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def mark_destroyed!
|
97
|
+
self.freeze
|
98
|
+
self._removed=true
|
99
|
+
self.class.store.delete(id)
|
100
|
+
end
|
101
|
+
|
102
|
+
public
|
103
|
+
|
104
|
+
# ==================================
|
105
|
+
# = Attribute and State Management =
|
106
|
+
# ==================================
|
107
|
+
|
108
|
+
# return the server id of a resource
|
109
|
+
def id
|
110
|
+
@data["id"]
|
111
|
+
end
|
112
|
+
|
113
|
+
# update the instance data for this resource
|
114
|
+
# keeping track of dirty state if the update came from
|
115
|
+
# the client
|
116
|
+
def update(attributes, source = :client)
|
117
|
+
attributes.each do |p|
|
118
|
+
key, value = p[0], p[1]
|
119
|
+
self.set(key, value, source)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# keep track of dirty state on the client by maintaining a copy
|
124
|
+
# of the original state of each intstance variable when it is provided by
|
125
|
+
# the server
|
126
|
+
def set(key, value, source = :client)
|
127
|
+
# Flareshow::Util.log_info("setting #{key} : #{value}")
|
128
|
+
@data["original_#{key}"] = value if source == :server
|
129
|
+
@data[key.to_s]=value
|
130
|
+
end
|
131
|
+
|
132
|
+
# get a data value
|
133
|
+
def get(key)
|
134
|
+
@data[key]
|
135
|
+
end
|
136
|
+
|
137
|
+
# all the state that has been modified on the client
|
138
|
+
def changes
|
139
|
+
attributes = @data.inject({}) do |memo, pair|
|
140
|
+
key, value = *pair
|
141
|
+
if @data[key] != @data["original_#{key}"] && !key.to_s.match(/original/)
|
142
|
+
memo[key] = value
|
143
|
+
end
|
144
|
+
memo
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# fallback to getter or setter
|
149
|
+
def method_missing(meth, *args)
|
150
|
+
meth = meth.to_s
|
151
|
+
meth.match(/\=/) ? set(meth.gsub(/\=/,''), *args) : get(meth)
|
152
|
+
end
|
153
|
+
|
154
|
+
# has this model been removed on the server
|
155
|
+
def method_name
|
156
|
+
!!self._removed
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
data/lib/server.rb
ADDED
data/lib/service.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# provides an interface to the shareflow api
|
2
|
+
class Flareshow::Service
|
3
|
+
|
4
|
+
# =================
|
5
|
+
# = Class Methods =
|
6
|
+
# =================
|
7
|
+
class << self
|
8
|
+
attr_accessor :server
|
9
|
+
|
10
|
+
# setup the service to use a particular host and domain
|
11
|
+
def configure(subdomain=nil, host='biz.zenbe.com')
|
12
|
+
raise Flareshow::ConfigurationException unless subdomain
|
13
|
+
self.server=Server.new(host, subdomain)
|
14
|
+
end
|
15
|
+
|
16
|
+
# return the authentication endpoint for a given host and domain
|
17
|
+
def auth_endpoint
|
18
|
+
"http://#{server.host}/#{server.domain}/shareflow/api/v2/auth.json"
|
19
|
+
end
|
20
|
+
|
21
|
+
# return the api endpoint for a given host and domain
|
22
|
+
def api_endpoint
|
23
|
+
"http://#{server.host}/#{server.domain}/shareflow/api/v2.json"
|
24
|
+
end
|
25
|
+
|
26
|
+
# has the server been configured?
|
27
|
+
def server_defined?
|
28
|
+
!!server
|
29
|
+
end
|
30
|
+
|
31
|
+
# make a post request to an endpoint
|
32
|
+
# returns a hash of
|
33
|
+
# - status code
|
34
|
+
# - headers
|
35
|
+
# - body
|
36
|
+
def post(url, params)
|
37
|
+
raise Flareshow::ConfigurationException unless server_defined?
|
38
|
+
request = Curl::Easy.new(url) do |curl|
|
39
|
+
curl.headers = {
|
40
|
+
'Accept' => 'application/json',
|
41
|
+
'User-Agent' => 'flareshow 0.1'
|
42
|
+
}
|
43
|
+
curl.multipart_form_post=true
|
44
|
+
end
|
45
|
+
request.http_post(*params)
|
46
|
+
process_response(request)
|
47
|
+
end
|
48
|
+
|
49
|
+
# do a get request
|
50
|
+
def http_get(url)
|
51
|
+
request = Curl::Easy.new(url + "?key=#{@key}") do |curl|
|
52
|
+
curl.headers = {
|
53
|
+
'User-Agent' => 'flareshow 0.1'
|
54
|
+
}
|
55
|
+
end
|
56
|
+
request.perform()
|
57
|
+
process_response(request)
|
58
|
+
end
|
59
|
+
|
60
|
+
# get the interesting bits out of the curl response
|
61
|
+
def process_response(request)
|
62
|
+
response = {"status_code" => request.response_code, "headers" => request.header_str, "body" => request.body_str}
|
63
|
+
if (/json/i).match(request.content_type)
|
64
|
+
response["resources"] = JSON.parse(response["body"])
|
65
|
+
Flareshow::Util.log_info(response["status_code"])
|
66
|
+
end
|
67
|
+
response
|
68
|
+
end
|
69
|
+
|
70
|
+
# authenticate with the server using an http post
|
71
|
+
def authenticate(login, password)
|
72
|
+
params = [
|
73
|
+
Curl::PostField.content("login", login),
|
74
|
+
Curl::PostField.content("password", password)
|
75
|
+
]
|
76
|
+
response = post(auth_endpoint, params)
|
77
|
+
Flareshow::Util.log_info(response)
|
78
|
+
# store the auth token returned from the authentication request
|
79
|
+
if response["status_code"] == 200
|
80
|
+
@key = response["resources"]["data"]["auth_token"]
|
81
|
+
response
|
82
|
+
else
|
83
|
+
raise Flareshow::AuthenticationFailed
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# clear the authenticated session
|
88
|
+
def logout
|
89
|
+
@key = nil
|
90
|
+
end
|
91
|
+
|
92
|
+
# query the server with an http post of the query params
|
93
|
+
def query(params={})
|
94
|
+
raise Flareshow::AuthenticationRequired unless @key
|
95
|
+
|
96
|
+
# add the json request parts
|
97
|
+
params = [
|
98
|
+
Curl::PostField.content("key", @key, 'application/json'),
|
99
|
+
Curl::PostField.content("query", params.to_json, 'application/json')
|
100
|
+
]
|
101
|
+
|
102
|
+
post(api_endpoint, params)
|
103
|
+
end
|
104
|
+
|
105
|
+
# commit changes to the server with an http post
|
106
|
+
def commit(params={}, files=[])
|
107
|
+
raise Flareshow::AuthenticationRequired unless @key
|
108
|
+
|
109
|
+
curl_params = []
|
110
|
+
has_files = false
|
111
|
+
if params["posts"]
|
112
|
+
# add any file parts passed in and assign a part id to the
|
113
|
+
params["posts"] = (params["posts"]).map do |f|
|
114
|
+
if f["files"]
|
115
|
+
f["files"] = (f["files"]).each do |ff|
|
116
|
+
has_files = true
|
117
|
+
curl_params.push(Curl::PostField.file(ff["part_id"], ff["file_path"]))
|
118
|
+
end
|
119
|
+
end
|
120
|
+
f
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
params["files"] = []
|
125
|
+
|
126
|
+
# add the json request parts
|
127
|
+
curl_params += [
|
128
|
+
Curl::PostField.content("key", @key, 'application/json'),
|
129
|
+
Curl::PostField.content("data", params.to_json, 'application/json')
|
130
|
+
]
|
131
|
+
|
132
|
+
post(api_endpoint, curl_params)
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
data/lib/user.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
class User < Flareshow::Resource
|
2
|
+
|
3
|
+
# =================
|
4
|
+
# = Class Methods =
|
5
|
+
# =================
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# return the current authenticated user
|
9
|
+
def current
|
10
|
+
@current
|
11
|
+
end
|
12
|
+
|
13
|
+
# authenticate user credentials
|
14
|
+
def log_in(login, password)
|
15
|
+
response = Flareshow::Service.authenticate(login, password)
|
16
|
+
user_data = response["resources"]["data"]
|
17
|
+
Flareshow::CacheManager.assimilate_resources({resource_key => [user_data]})
|
18
|
+
@current = User.get_from_cache(user_data["id"])
|
19
|
+
end
|
20
|
+
|
21
|
+
# ==================
|
22
|
+
# = Authentication =
|
23
|
+
# ==================
|
24
|
+
def logout
|
25
|
+
Flareshow::Service.logout
|
26
|
+
@current = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# ====================
|
32
|
+
# = Instance Methods =
|
33
|
+
# ====================
|
34
|
+
|
35
|
+
# ================
|
36
|
+
# = Associations =
|
37
|
+
# ================
|
38
|
+
def flows
|
39
|
+
Flow.find({"user_id" => ["in", id]})
|
40
|
+
end
|
41
|
+
|
42
|
+
def posts
|
43
|
+
Post.find({"user_id" => ["in", id]})
|
44
|
+
end
|
45
|
+
|
46
|
+
def comments
|
47
|
+
Comment.find({"user_id" => ["in", id]})
|
48
|
+
end
|
49
|
+
|
50
|
+
def files
|
51
|
+
File.find({"user_id" => ["in", id]})
|
52
|
+
end
|
53
|
+
|
54
|
+
def logged_in?
|
55
|
+
@current
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/util.rb
ADDED
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zenbe-flareshow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Will Bailey
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-03 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: thoughtbot-shoulda
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: json
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: curb
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: facets
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: facets/dictionary
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: uuid
|
67
|
+
type: :development
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
description: "TODO: a ruby gem for interacting with the shareflow collaboration service by Zenbe"
|
76
|
+
email: will.bailey@gmail.com
|
77
|
+
executables: []
|
78
|
+
|
79
|
+
extensions: []
|
80
|
+
|
81
|
+
extra_rdoc_files:
|
82
|
+
- LICENSE
|
83
|
+
- README.txt
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- Flareshow.gemspec
|
87
|
+
- LICENSE
|
88
|
+
- README.txt
|
89
|
+
- Rakefile
|
90
|
+
- TODO
|
91
|
+
- VERSION
|
92
|
+
- lib/cache.rb
|
93
|
+
- lib/comment.rb
|
94
|
+
- lib/exceptions.rb
|
95
|
+
- lib/file_attachment.rb
|
96
|
+
- lib/flareshow.rb
|
97
|
+
- lib/flow.rb
|
98
|
+
- lib/invitation.rb
|
99
|
+
- lib/membership.rb
|
100
|
+
- lib/post.rb
|
101
|
+
- lib/resource.rb
|
102
|
+
- lib/server.rb
|
103
|
+
- lib/service.rb
|
104
|
+
- lib/user.rb
|
105
|
+
- lib/util.rb
|
106
|
+
- test/flareshow_test.rb
|
107
|
+
- test/test_helper.rb
|
108
|
+
has_rdoc: true
|
109
|
+
homepage: http://github.com/willbailey/flareshow
|
110
|
+
licenses:
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options:
|
113
|
+
- --charset=UTF-8
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: "0"
|
121
|
+
version:
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: "0"
|
127
|
+
version:
|
128
|
+
requirements: []
|
129
|
+
|
130
|
+
rubyforge_project: flareshow
|
131
|
+
rubygems_version: 1.3.5
|
132
|
+
signing_key:
|
133
|
+
specification_version: 2
|
134
|
+
summary: "TODO: a ruby gem for interacting with the shareflow collaboration service"
|
135
|
+
test_files:
|
136
|
+
- test/flareshow_test.rb
|
137
|
+
- test/test_helper.rb
|