confluence4r 0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 41dcb8b373b04bf307d72faacb8344bd9a8beea4
4
+ data.tar.gz: 723450b5298bd65a9190f390baa09fd4801576ca
5
+ SHA512:
6
+ metadata.gz: 5fd675efc44a93f1f50a33d297c560cc9b71421d5dd35f024626b30d59ad4d1a6699b475e069853e0c6a25477ee3a9faed289e0eb41247e15072bf0b9e6bf7c6
7
+ data.tar.gz: 403f58e7d0e9ec5eb4c4120325429088cd1aa3559859380b5b9e9250c19a2ec2d7826c6ac147831e3725342625fd2bd62504faa73784017376940868e560a448
@@ -0,0 +1,64 @@
1
+ require 'yaml'
2
+
3
+ require 'confluence/confluence_rpc'
4
+
5
+ class Confluence::Connector
6
+
7
+ attr_accessor :username, :password, :default_service, :url,
8
+ :admin_proxy_username, :admin_proxy_password
9
+
10
+ def initialize(options = {})
11
+ load_confluence_config
12
+ @url ||= options[:url]
13
+ @username = options[:username] || @username
14
+ @password = options[:password] || @password
15
+ @default_service = options[:service] || 'confluence1'
16
+ end
17
+
18
+ def connect(service = nil)
19
+ unless url and ((admin_proxy_username and admin_proxy_password) or (username and password)) and service || default_service
20
+ raise "Cannot get Confluence::RPC instance because the confluence url, username, password, or service have not been set"
21
+ end
22
+
23
+ rpc = Confluence::RPC.new(url, service || default_service)
24
+ rpc.login(username, password)
25
+
26
+ return rpc
27
+ end
28
+
29
+ def load_confluence_config
30
+ conf = YAML.load_file("#{RAILS_ROOT}/config/confluence.yml")[RAILS_ENV]
31
+ @url = conf['url'] || conf[:url]
32
+ @default_service = conf['service'] || conf[:service]
33
+ @admin_proxy_username = conf['admin_proxy_username'] || conf[:admin_proxy_username]
34
+ @admin_proxy_password = conf['admin_proxy_password'] || conf[:admin_proxy_password]
35
+ # take these params manually if that's what is sent
36
+ @username = conf['username'] || conf[:username] || @admin_proxy_username
37
+ @password = conf['password'] || conf[:password] || @admin_proxy_password
38
+ end
39
+
40
+ def self.default_confluence_url
41
+ conf = YAML.load_file("#{RAILS_ROOT}/config/confluence.yml")[RAILS_ENV]
42
+ conf['url'] || conf[:url]
43
+ end
44
+
45
+ # The given block will be executed using another account, as set in the confluence.yml file under admin_proxy_username
46
+ # and admin_proxy_password. This is useful when you want to execute some function that requires admin privileges. You
47
+ # will of course have to set up a corresponding account on your Confluence server with administrative rights.
48
+ def self.through_admin_proxy
49
+ super_connector = Confluence::Connector.new
50
+
51
+ raise "Cannot execute through_admin_proxy because the admin_proxy_username has not been set." unless super_connector.admin_proxy_username
52
+ raise "Cannot execute through_admin_proxy because the admin_proxy_password has not been set." unless super_connector.admin_proxy_password
53
+
54
+ super_connector.username = super_connector.admin_proxy_username
55
+ super_connector.password = super_connector.admin_proxy_password
56
+
57
+ normal_connector = Confluence::RemoteDataObject.connector
58
+ Confluence::RemoteDataObject.connector = super_connector
59
+
60
+ yield
61
+
62
+ Confluence::RemoteDataObject.connector = normal_connector
63
+ end
64
+ end
@@ -0,0 +1,187 @@
1
+ require 'active_support'
2
+ require 'confluence/confluence_connector'
3
+
4
+ # Abstract object representing some piece of data in Confluence.
5
+ # This must be overridden by a child class that defines values
6
+ # for the class attributes save_method and get_method and/or
7
+ # implements its own get and save methods.
8
+ class Confluence::RemoteDataObject
9
+ # include Reloadable
10
+
11
+ class_inheritable_accessor :attr_conversions, :readonly_attrs
12
+ class_inheritable_accessor :save_method, :get_method, :destroy_method
13
+
14
+ attr_accessor :attributes
15
+
16
+ attr_accessor :confluence, :encore
17
+
18
+ @@connector = nil
19
+
20
+ def self.connector=(connector)
21
+ @@connector = connector
22
+ end
23
+
24
+ def self.connector
25
+ @@connector
26
+ end
27
+
28
+ def self.confluence
29
+ raise "Cannot establish confluence connection because the connector has not been set." unless @@connector
30
+ @@connector.connect
31
+ end
32
+
33
+ def confluence
34
+ raise "Cannot establish confluence connection because the connector has not been set." unless @@connector
35
+ @@connector.connect
36
+ end
37
+
38
+ # TODO: encore-specific code like this probably shouldn't be here...
39
+ def self.encore
40
+ raise "Cannot establish confluence connection because the connector has not been set." unless @@connector
41
+ @@connector.connect("encore")
42
+ end
43
+
44
+ def encore
45
+ raise "Cannot establish confluence connection because the connector has not been set." unless @@connector
46
+ @@connector.connect("encore")
47
+ end
48
+
49
+ def initialize(data_object = nil)
50
+ self.attributes = {}
51
+ load_from_object(data_object) unless data_object.nil?
52
+ end
53
+
54
+ def load_from_object(data_object)
55
+ data_object.each do |attr, value|
56
+ if self.class.attr_conversions.has_key? attr.to_sym
57
+ value = self.send("as_#{attr_conversions[attr.to_sym]}", value)
58
+ end
59
+ self.send("#{attr}=", value)
60
+ end
61
+ end
62
+
63
+ def save
64
+ before_save if respond_to? :before_save
65
+
66
+ data = {} unless data
67
+
68
+ self.attributes.each do |attr,value|
69
+ data[attr.to_s] = value.to_s unless self.readonly_attrs.include? attr
70
+ end
71
+
72
+ raise NotImplementedError.new("Can't call #{self}.save because no @@save_method is defined for this class") unless self.save_method
73
+
74
+ self.confluence.send(self.class.send(:save_method), data)
75
+
76
+ # we need to reload because the version number has probably changed, we want the new ID, etc.
77
+ reload
78
+
79
+ after_save if respond_to? :after_save
80
+ end
81
+
82
+ def reload
83
+ before_reload if respond_to? :before_reload
84
+
85
+ if self.id
86
+ self.load_from_object(self.class.send(:get, self.id))
87
+ else
88
+ # We don't have an ID, so try to use alternate method for reloading. (This is for newly created records that may not yet have an id assigned)
89
+ raise NotImplementedError, "Can't reload this #{self.class} because it does not have an id and does not implement the reload_newly_created! method." unless self.respond_to? :reload_newly_created!
90
+ self.reload_newly_created!
91
+ end
92
+
93
+ after_reload if respond_to? :after_reload
94
+ end
95
+
96
+ def destroy
97
+ before_destroy if respond_to? :before_destroy
98
+
99
+ raise NotImplementedError.new("Can't call #{self}.destroy because no @@destroy_method is defined for this class") unless self.destroy_method
100
+ eval "confluence.#{self.destroy_method}(self.id.to_s)"
101
+
102
+ after_destroy if respond_to? :after_destroy
103
+ end
104
+
105
+ def [](attr)
106
+ self.attributes[attr]
107
+ end
108
+
109
+ def []=(attr, value)
110
+ self.attributes[attr] = value
111
+ end
112
+
113
+ def id
114
+ self[:id]
115
+ end
116
+
117
+ def method_missing(name, *args)
118
+ if name.to_s =~ /^(.*?)=$/
119
+ self[$1.intern] = args[0]
120
+ elsif name.to_s =~ /^[\w_]+$/
121
+ self[name]
122
+ else
123
+ raise NoMethodError, name.to_s
124
+ end
125
+ end
126
+
127
+ def ==(obj)
128
+ if obj.kind_of? self.class
129
+ self.attributes == obj.attributes
130
+ else
131
+ super
132
+ end
133
+ end
134
+
135
+ ### class methods #########################################################
136
+
137
+ def self.find(id)
138
+ r = get(id)
139
+ self.new(r)
140
+ end
141
+
142
+ ### type conversions ######################################################
143
+
144
+ # TODO: put these in a module?
145
+ def as_int(val)
146
+ val.to_i
147
+ end
148
+
149
+ def as_string(val)
150
+ val.to_s
151
+ end
152
+
153
+ def as_boolean(val)
154
+ val == "true"
155
+ end
156
+
157
+ def as_datetime(val)
158
+ if val.is_a?(String)
159
+ # for older versions of Confluence (pre 2.7?)
160
+ val =~ /\w{3} (\w{3}) (\d{2}) (\d{2}):(\d{2}):(\d{2}) (\w{3}) (\d{4})/
161
+ month = $1
162
+ day = $2
163
+ hour = $3
164
+ minute = $4
165
+ second = $5
166
+ tz = $6
167
+ year = $7
168
+ Time.local(year, month, day, hour, minute, second)
169
+ else
170
+ Time.local(val.year, val.month, val.day, val.hour, val.min, val.sec)
171
+ end
172
+ end
173
+
174
+ ###########################################################################
175
+
176
+ protected
177
+ # Returns the raw XML-RPC anonymous object with the data corresponding to
178
+ # the given id. This depends on the get_method class attribute, which must
179
+ # be defined for this method to work.
180
+ def self.get(id)
181
+ raise NotImplementedError.new("Can't call #{self}.get(#{id}) because no get_method is defined for this class") unless self.get_method
182
+ raise ArgumentError.new("You must specify a #{self} id!") unless id
183
+ confluence.log.debug("get_method for #{self} is #{self.get_method}")
184
+ obj = confluence.send(self.send(:get_method), id.to_s)
185
+ return obj
186
+ end
187
+ end
@@ -0,0 +1,83 @@
1
+ require 'xmlrpc/client'
2
+ require 'logger'
3
+
4
+ # A useful helper for running Confluence XML-RPC from Ruby. Takes care of
5
+ # adding the token to each method call (so you can call server.getSpaces()
6
+ # instead of server.getSpaces(token)). Also takes care of re-logging in
7
+ # if your login times out.
8
+ #
9
+ # Usage:
10
+ #
11
+ # server = Confluence::RPC.new("http://confluence.atlassian.com")
12
+ # server.login("user", "password")
13
+ # puts server.getSpaces()
14
+ #
15
+ module Confluence
16
+
17
+ class RPC
18
+ attr_reader :log
19
+
20
+ def initialize(server_url, proxy = "confluence1")
21
+ server_url += "/rpc/xmlrpc" unless server_url[-11..-1] == "/rpc/xmlrpc"
22
+ @server_url = server_url
23
+ server = XMLRPC::Client.new2(server_url)
24
+ @conf = server.proxy(proxy)
25
+ @token = "12345"
26
+
27
+ @log = Logger.new "#{RAILS_ROOT}/log/confluence4r.log"
28
+ end
29
+
30
+ def login(username, password)
31
+ log.info "Logging in as '#{username}'."
32
+ @user = username
33
+ @pass = password
34
+ do_login()
35
+ end
36
+
37
+ def method_missing(method_name, *args)
38
+ log.info "Calling #{method_name}(#{args.inspect})."
39
+ begin
40
+ @conf.send(method_name, *([@token] + args))
41
+ rescue XMLRPC::FaultException => e
42
+ log.error "#{e}: #{e.message}"
43
+ if (e.faultString.include?("InvalidSessionException"))
44
+ do_login
45
+ retry
46
+ else
47
+ raise RemoteException, e.respond_to?(:message) ? e.message : e
48
+ end
49
+ rescue
50
+ log.error "#{$!}"
51
+ raise $!
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def do_login()
58
+ begin
59
+ @token = @conf.login(@user, @pass)
60
+ rescue XMLRPC::FaultException => e
61
+ log.error "#{e}: #{e.faultString}"
62
+ raise RemoteAuthenticationException, e
63
+ end
64
+ end
65
+ end
66
+
67
+
68
+ class RemoteException < Exception
69
+ def initialize(msg = nil, type = nil)
70
+ if msg.kind_of? XMLRPC::FaultException
71
+ msg.faultString =~ /^.*?:\s(.*?):\s(.*)/
72
+ msg = $2
73
+ type = $1
74
+ end
75
+
76
+ super(msg)
77
+ @type = type
78
+ end
79
+ end
80
+
81
+ class RemoteAuthenticationException < RemoteException
82
+ end
83
+ end
@@ -0,0 +1,117 @@
1
+ # Allows for reading and writing a {metadata-list} macro in a page's content.
2
+ #
3
+ # For example, if your wiki content is:
4
+ #
5
+ # Hello, World!
6
+ # {metadata-list}
7
+ # || Foo | Bar |
8
+ # || Fruit | Orange |
9
+ # {metadata-list}
10
+ #
11
+ # You may do this (assuming that you have the above text stored in a variable
12
+ # called 'wiki_content'):
13
+ #
14
+ # m = Confluence::Metadata.new(wiki_content)
15
+ # puts m['Foo'] # outputs "Bar"
16
+ # puts m['Fruit'] # outputs "Orange"
17
+ #
18
+ # m['Fruit'] = "Banana"
19
+ # puts m['Fruit'] # outputs "Banana"
20
+ # m['Hello'] = "Goodbye"
21
+ #
22
+ # ... and your wiki_content now holds:
23
+ #
24
+ # Hello, World!
25
+ # {metadata-list}
26
+ # || Foo | Bar |
27
+ # || Fruit | Banana |
28
+ # || Hello | Goodbye |
29
+ # {metadata-list}
30
+ #
31
+ class Confluence::Metadata
32
+ include Enumerable
33
+
34
+ def initialize(page)
35
+ raise ArgumentError.new("Argument passed to Confluence::Metadata must be a Confluence::Page") unless page.kind_of? Confluence::Page
36
+ @page = page
37
+ end
38
+
39
+ def [](metadata_key)
40
+ extract_metadata_from_content[metadata_key]
41
+ end
42
+
43
+ def []=(metadata_key, value)
44
+ metadata = extract_metadata_from_content
45
+ metadata[metadata_key] = value
46
+ replace_metadata_in_content(metadata)
47
+ value
48
+ end
49
+
50
+ def each
51
+ metadata = extract_metadata_from_content
52
+ metadata.each{|k,v| yield k,v}
53
+ end
54
+
55
+ def include?(key)
56
+ not self.find{|k,v| k}.nil?
57
+ end
58
+ alias_method :has_key?, :include?
59
+
60
+ def empty?
61
+ extract_metadata_from_content.empty?
62
+ end
63
+
64
+ # Merges a hash (or some other Hash-like, Enumerable object) into
65
+ # this metadata. That is, key-value pairs from the given object will be added
66
+ # as metadata to this Confluence::Metadata's page, overriding any existing duplicate keys.
67
+ def merge!(data)
68
+ data.each do |k,v|
69
+ self[k] = v
70
+ end
71
+ end
72
+
73
+ private
74
+ def extract_metadata_from_content
75
+ return {} if @page.content.nil?
76
+
77
+ in_body = false
78
+ metadata = {}
79
+ @page.content.each do |line|
80
+ #TODO: handle other kinds of metadata macros
81
+ in_body = !in_body if line =~ /\{metadata-list\}/
82
+
83
+ if in_body
84
+ metadata[$1] = $2 if line =~ /\|\|\s*(.*?)\s*\|\s*(.*?)\s*\|/
85
+ end
86
+ end
87
+
88
+ return metadata
89
+ end
90
+
91
+ def replace_metadata_in_content(metadata)
92
+ #TODO: handle cases where there are multiple {metadata-list} macros in a page
93
+ #TODO: handle other kinds of metadata macros
94
+
95
+ macro_name = "{metadata-list}"
96
+
97
+ @page.content = "" if @page.content.nil?
98
+
99
+ @page.content += "\n#{macro_name}\n\n#{macro_name}" unless @page.content.include? macro_name
100
+
101
+ metadata_start = @page.content.index(macro_name) + macro_name.size
102
+ metadata_end = @page.content.index(macro_name, metadata_start) - 1
103
+
104
+ longest_key = 0
105
+ metadata.each do |key, val|
106
+ longest_key = key.size if key.size > longest_key
107
+ end
108
+
109
+ metadata_body = ""
110
+ metadata.each do |key, val|
111
+ padding = " " * (longest_key - key.size)
112
+ metadata_body += "|| #{key}#{padding} | #{val} |\n"
113
+ end
114
+
115
+ @page.content[metadata_start..metadata_end] = "\n"+metadata_body
116
+ end
117
+ end
@@ -0,0 +1,139 @@
1
+ require 'confluence/confluence_remote_data_object'
2
+
3
+ # Exposes a vaguely ActiveRecord-like interface for dealing with Confluence::Pages
4
+ # in Confluence.
5
+ class Confluence::Page < Confluence::RemoteDataObject
6
+
7
+ self.save_method = :storePage
8
+ self.get_method = :getPage
9
+ self.destroy_method = :removePage
10
+
11
+ self.attr_conversions = {
12
+ :id => :int,
13
+ :version => :int,
14
+ :parentId => :int,
15
+ :current => :boolean,
16
+ :homePage => :boolean,
17
+ :created => :datetime,
18
+ :modified => :datetime
19
+ }
20
+
21
+ self.readonly_attrs = [:current, :created, :modified, :contentStatus]
22
+
23
+ DEFAULT_SPACE = "encore"
24
+
25
+ def initialize(data_object = nil)
26
+ super
27
+ #self.creator = self.confluence_user unless self.include? :creator
28
+ end
29
+
30
+ def name=(new_name)
31
+ self.title = new_name
32
+ end
33
+
34
+ def name
35
+ self.title
36
+ end
37
+
38
+ def content=(new_content)
39
+ # make sure metadata doesn't get overwritten
40
+ old_metadata = self.metadata.entries if self.content
41
+ super
42
+ self.metadata.merge! old_metadata if old_metadata
43
+ end
44
+
45
+ def load_from_object(data_object)
46
+ super
47
+ self.modifier = self.confluence_username unless self.attributes.include? :modifier
48
+ end
49
+
50
+ def metadata
51
+ Confluence::Metadata.new(self)
52
+ end
53
+
54
+ def parent
55
+ self.class.find(self.parentId)
56
+ end
57
+
58
+ def to_s
59
+ self.title
60
+ end
61
+
62
+ def edit_group=(group)
63
+ encore.setPageEditGroup(self.title, group)
64
+ end
65
+
66
+ def edit_group
67
+ perm = get_permissions
68
+ return nil if perm.nil? or perm.empty?
69
+ perm.each do |p|
70
+ return p['lockedBy'] if p['lockType'] == 'Edit'
71
+ end
72
+ return nil
73
+ end
74
+
75
+ def view_group=(group)
76
+ encore.setPageViewGroup(self.title, group)
77
+ end
78
+
79
+ def view_group
80
+ perm = get_permissions
81
+ return nil if perm.nil? or perm.empty?
82
+ perm.each do |p|
83
+ return p['lockedBy'] if p['lockType'] == 'View'
84
+ end
85
+ return nil
86
+ end
87
+
88
+ def get_permissions
89
+ confluence.getPagePermissions(self.id.to_s)
90
+ end
91
+
92
+ ### callbacks ###############################################################
93
+
94
+ def before_save
95
+ raise "Cannot save this page because it has no title and/or space." unless self.title and self.space
96
+ end
97
+
98
+ #############################################################################
99
+
100
+ ### class methods ###########################################################
101
+
102
+ def self.find_by_name(name, space = DEFAULT_SPACE)
103
+ find_by_title(name, space)
104
+ end
105
+
106
+ def self.find_by_title(title, space = DEFAULT_SPACE)
107
+ begin
108
+ r = confluence.getPage(space, title)
109
+ rescue Confluence::RemoteException => e
110
+ # FIXME: dangerous to be checking based on error string like this...
111
+ if e.message =~ /does not exist/
112
+ return nil
113
+ else
114
+ raise e
115
+ end
116
+ end
117
+ self.new(r)
118
+ end
119
+
120
+
121
+ #############################################################################
122
+
123
+ protected
124
+ def self.metadata_accessor(accessor, metadata_key)
125
+ f = <<-END
126
+ def #{accessor.to_s}
127
+ self.metadata['#{metadata_key}']
128
+ end
129
+ def #{accessor.to_s}=(val)
130
+ self.metadata['#{metadata_key}'] = val.gsub("\n", " ")
131
+ end
132
+ END
133
+ module_eval f
134
+ end
135
+
136
+ def reload_newly_created!
137
+ self.load_from_object(confluence.getPage(self.space, self.title))
138
+ end
139
+ end
@@ -0,0 +1,112 @@
1
+ require 'confluence/confluence_remote_data_object'
2
+
3
+ class Confluence::User < Confluence::RemoteDataObject
4
+
5
+ self.save_method = :editUser
6
+ # TODO: implement :create_method ('addUser')
7
+ self.get_method = :getUser
8
+ self.destroy_method = :removeUser
9
+
10
+ self.readonly_attrs = ['username', 'name']
11
+ self.attr_conversions = {}
12
+
13
+ @groups
14
+
15
+ def id
16
+ self.username
17
+ end
18
+
19
+ def id=(new_id)
20
+ self.username = new_id
21
+ end
22
+
23
+ def username
24
+ self.name
25
+ end
26
+
27
+ def username=(new_username)
28
+ self.username=(new_username)
29
+ end
30
+
31
+ def groups
32
+ # groups are cached for the lifetime of the user object or until a group-modifying method is called
33
+ # FIXME: This is probably a bad idea, since the user's groups may be changed outside of the user object
34
+ # ... currently it's not a serious problem, since this is unlikely to happen within the object's
35
+ # short lifetime, but it may be problematic if start storing the user object in a cache or in the
36
+ # session.
37
+ @groups ||= confluence.getUserGroups(username)
38
+ end
39
+
40
+ def in_group?(group)
41
+ groups.include? group
42
+ end
43
+
44
+ def add_to_group(group)
45
+ @groups = nil # reset cached group list
46
+ confluence.addUserToGroup(username, group)
47
+ end
48
+
49
+ def remove_from_group(group)
50
+ @groups = nil # reset cached group list
51
+ confluence.removeUserFromGroup(username, group)
52
+ end
53
+
54
+ def has_permission?(permtype, page)
55
+ if permtype == :edit
56
+ group_or_name = page.edit_group
57
+ else
58
+ group_or_name = page.view_group
59
+ end
60
+
61
+ return true if group_or_name.nil?
62
+ return true if group_or_name == username
63
+ return in_group?(group_or_name)
64
+ end
65
+
66
+ def add_profile_picture(filename, mime_type, picture_data)
67
+ confluence.addProfilePicture(self.username, filename, mime_type, picture_data)
68
+ end
69
+
70
+ def to_s
71
+ self.username
72
+ end
73
+
74
+ def to_wiki
75
+ "[~#{self.username}]"
76
+ end
77
+
78
+ ### class methods #########################################################
79
+
80
+ def self.find_by_username(username)
81
+ find(username)
82
+ end
83
+
84
+ # DEPRECATED: this method is confusing since it could be taken as meaning "find by first/last name"
85
+ def self.find_by_name(username)
86
+ find_by_username(username)
87
+ end
88
+
89
+ def self.find_by_email(email)
90
+ usernames = confluence.getActiveUsers(true)
91
+ usernames.each do |username|
92
+ user = find_by_username(username)
93
+ return user if user.email == email
94
+ end
95
+
96
+ return nil
97
+ end
98
+
99
+ def self.find_all
100
+ # FIXME: this is really slow... we should probably just look in the confluence database instead
101
+ usernames = find_all_usernames
102
+ usernames.collect{|u| find_by_username(u)}
103
+ end
104
+
105
+ def self.find_all_usernames
106
+ confluence.getActiveUsers(true)
107
+ end
108
+
109
+ class NoSuchUser < Exception
110
+ end
111
+
112
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: confluence4r
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Atlassian and Alagu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Atlassian Confluence XML::RPC client
28
+ email: alagu@goyaka.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/confluence/confluence_connector.rb
34
+ - lib/confluence/confluence_remote_data_object.rb
35
+ - lib/confluence/confluence_rpc.rb
36
+ - lib/confluence/metadata.rb
37
+ - lib/confluence/page.rb
38
+ - lib/confluence/user.rb
39
+ homepage: http://confluence.atlassian.com/display/DISC/Confluence4r
40
+ licenses:
41
+ - unknown
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements:
58
+ - none
59
+ rubyforge_project:
60
+ rubygems_version: 2.2.2
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Atlassian Confluence XML::RPC client
64
+ test_files: []