rox 0.1.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.
- data/README +1 -0
- data/examples/workspace.rb +11 -0
- data/lib/rox.rb +10 -0
- data/lib/rox/client.rb +78 -0
- data/lib/rox/config_tree.rb +27 -0
- data/lib/rox/dependencies.rb +2 -0
- data/lib/rox/exception.rb +14 -0
- data/lib/rox/folders.rb +59 -0
- data/lib/rox/response.rb +29 -0
- data/lib/rox/rox.rb +170 -0
- data/lib/rox/simple_module.rb +42 -0
- data/lib/rox/webconversation.rb +96 -0
- metadata +86 -0
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Try it out at: http://www.ox.io
|
data/lib/rox.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/rox/dependencies'
|
2
|
+
require File.dirname(__FILE__)+'/rox/rox'
|
3
|
+
require File.dirname(__FILE__)+'/rox/webconversation'
|
4
|
+
require File.dirname(__FILE__)+'/rox/client'
|
5
|
+
require File.dirname(__FILE__)+'/rox/response'
|
6
|
+
require File.dirname(__FILE__)+'/rox/exception'
|
7
|
+
require File.dirname(__FILE__)+'/rox/config_tree'
|
8
|
+
require File.dirname(__FILE__)+'/rox/folders'
|
9
|
+
require File.dirname(__FILE__)+'/rox/simple_module'
|
10
|
+
|
data/lib/rox/client.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
module ROX
|
2
|
+
|
3
|
+
class Client
|
4
|
+
|
5
|
+
attr_accessor :webconversation
|
6
|
+
attr_accessor :session
|
7
|
+
|
8
|
+
def initialize(options = nil)
|
9
|
+
if options
|
10
|
+
host = options[:host]
|
11
|
+
raise "Please specify an option 'host'" unless host
|
12
|
+
@webconversation = ROX::WebConversation.new(host)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def login(username, password)
|
17
|
+
response = @webconversation.get("/ajax/login", :action => :login, :name => username, :password => password)
|
18
|
+
response = ROX::Response.new(JSON.parse(response))
|
19
|
+
raise OXException.new(response) if response.error?
|
20
|
+
@session = response["session"]
|
21
|
+
|
22
|
+
if block_given?
|
23
|
+
begin
|
24
|
+
return yield(self)
|
25
|
+
ensure
|
26
|
+
logout
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def logout
|
33
|
+
response = @webconversation.get("/ajax/login", :action => :logout, :session => session)
|
34
|
+
if(response and response != "")
|
35
|
+
response = ROX::Response.new(JSON.parse(response))
|
36
|
+
raise OXException.new(response) if response.error?
|
37
|
+
end
|
38
|
+
@session = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def logged_in?
|
42
|
+
!@session.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
def get(path, parameters = {})
|
46
|
+
parameters[:session] ||= @session
|
47
|
+
@webconversation.get(path, parameters)
|
48
|
+
end
|
49
|
+
|
50
|
+
def put(path, parameters = {})
|
51
|
+
parameters[:session] ||= @session
|
52
|
+
@webconversation.put(path, parameters)
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_response(path, parameters = {})
|
56
|
+
response = ROX::Response.new(JSON.parse(get(path, parameters)))
|
57
|
+
raise ROX::OXException.new(response) if response.error?
|
58
|
+
return response;
|
59
|
+
end
|
60
|
+
|
61
|
+
def put_response(path, parameters = {})
|
62
|
+
response = ROX::Response.new(JSON.parse(put(path, parameters)))
|
63
|
+
raise ROX::OXException.new(response) if response.error?
|
64
|
+
return response
|
65
|
+
end
|
66
|
+
|
67
|
+
def in_module(moduleName, &block)
|
68
|
+
mod = self.module(moduleName)
|
69
|
+
return mod.instance_eval(&block) if block_given?
|
70
|
+
return mod
|
71
|
+
end
|
72
|
+
|
73
|
+
def module(moduleName)
|
74
|
+
ROX::SimpleModule.new("/ajax/"+moduleName.to_s, self)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ROX
|
2
|
+
class ConfigTree
|
3
|
+
def initialize(client)
|
4
|
+
@client = client
|
5
|
+
end
|
6
|
+
|
7
|
+
def [](path)
|
8
|
+
return @client.get_response("/ajax/config/#{path}")["data"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(path, value)
|
12
|
+
@client.put("/ajax/config/#{path}", :body => value.to_json)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
class Client
|
19
|
+
|
20
|
+
def config
|
21
|
+
@config ||= ROX::ConfigTree.new(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/rox/folders.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module ROX
|
2
|
+
|
3
|
+
columns(:common) {
|
4
|
+
|
5
|
+
column(1, :id, :string)
|
6
|
+
column(2, :created_by, :string)
|
7
|
+
column(3, :modified_by, :string)
|
8
|
+
column(4, :creation_date, :time)
|
9
|
+
column(5, :last_modified, :time)
|
10
|
+
column(6, :last_modified_utc, :timestamp)
|
11
|
+
column(20, :folder_id, :string)
|
12
|
+
|
13
|
+
}
|
14
|
+
|
15
|
+
columns(:folder => :common) {
|
16
|
+
|
17
|
+
column(300, :title, :string)
|
18
|
+
column(301, :module, :string)
|
19
|
+
column(302, :type, :mappable, {1 => :private, 2 => :public, 3 => :shared, 5 => :system})
|
20
|
+
column(304, :boolean, :subfolders)
|
21
|
+
#column(305, :own_rights, :)
|
22
|
+
#column(306, :permissions, :)
|
23
|
+
column(307, :summary, :string)
|
24
|
+
column(308, :standard_folder, :boolean)
|
25
|
+
column(309, :total, :number)
|
26
|
+
column(310, :new, :number)
|
27
|
+
column(311, :unread, :number)
|
28
|
+
column(312, :deleted, :number)
|
29
|
+
column(313, :capabilities, :number)
|
30
|
+
column(314, :subscribed, :boolean)
|
31
|
+
column(315, :subscr_subflds, :boolean)
|
32
|
+
}
|
33
|
+
|
34
|
+
|
35
|
+
class Folders
|
36
|
+
def initialize(client)
|
37
|
+
@client = client
|
38
|
+
@columns = COLUMN_DEFINITIONS[:folder]
|
39
|
+
end
|
40
|
+
|
41
|
+
def root(*columns)
|
42
|
+
column_list = get_columns(*columns)
|
43
|
+
@client.get_response("/ajax/folders", :action => :root, :columns => column_list.to_s)
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_columns(*columns)
|
47
|
+
@columns.sublist(*columns)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
class Client
|
54
|
+
def folders
|
55
|
+
Folders.new(self)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/lib/rox/response.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module ROX
|
2
|
+
class Response
|
3
|
+
def initialize(ox_response)
|
4
|
+
@oxresponse = ox_response
|
5
|
+
end
|
6
|
+
|
7
|
+
def error?
|
8
|
+
@oxresponse.include?('error')
|
9
|
+
end
|
10
|
+
|
11
|
+
def error
|
12
|
+
return nil unless error?
|
13
|
+
sprintf(@oxresponse['error'], *@oxresponse['error_params'])
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](key)
|
17
|
+
@oxresponse[key.to_s]
|
18
|
+
end
|
19
|
+
|
20
|
+
def data
|
21
|
+
@oxresponse['data']
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
@oxresponse.inspect
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/rox/rox.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
module ROX
|
2
|
+
|
3
|
+
class Column
|
4
|
+
|
5
|
+
attr_accessor :number, :name, :type, :options
|
6
|
+
|
7
|
+
def initialize(number, name, type, options = nil)
|
8
|
+
@number = number
|
9
|
+
@name = name
|
10
|
+
@type = type
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_ox(client, value)
|
15
|
+
value
|
16
|
+
end
|
17
|
+
|
18
|
+
def from_ox(client, value)
|
19
|
+
value
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class MappableColumn < Column
|
25
|
+
def initialize(number, name, type, options)
|
26
|
+
super(number, name, type, options)
|
27
|
+
@from_ox = options
|
28
|
+
@inverse = @from_ox.inject({}) {|memo, pair| memo[pair[1]] = pair[0]; memo} if options
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_ox(client, value)
|
32
|
+
@inverse[value]
|
33
|
+
end
|
34
|
+
|
35
|
+
def from_ox(client, value)
|
36
|
+
@from_ox[value]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
class TimeColumn < Column
|
42
|
+
|
43
|
+
def from_ox(client, value)
|
44
|
+
Time.at(client.config.timezone.local_to_utc(value / 1000))
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_ox(client, value)
|
48
|
+
client.config.timezone.utc_to_local(value.to_i) * 1000
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class DateColumn < Column
|
54
|
+
|
55
|
+
def from_ox(client, value)
|
56
|
+
Time.at(value / 1000)
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_ox(client, value)
|
60
|
+
value.to_i * 1000
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
class ComplexColumn < Column
|
66
|
+
def initialize(number, name, type, klass)
|
67
|
+
super(number, name, type, klass)
|
68
|
+
@klass = klass
|
69
|
+
end
|
70
|
+
|
71
|
+
def from_ox(client, value)
|
72
|
+
@klass.new(value)
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_ox(client, value)
|
76
|
+
value.to_ox
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class ColumnList
|
81
|
+
attr_reader :columns, :ancestors
|
82
|
+
|
83
|
+
def initialize(ancestors = nil)
|
84
|
+
@ancestors = ancestors || Array.new
|
85
|
+
@columns = Hash.new
|
86
|
+
@columns_by_number = Hash.new
|
87
|
+
end
|
88
|
+
|
89
|
+
def column(number, name, type, options = nil)
|
90
|
+
case(type)
|
91
|
+
when :date
|
92
|
+
klass = DateColumn
|
93
|
+
when :time
|
94
|
+
klass = TimeColumn
|
95
|
+
when :mappable
|
96
|
+
klass = MappableColumn
|
97
|
+
when :complex
|
98
|
+
klass = ComplexColumn
|
99
|
+
else
|
100
|
+
klass = Column
|
101
|
+
end
|
102
|
+
column = klass.new(number, name, type, options)
|
103
|
+
add_column(column)
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
def add_column(column_def)
|
108
|
+
@columns[column_def.name] = column_def
|
109
|
+
@columns_by_number[column_def.number] = column_def
|
110
|
+
end
|
111
|
+
|
112
|
+
def [](key)
|
113
|
+
return @columns[key] if @columns.include?(key)
|
114
|
+
return @columns_by_number[key] if @columns_by_number.include?(key)
|
115
|
+
@ancestors.each do
|
116
|
+
|ancestor|
|
117
|
+
column = ancestor[key]
|
118
|
+
return column if column
|
119
|
+
end
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def sublist(*keys)
|
124
|
+
sublist = ColumnList.new
|
125
|
+
keys.each do
|
126
|
+
|key|
|
127
|
+
value = self[key]
|
128
|
+
sublist.add_column(value)
|
129
|
+
end
|
130
|
+
sublist
|
131
|
+
end
|
132
|
+
|
133
|
+
def to_s
|
134
|
+
@columns_by_number.keys.join(",")
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
class ColumnDefinitions
|
141
|
+
|
142
|
+
def initialize
|
143
|
+
@definitions = Hash.new
|
144
|
+
end
|
145
|
+
|
146
|
+
def columns(typedef, &block)
|
147
|
+
name = typedef
|
148
|
+
ancestors = Array.new
|
149
|
+
if(typedef.kind_of?(Hash))
|
150
|
+
name = typedef.keys.first
|
151
|
+
ancestors = [*typedef[name]].map{|ancestor| self[ancestor]}
|
152
|
+
end
|
153
|
+
column_list = ColumnList.new(ancestors)
|
154
|
+
column_list.instance_eval(&block)
|
155
|
+
@definitions[name] = column_list
|
156
|
+
end
|
157
|
+
|
158
|
+
def [](key)
|
159
|
+
@definitions[key]
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
COLUMN_DEFINITIONS = ColumnDefinitions.new
|
165
|
+
|
166
|
+
def self.columns(options, &block)
|
167
|
+
COLUMN_DEFINITIONS.columns(options, &block)
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ROX
|
2
|
+
|
3
|
+
class SimpleModule
|
4
|
+
def initialize(path, client)
|
5
|
+
@path = path
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def _get(params)
|
10
|
+
@client.get_response(@path, params)
|
11
|
+
end
|
12
|
+
|
13
|
+
def _put(params)
|
14
|
+
@client.put_response(@path, params)
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(action, params)
|
18
|
+
_call(action, params)
|
19
|
+
end
|
20
|
+
|
21
|
+
def _call(action, params)
|
22
|
+
params[:action] = action
|
23
|
+
if(params[:body])
|
24
|
+
params[:body] = params[:body].to_json
|
25
|
+
_put(params)
|
26
|
+
elsif params[:body_raw]
|
27
|
+
params[:body] = params[:body_raw]
|
28
|
+
params.delete(:body_raw)
|
29
|
+
_put(params)
|
30
|
+
else
|
31
|
+
_get(params)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(meth, *args, &block)
|
36
|
+
params = args.first || {}
|
37
|
+
_call(meth, params)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'open-uri'
|
4
|
+
|
5
|
+
module ROX
|
6
|
+
class WebConversation
|
7
|
+
attr_accessor :cookies
|
8
|
+
|
9
|
+
def initialize(uri)
|
10
|
+
@cookies = Hash.new
|
11
|
+
@uri = URI.parse(uri)
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(url, params = {})
|
15
|
+
req = Net::HTTP::Get.new(url+"?"+query(params))
|
16
|
+
res = request(req)
|
17
|
+
return res.body
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(url, params = {})
|
21
|
+
stringified_params = Hash.new
|
22
|
+
params.each do
|
23
|
+
|key, value|
|
24
|
+
next if key == :body or key == 'body'
|
25
|
+
stringified_params[key.to_s] = value.to_s
|
26
|
+
end
|
27
|
+
req = Net::HTTP::Post.new(url)
|
28
|
+
req.set_form_data(stringified_params)
|
29
|
+
req.body = params[:body] || params['body']
|
30
|
+
res = request(req)
|
31
|
+
return res.body
|
32
|
+
end
|
33
|
+
|
34
|
+
def put(url, params = {})
|
35
|
+
req = Net::HTTP::Put.new(url+"?"+query(params))
|
36
|
+
req.body = params[:body] || params['body']
|
37
|
+
res = request(req)
|
38
|
+
return res.body
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(url, params = {})
|
42
|
+
req = Net::HTTP::Delete.new(url+"?"+query(params))
|
43
|
+
res = request(req)
|
44
|
+
return res.body
|
45
|
+
end
|
46
|
+
|
47
|
+
def remember_cookies(res)
|
48
|
+
setCookie = res['Set-Cookie']
|
49
|
+
return unless setCookie
|
50
|
+
cookies = setCookie.split(/\s*[,;]\s*/)
|
51
|
+
|
52
|
+
cookies.each do
|
53
|
+
|cookieDef|
|
54
|
+
if (cookieDef =~ /^(JS|open-xchange)/)
|
55
|
+
key, value = cookieDef.split(/\=/)
|
56
|
+
@cookies[key] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_cookies(req)
|
62
|
+
return if @cookies.empty?
|
63
|
+
cookie_string = ""
|
64
|
+
@cookies.each do
|
65
|
+
|key, value|
|
66
|
+
cookie_string << "#{key}=#{value},"
|
67
|
+
end
|
68
|
+
req["Cookie"] = cookie_string[0...cookie_string.size-1]
|
69
|
+
end
|
70
|
+
|
71
|
+
def query(params)
|
72
|
+
query = ""
|
73
|
+
params.each do
|
74
|
+
|key, value|
|
75
|
+
next if key == :body || key == 'body'
|
76
|
+
query += '&'
|
77
|
+
query += URI.escape(key.to_s)+"="+URI.escape(value.to_s)
|
78
|
+
end
|
79
|
+
query = query[1..query.size]
|
80
|
+
return query
|
81
|
+
end
|
82
|
+
|
83
|
+
def request(req)
|
84
|
+
add_cookies(req)
|
85
|
+
http_session = Net::HTTP.new(@uri.host, @uri.port)
|
86
|
+
http_session.use_ssl = @uri.scheme == "https"
|
87
|
+
res = http_session.start do
|
88
|
+
|http|
|
89
|
+
http.request(req)
|
90
|
+
end
|
91
|
+
remember_cookies(res)
|
92
|
+
return res
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rox
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Francisco Laguna
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-11 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: tzinfo
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
description: An Open-Xchange HTTP API client library
|
38
|
+
email: fla@synapps.de
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files: []
|
44
|
+
|
45
|
+
files:
|
46
|
+
- lib/rox.rb
|
47
|
+
- lib/rox/client.rb
|
48
|
+
- lib/rox/config_tree.rb
|
49
|
+
- lib/rox/dependencies.rb
|
50
|
+
- lib/rox/exception.rb
|
51
|
+
- lib/rox/folders.rb
|
52
|
+
- lib/rox/response.rb
|
53
|
+
- lib/rox/rox.rb
|
54
|
+
- lib/rox/simple_module.rb
|
55
|
+
- lib/rox/webconversation.rb
|
56
|
+
- examples/workspace.rb
|
57
|
+
- README
|
58
|
+
homepage: http://www.ox.io
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.8.1
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: An Open-Xchange HTTP API client library
|
85
|
+
test_files: []
|
86
|
+
|