mediawiki-gateway 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +3 -0
  2. data/README +12 -0
  3. data/Rakefile +40 -0
  4. data/VERSION +1 -0
  5. data/config/hosts.yml +17 -0
  6. data/doc/classes/MediaWiki.html +189 -0
  7. data/doc/classes/MediaWiki/Config.html +269 -0
  8. data/doc/classes/MediaWiki/Gateway.html +952 -0
  9. data/doc/created.rid +1 -0
  10. data/doc/files/README_txt.html +117 -0
  11. data/doc/files/media_wiki/config_rb.html +108 -0
  12. data/doc/files/media_wiki/gateway_rb.html +113 -0
  13. data/doc/files/media_wiki/utils_rb.html +101 -0
  14. data/doc/files/script/create_page_rb.html +115 -0
  15. data/doc/files/script/delete_book_rb.html +108 -0
  16. data/doc/files/script/export_xml_rb.html +114 -0
  17. data/doc/files/script/get_page_rb.html +114 -0
  18. data/doc/files/script/import_xml_rb.html +114 -0
  19. data/doc/files/script/undelete_page_rb.html +101 -0
  20. data/doc/files/script/upload_commons_rb.html +109 -0
  21. data/doc/files/script/upload_file_rb.html +115 -0
  22. data/doc/fr_class_index.html +29 -0
  23. data/doc/fr_file_index.html +38 -0
  24. data/doc/fr_method_index.html +49 -0
  25. data/doc/index.html +24 -0
  26. data/doc/rdoc-style.css +208 -0
  27. data/lib/media_wiki.rb +3 -0
  28. data/lib/media_wiki/config.rb +69 -0
  29. data/lib/media_wiki/gateway.rb +307 -0
  30. data/lib/media_wiki/utils.rb +18 -0
  31. data/mediawiki-gateway.gemspec +88 -0
  32. data/script/create_page.rb +14 -0
  33. data/script/delete_book.rb +14 -0
  34. data/script/export_xml.rb +14 -0
  35. data/script/get_page.rb +12 -0
  36. data/script/import_xml.rb +14 -0
  37. data/script/run_fake_media_wiki.rb +8 -0
  38. data/script/undelete_page.rb +15 -0
  39. data/script/upload_commons.rb +42 -0
  40. data/script/upload_file.rb +14 -0
  41. data/spec/fake_media_wiki/api_pages.rb +131 -0
  42. data/spec/fake_media_wiki/app.rb +262 -0
  43. data/spec/fake_media_wiki/query_handling.rb +112 -0
  44. data/spec/gateway_spec.old +535 -0
  45. data/spec/gateway_spec.rb +653 -0
  46. data/spec/import-test-data.xml +68 -0
  47. metadata +115 -0
@@ -0,0 +1,18 @@
1
+ module MediaWiki
2
+
3
+ class << self
4
+
5
+ def version
6
+ "0.0.1"
7
+ end
8
+
9
+ # Convert a Wiki page name ("getting there & away") to URI-safe format ("getting_there_%26_away"),
10
+ # taking care not to mangle slashes
11
+ # [wiki] Page name string in Wiki format
12
+ def wiki_to_uri(wiki)
13
+ wiki.to_s.split('/').map {|chunk| CGI.escape(chunk.tr(' ', '_')) }.join('/') if wiki
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,88 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mediawiki-gateway}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jani Patokallio"]
12
+ s.date = %q{2010-10-01}
13
+ s.description = %q{}
14
+ s.email = %q{jpatokal@iki.fi}
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "README",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "config/hosts.yml",
24
+ "doc/classes/MediaWiki.html",
25
+ "doc/classes/MediaWiki/Config.html",
26
+ "doc/classes/MediaWiki/Gateway.html",
27
+ "doc/created.rid",
28
+ "doc/files/README_txt.html",
29
+ "doc/files/media_wiki/config_rb.html",
30
+ "doc/files/media_wiki/gateway_rb.html",
31
+ "doc/files/media_wiki/utils_rb.html",
32
+ "doc/files/script/create_page_rb.html",
33
+ "doc/files/script/delete_book_rb.html",
34
+ "doc/files/script/export_xml_rb.html",
35
+ "doc/files/script/get_page_rb.html",
36
+ "doc/files/script/import_xml_rb.html",
37
+ "doc/files/script/undelete_page_rb.html",
38
+ "doc/files/script/upload_commons_rb.html",
39
+ "doc/files/script/upload_file_rb.html",
40
+ "doc/fr_class_index.html",
41
+ "doc/fr_file_index.html",
42
+ "doc/fr_method_index.html",
43
+ "doc/index.html",
44
+ "doc/rdoc-style.css",
45
+ "lib/media_wiki.rb",
46
+ "lib/media_wiki/config.rb",
47
+ "lib/media_wiki/gateway.rb",
48
+ "lib/media_wiki/utils.rb",
49
+ "mediawiki-gateway.gemspec",
50
+ "script/create_page.rb",
51
+ "script/delete_book.rb",
52
+ "script/export_xml.rb",
53
+ "script/get_page.rb",
54
+ "script/import_xml.rb",
55
+ "script/run_fake_media_wiki.rb",
56
+ "script/undelete_page.rb",
57
+ "script/upload_commons.rb",
58
+ "script/upload_file.rb",
59
+ "spec/fake_media_wiki/api_pages.rb",
60
+ "spec/fake_media_wiki/app.rb",
61
+ "spec/fake_media_wiki/query_handling.rb",
62
+ "spec/gateway_spec.old",
63
+ "spec/gateway_spec.rb",
64
+ "spec/import-test-data.xml"
65
+ ]
66
+ s.homepage = %q{http://github.com/jpatokal/media_wiki_gateway}
67
+ s.rdoc_options = ["--charset=UTF-8"]
68
+ s.require_paths = ["lib"]
69
+ s.rubygems_version = %q{1.3.7}
70
+ s.summary = %q{Connect to the mediawiki API}
71
+ s.test_files = [
72
+ "spec/fake_media_wiki/api_pages.rb",
73
+ "spec/fake_media_wiki/app.rb",
74
+ "spec/fake_media_wiki/query_handling.rb",
75
+ "spec/gateway_spec.rb"
76
+ ]
77
+
78
+ if s.respond_to? :specification_version then
79
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
80
+ s.specification_version = 3
81
+
82
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
83
+ else
84
+ end
85
+ else
86
+ end
87
+ end
88
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Sample script for fetching a page's current contents in Wiki markup
4
+ #
5
+ require 'media_wiki/gateway'
6
+ require 'media_wiki/config'
7
+
8
+ config = MediaWiki::Config.new ARGV
9
+ config.abort("Name of article is mandatory.") unless config.article
10
+
11
+ mw = MediaWiki::Gateway.new(config.url, Logger::DEBUG)
12
+ mw.login(config.user, config.pw)
13
+ content = ARGF.read.to_s
14
+ puts mw.create(config.article, content, {:overwrite => true, :summary => config.summary})
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require 'media_wiki/gateway'
3
+
4
+ if ARGV.length <1
5
+ raise "Syntax: delete_batch.rb <wiki-api-url> <startswith_pattern>"
6
+ end
7
+
8
+ rw = MediaWiki::Gateway.new(ARGV[0])
9
+ rw.list(ARGV[1]).each do |title|
10
+ print "Deleting #{title}..."
11
+ rw.delete(title)
12
+ end
13
+ print "Done."
14
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Export MediaWiki pages as XML
4
+ #
5
+ require 'media_wiki/gateway'
6
+
7
+ if ARGV.length < 3
8
+ raise "Syntax: export_xml.rb <host> <user> <password> [page page page...]"
9
+ end
10
+
11
+ mw = MediaWiki::Gateway.new(ARGV[0])
12
+ mw.login(ARGV[1], ARGV[2])
13
+ print mw.export(ARGV[3..-1])
14
+
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Sample script for fetching a page's current contents in Wiki markup
4
+ #
5
+ require 'media_wiki/gateway'
6
+
7
+ if ARGV.length < 2
8
+ raise "Syntax: get_page.rb <host> <name>"
9
+ end
10
+
11
+ mw = MediaWiki::Gateway.new(ARGV[0])
12
+ puts mw.get(ARGV[1])
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Import a MediaWiki XML dump
4
+ #
5
+ require 'media_wiki/gateway'
6
+
7
+ if ARGV.length < 3
8
+ raise "Syntax: import_xml.rb <host> <username> <password> <file>"
9
+ end
10
+
11
+ mw = MediaWiki::Gateway.new(ARGV[0], Logger::DEBUG)
12
+ mw.login(ARGV[1], ARGV[2])
13
+ mw.import(ARGV[3])
14
+
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # Helper script for running a live FakeMediaWiki instance instead of just shamracking it.
3
+ require 'rubygems'
4
+ require 'sinatra/base'
5
+ require 'spec/fake_media_wiki/app'
6
+
7
+ FakeMediaWiki::App.run! :host => 'localhost', :port => 9090
8
+
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/environment'
3
+
4
+ if ARGV.length <1
5
+ raise "Syntax: undelete.rb <initial-pattern> [environment]\n Sample: undelete.rb 'Book:Shanghai 5' devint"
6
+ end
7
+
8
+ host = ARGV[1] ? "webvip.mediawiki.#{ARGV[1]}.lpo" : LonelyPlanet::OnRails.environment.services[:atlas_mediawiki]
9
+ rw = MediaWiki::Gateway.new(host)
10
+ rw.login('atlasmw', 'wombat')
11
+ # Warning: List only works on existing pages (deleted & reimported), there's a separate unimplemented op for listing currently deleted pages
12
+ rw.list(ARGV[0]).each do |title|
13
+ print "Undeleting #{title}... #{rw.undelete(title)} revisions restored.\n"
14
+ end
15
+ print "Done."
@@ -0,0 +1,42 @@
1
+ require 'lib/media_wiki/gateway'
2
+ require 'lib/media_wiki/config'
3
+
4
+ config = MediaWiki::Config.new(ARGV, "upload")
5
+ file = ARGV[0]
6
+ config.abort("Name of file to upload is mandatory.") unless file
7
+
8
+ mw = MediaWiki::Gateway.new(config.url)
9
+ mw.login(config.user, config.pw)
10
+
11
+ puts "Login successful."
12
+ puts "Description of file:"
13
+ desc = STDIN.gets.chomp
14
+ puts "Date of file:"
15
+ date = STDIN.gets.chomp
16
+ puts "Target filename: (leave blank to use existing name)"
17
+ target = STDIN.gets.chomp
18
+ target = config.target if target.empty?
19
+ puts "Categories, separated by commas:"
20
+ cats = STDIN.gets.chomp.split(",")
21
+ cats = "[[Category:" + cats.join("]]\n[[Category:") + "]]" unless cats.empty?
22
+
23
+ template = <<-TEMPLATE
24
+ == Summary ==
25
+ {{Information
26
+ |Description={{en|1=%DESC%}}
27
+ |Source={{own}}
28
+ |Author=[[User:%USER%|%USER%]]
29
+ |Date=%DATE%
30
+ |Permission=
31
+ |other_versions=
32
+ }}
33
+
34
+ == Licensing ==
35
+ {{self|cc-by-sa-3.0|GFDL}}
36
+
37
+ %CATS%
38
+ TEMPLATE
39
+ desc = template.gsub('%USER%', config.user).gsub('%DESC%', desc).gsub('%DATE%', date).gsub('%CATS%', cats)
40
+ puts "Uploading #{file} to #{target}..."
41
+ mw.upload(file, {:target => target, :description => desc, :summary => "Uploaded by MediaWiki::Gateway"})
42
+ puts "Done."
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Sample script for fetching a page's current contents in Wiki markup
4
+ #
5
+ require 'media_wiki/gateway'
6
+ require 'media_wiki/config'
7
+
8
+ config = MediaWiki::Config.new(ARGV, "upload")
9
+ config.abort("Name of file to upload is mandatory.") unless ARGV[0]
10
+
11
+ mw = MediaWiki::Gateway.new(config.url, Logger::DEBUG)
12
+ mw.login(config.user, config.pw)
13
+ mw.upload(ARGV[0], {:target => config.target, :description => config.desc, :summary => config.summary})
14
+
@@ -0,0 +1,131 @@
1
+ module FakeMediaWiki
2
+
3
+ class ApiPages
4
+
5
+ def initialize
6
+ @page_id = 0
7
+ @pages = {}
8
+ @namespaces = { "" => 0 }
9
+ end
10
+
11
+ def add_namespace(id, prefix)
12
+ @namespaces[prefix] = id
13
+ end
14
+
15
+ def namespaces_by_prefix
16
+ @namespaces
17
+ end
18
+
19
+ def namespaces_by_id
20
+ @namespaces.invert
21
+ end
22
+
23
+ def add(title, content)
24
+ @page_id += 1
25
+ dummy, prefix = title.split(":", 2).reverse
26
+ @pages[title] = {
27
+ :pageid => @page_id,
28
+ :namespace => namespaces_by_prefix[prefix || ""],
29
+ :title => title,
30
+ :content => content
31
+ }
32
+ end
33
+
34
+ def get(title)
35
+ @pages[title]
36
+ end
37
+
38
+ def list(prefix)
39
+ @pages.select do |key, page|
40
+ key =~ /^#{prefix}/
41
+ end
42
+ end
43
+
44
+ def search(searchkey, namespaces)
45
+ raise FakeMediaWiki::ApiError.new("srparam-search", "empty search string is not allowed") if searchkey.empty?
46
+ @pages.select do |key, page|
47
+ page[:content] =~ /#{searchkey}/ and namespaces.include? page[:namespace].to_s
48
+ end
49
+ end
50
+
51
+ def delete(title)
52
+ @pages.delete(title)
53
+ end
54
+
55
+ def undelete(title)
56
+ if @pages[title]
57
+ 0
58
+ else
59
+ add(title, "Undeleted content")
60
+ 1
61
+ end
62
+ end
63
+ end
64
+
65
+ class ApiToken
66
+ ADMIN_TOKEN = "admin_token+\\"
67
+ REGULAR_TOKEN = "regular_token+\\"
68
+ BLANK_TOKEN = "+\\"
69
+
70
+ def initialize(params)
71
+ @token_str = params[:token]
72
+ @token_in = params[:intoken]
73
+ end
74
+
75
+ def set_type(type)
76
+ @token_in = type
77
+ end
78
+
79
+ def validate
80
+ unless @token_str
81
+ raise FakeMediaWiki::ApiError.new("notoken", "The token parameter must be set")
82
+ end
83
+ end
84
+
85
+ def validate_admin
86
+ validate
87
+ if @token_str != ADMIN_TOKEN
88
+ raise FakeMediaWiki::ApiError.new("badtoken", "Invalid token")
89
+ end
90
+ end
91
+
92
+ def request(user)
93
+ @user = user
94
+ respond_to?(requested_token_type) ? send(requested_token_type) : nil
95
+ end
96
+
97
+ def requested_token_type
98
+ "#{@token_in}token".to_sym
99
+ end
100
+
101
+ def importtoken
102
+ if @user && @user[:is_admin]
103
+ ADMIN_TOKEN
104
+ else
105
+ nil
106
+ end
107
+ end
108
+ alias_method :deletetoken, :importtoken
109
+ alias_method :undeletetoken, :importtoken
110
+
111
+ def edittoken
112
+ if @user
113
+ REGULAR_TOKEN
114
+ else
115
+ BLANK_TOKEN
116
+ end
117
+ end
118
+ end
119
+
120
+ class ApiError < StandardError
121
+
122
+ attr_reader :code, :message
123
+
124
+ def initialize(code, message)
125
+ @code = code
126
+ @message = message
127
+ end
128
+
129
+ end
130
+
131
+ end
@@ -0,0 +1,262 @@
1
+ require 'rubygems'
2
+ require 'sinatra/base'
3
+ require 'spec/fake_media_wiki/api_pages'
4
+ require 'spec/fake_media_wiki/query_handling'
5
+
6
+ # A simple Rack app that stubs out a web service, for testing.
7
+
8
+ module FakeMediaWiki
9
+
10
+ class App < Sinatra::Base
11
+
12
+ set :show_exceptions, false
13
+ set :environment, :development
14
+
15
+ def initialize
16
+ reset
17
+ super
18
+ end
19
+
20
+ def reset
21
+ @sequence_id = 0
22
+
23
+ @users = {}
24
+ add_user('atlasmw', 'wombat', 'local', true)
25
+ add_user('nonadmin', 'sekrit', 'local', false)
26
+ add_user('ldapuser', 'ldappass', 'ldapdomain', false)
27
+
28
+ @pages = ApiPages.new
29
+ @pages.add('Main Page', 'Content')
30
+ @pages.add('Main 2', 'Content')
31
+ @pages.add('Level/Level/Index', '{{#include:Foo}} {{#include:Bar}}')
32
+ @pages.add_namespace(100, "Book")
33
+ @pages.add('Book:Italy', 'Introduction')
34
+ @pages.add_namespace(200, "Sandbox")
35
+
36
+ @extensions = { 'FooExtension' => 'r1', 'BarExtension' => 'r2' }
37
+
38
+ @logged_in_users = []
39
+ end
40
+
41
+ def next_id
42
+ @sequence_id += 1
43
+ end
44
+
45
+ def add_user(username, password, domain, is_admin)
46
+ @users[username] = {
47
+ :userid => next_id,
48
+ :username => username,
49
+ :password => password,
50
+ :domain => domain,
51
+ :is_admin => is_admin
52
+ }
53
+ end
54
+
55
+ def logged_in(username)
56
+ @logged_in_users.include?(username)
57
+ end
58
+
59
+ post "/w/api.php" do
60
+ begin
61
+ @token = ApiToken.new(params)
62
+ action = params[:action]
63
+ if respond_to?(action)
64
+ content_type "application/xml"
65
+ return send(action)
66
+ end
67
+
68
+ halt(404, "Page not found")
69
+ rescue FakeMediaWiki::ApiError => e
70
+ return api_error_response(e.code, e.message)
71
+ end
72
+ end
73
+
74
+ def import
75
+ @token.validate_admin
76
+
77
+ api_response do |_|
78
+ _.import do
79
+ _.page(nil, :title => "Main Page", :ns => 0, :revisions => 0)
80
+ _.page(nil, :title => "Template:Header", :ns => 10, :revisions => 1)
81
+ end
82
+ end
83
+ end
84
+
85
+ def validate_page_overwrite(current_page)
86
+ if current_page && params[:createonly]
87
+ raise FakeMediaWiki::ApiError.new("articleexists", "The article you tried to create has been created already")
88
+ end
89
+ end
90
+
91
+ def edit
92
+ @token.validate
93
+
94
+ title = params[:title]
95
+ current_page = @pages.get(title)
96
+ validate_page_overwrite(current_page)
97
+
98
+ new_page = @pages.add(title, params[:text])
99
+ page_info = {:result => "Success", :pageid => new_page[:pageid], :title => new_page[:title], :newrevid => new_page[:pageid]}
100
+ if current_page
101
+ page_info.merge!(:oldrevid => current_page[:pageid])
102
+ else
103
+ page_info.merge!(:new => "", :oldrevid => 0)
104
+ end
105
+
106
+ api_response do |_|
107
+ _.edit(nil, page_info)
108
+ end
109
+ end
110
+
111
+ def delete
112
+ @token.validate_admin
113
+
114
+ title = params[:title]
115
+ raise FakeMediaWiki::ApiError.new("missingtitle", "The page you requested doesn't exist") unless @pages.get(title)
116
+ @pages.delete(title)
117
+
118
+ api_response do |_|
119
+ _.delete(nil, {:title => title, :reason => "Default reason"})
120
+ end
121
+ end
122
+
123
+ def undelete
124
+ @token.validate_admin
125
+
126
+ title = params[:title]
127
+ revisions = @pages.undelete(title)
128
+ api_response do |_|
129
+ _.undelete(nil, {:title => title, :revisions => revisions})
130
+ end
131
+ end
132
+
133
+ def upload
134
+ @token.validate
135
+
136
+ filename = params[:filename]
137
+ @pages.add(filename, params[:file])
138
+ api_response do |_|
139
+ _.upload(nil, {:filename => filename, :result => "Success"})
140
+ end
141
+ end
142
+
143
+ def parse
144
+ page = @pages.get(params[:page])
145
+ api_response do |_|
146
+ _.parse({ :revid => page ? page[:pageid] : 0}) do
147
+ _.text("Sample <B>HTML</B> content")
148
+ end
149
+ end
150
+ end
151
+
152
+ include QueryHandling
153
+
154
+ def api_response
155
+ builder do |_|
156
+ _.api do |_|
157
+ yield(_)
158
+ end
159
+ end
160
+ end
161
+
162
+ def api_error_response(code, info)
163
+ api_response do |_|
164
+ _.error(nil,
165
+ :code => code,
166
+ :info => info)
167
+ end
168
+ end
169
+
170
+ def query_pages
171
+ api_response do |_|
172
+ _.query do
173
+ _.pages do
174
+ requested_page_titles.each do |title|
175
+ yield(_, title, @pages.get(title))
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ def get_revisions
183
+ query_pages do |_, title, page|
184
+ if page.nil?
185
+ _.page(nil, { :title => title, :ns => '0', :missing => "" })
186
+ else
187
+ page = page.dup
188
+ content = page.delete(:content)
189
+ _.page(page.merge({ :ns => 0 })) do
190
+ _.revisions do
191
+ _.rev(content)
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ end
198
+
199
+ def user
200
+ username = request.cookies['login']
201
+ @users[username] if logged_in(username)
202
+ end
203
+
204
+ def requested_page_titles
205
+ params[:titles].split("|")
206
+ end
207
+
208
+ def get_token
209
+ token_str = @token.request(user)
210
+ query_pages do |_, title, page|
211
+ page = page ? page.dup : {}
212
+ page[params[:intoken] + "token"] = token_str if token_str
213
+ _.page(nil, page.merge({ :ns => 0 }))
214
+ end
215
+ end
216
+
217
+ def get_undelete_token
218
+ @token.set_type 'undelete'
219
+ token_str = @token.request(user)
220
+ api_response do |_|
221
+ _.query do
222
+ _.deletedrevs do
223
+ requested_page_titles.select {|title| ! @pages.get(title) }.each do |title|
224
+ _.page(nil, { :title => title, :token => token_str })
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+
231
+ def login
232
+ user = @users[params[:lgname]]
233
+ if user and user[:domain] == params[:lgdomain]
234
+ if params[:lgpassword] == user[:password]
235
+ @logged_in_users << user[:username]
236
+ response.set_cookie('login', user[:username])
237
+ result = { :result => "Success", :lguserid => "1", :lgusername => "Atlasmw"}
238
+ else
239
+ result = { :result => "WrongPass" }
240
+ end
241
+ else
242
+ result = { :result => "NotExists" }
243
+ end
244
+
245
+ api_response do |_|
246
+ _.login(nil, result)
247
+ end
248
+ end
249
+
250
+ end
251
+
252
+ class WikiPage
253
+
254
+ def initialize(options={})
255
+ options.each { |k, v| send("#{k}=", v) }
256
+ end
257
+
258
+ attr_accessor :content, :author
259
+
260
+ end
261
+
262
+ end