sunflower 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -101,7 +101,7 @@ class Page
101
101
 
102
102
  def code_cleanup
103
103
  # simple, safe code cleanup
104
- # use $alwaysDoCodeCleanup=true to do it automatically just before saving page
104
+ # use Sunflower.always_do_code_cleanup=true to do it automatically just before saving page
105
105
  # based on Nux's cleaner: http://pl.wikipedia.org/wiki/Wikipedysta:Nux/wp_sk.js
106
106
  str=self.text.gsub(/\r\n/,"\n")
107
107
 
@@ -12,18 +12,31 @@ require 'cgi'
12
12
  # s = Sunflower.new.login
13
13
  #
14
14
  # Then you can request data from API using #API method.
15
- # To log data to file, use #log method (works like puts, append new line if needed) of #log2 (like print).
15
+ #
16
+ # To log data to file, use #log method (works like puts). Use RestClient.log=<io> to log all requests.
17
+ #
16
18
  # You can use multiple Sunflowers at once, to work on multiple wikis.
19
+
20
+ class SunflowerError < StandardError; end
21
+
17
22
  class Sunflower
23
+ VERSION = '0.3'
24
+
25
+ INVALID_CHARS = %w(# < > [ ] | { })
26
+ INVALID_CHARS_REGEX = Regexp.union *INVALID_CHARS
27
+
18
28
  # Path to user data file.
19
29
  def self.path
20
30
  File.join(ENV['HOME'], 'sunflower-userdata')
21
31
  end
22
32
 
33
+ # Options for this Sunflower.
34
+ attr_accessor :summary, :always_do_code_cleanup
35
+
23
36
  attr_accessor :cookie, :headers, :wikiURL, :warnings, :log
24
37
 
25
38
  # Initialize a new Sunflower working on a wiki with given URL, for ex. "pl.wikipedia.org".
26
- def initialize url=''
39
+ def initialize url=nil
27
40
  begin
28
41
  r=File.read(Sunflower.path)
29
42
  @userdata=r.split(/\r?\n/).map{|i| i.strip}
@@ -31,30 +44,34 @@ class Sunflower
31
44
  @userdata=[]
32
45
  end
33
46
 
34
- if url==''
47
+ if !url
35
48
  if !@userdata.empty?
36
49
  url=@userdata[0]
37
50
  else
38
- raise RuntimeError, 'Sunflower - initialize: no URL supplied and no userdata found!'
51
+ raise SunflowerError, 'initialize: no URL supplied and no userdata found!'
39
52
  end
40
53
  end
41
54
 
42
55
  @warnings=true
43
- @log=true
56
+ @log=false
44
57
 
45
58
  @wikiURL=url
46
- @logData=''
47
59
 
48
60
  @loggedin=false
49
61
  end
50
62
 
51
- # Call the API. Returns a hash of JSON response.
63
+ # Call the API. Returns a hash of JSON response. Request can be a HTTP request string or a hash.
52
64
  def API request
53
- #$stderr.puts 'Warning: Sunflower: API request before logging in! ('+request+')' unless @loggedin || !@warnings
54
- self.log 'http://'+@wikiURL+'/w/api.php?'+request+'&format=jsonfm'
55
- resp = RestClient.get(
56
- 'http://'+@wikiURL+'/w/api.php?'+request+'&format=json',
57
- {:user_agent => 'Sunflower alpha', :cookies => @cookies}
65
+ if request.is_a? String
66
+ request += '&format=json'
67
+ elsif request.is_a? Hash
68
+ request.merge!({format:'json'})
69
+ end
70
+
71
+ resp = RestClient.post(
72
+ 'http://'+@wikiURL+'/w/api.php',
73
+ request,
74
+ {:user_agent => "Sunflower #{VERSION} alpha", :cookies => @cookies}
58
75
  )
59
76
  JSON.parse resp.to_str
60
77
  end
@@ -66,13 +83,11 @@ class Sunflower
66
83
  user=@userdata[1] if user==''
67
84
  password=@userdata[2] if password==''
68
85
  else
69
- raise RuntimeError, 'Sunflower - login: no user/pass supplied and no userdata found!'
86
+ raise SunflowerError, 'login: no user/pass supplied and no userdata found!'
70
87
  end
71
88
  end
72
89
 
73
- if user.index(/[#<>\[\]\|\{\}]/)
74
- raise RuntimeError, 'Sunflower - bad username!'
75
- end
90
+ raise SunflowerError, 'bad username!' if user =~ INVALID_CHARS_REGEX
76
91
 
77
92
 
78
93
  # 1. get the login token
@@ -103,55 +118,52 @@ class Sunflower
103
118
  })
104
119
 
105
120
 
106
- raise RuntimeError, 'Sunflower - unable to log in (no cookies received)!' if !@cookies
121
+ raise SunflowerError, 'unable to log in (no cookies received)!' if !@cookies
107
122
 
108
123
 
109
- @loggedin=true
124
+ # 3. confirm you did log in by checking the watchlist.
125
+ @loggedin=true
110
126
  r=self.API('action=query&list=watchlistraw')
111
127
  if r['error'] && r['error']['code']=='wrnotloggedin'
112
128
  @loggedin=false
113
- raise RuntimeError, 'Sunflower - unable to log in!'
129
+ raise SunflowerError, 'unable to log in!'
114
130
  end
115
131
 
132
+ # 4. check bot rights
116
133
  r=self.API('action=query&list=allusers&aulimit=1&augroup=bot&aufrom='+user)
117
134
  unless r['query']['allusers'][0]['name']==user
118
- $stderr.puts 'Warning: Sunflower - this user does not have bot rights!' if @warnings
119
- @haveBotRights=false
135
+ warn 'Sunflower - this user does not have bot rights!' if @warnings
136
+ @is_bot=false
120
137
  else
121
- @haveBotRights=true
138
+ @is_bot=true
122
139
  end
123
140
 
124
141
  return self
125
142
  end
126
143
 
127
- def log2(t)
128
- @logData+=t
129
- if @log
130
- f=File.open('log.txt','a')
131
- f.write t
132
- f.close
133
- end
144
+ def log t
145
+ File.open('log.txt','a'){|f| f.puts t}
134
146
  end
135
147
 
136
- def log(t)
137
- self.log2(t.to_s.chomp+"\n")
138
- end
139
-
140
- def isBot?
141
- @haveBotRights
148
+ def is_bot?
149
+ @is_bot
142
150
  end
143
151
  end
144
152
 
145
153
  # Class representng single Wiki page. To load specified page, use #new/#get/#load method.
146
154
  #
155
+ # When calling Page.new, at first only the text will be loaded - attributes and edit token will be loaded when needed, or when you call #preload_attrs.
156
+ #
147
157
  # If you are using multiple Sunflowers, you have to specify which wiki this page belongs to using second argument of function; you can pass whole URL (same as when creating new Sunflower) or just language code.
148
158
  #
149
- # To save page, use #save/#put method. Optional argument is new title page, if ommited, page is saved at old title. Summary can be passed as second parameter. If it's ommited, global variable $summary is used. If it's empty too, error is raised.
159
+ # To save page, use #save/#put method. Optional argument is new title page, if ommited, page is saved at old title. Summary can be passed as second parameter. If it's ommited, s.summary is used. If it's empty too, error is raised.
150
160
  #
151
161
  # To get Sunflower instance which this page belongs to, use #sunflower of #belongs_to.
152
162
  class Page
153
- attr_accessor :text
163
+ INVALID_CHARS = %w(# < > [ ] | { })
164
+ INVALID_CHARS_REGEX = Regexp.union *INVALID_CHARS
154
165
 
166
+ attr_accessor :text
155
167
  attr_reader :orig_text
156
168
 
157
169
  attr_reader :sunflower
@@ -159,8 +171,16 @@ class Page
159
171
 
160
172
  attr_reader :pageid, :ns, :title, :touched, :lastrevid, :counter, :length, :starttimestamp, :edittoken, :protection #prop=info
161
173
 
174
+ # calling any of these accessors will fetch the data.
175
+ [:pageid, :ns, :title, :touched, :lastrevid, :counter, :length, :starttimestamp, :edittoken, :protection].each do |meth|
176
+ define_method meth do
177
+ preload_attrs unless @preloaded_attrs
178
+ instance_variable_get "@#{meth}"
179
+ end
180
+ end
181
+
162
182
  def initialize title='', wiki=''
163
- raise RuntimeError, 'Sunflower - title invalid: '+title if title.index(/[#<>\[\]\|\{\}]/)
183
+ raise SunflowerError, 'title invalid: '+title if title =~ INVALID_CHARS_REGEX
164
184
 
165
185
  @modulesExecd=[] #used by sunflower-commontasks.rb
166
186
  @summaryAppend=[] #used by sunflower-commontasks.rb
@@ -170,7 +190,7 @@ class Page
170
190
 
171
191
  if wiki==''
172
192
  count=ObjectSpace.each_object(Sunflower){|o| @sunflower=o}
173
- raise RuntimeError, 'Sunflower - you must pass wiki name if using multiple Sunflowers at once!' if count>1
193
+ raise SunflowerError, 'you must pass wiki name if using multiple Sunflowers at once!' if count>1
174
194
  else
175
195
  ObjectSpace.each_object(Sunflower){|o| @sunflower=o if o.wikiURL==wiki}
176
196
  end
@@ -181,46 +201,60 @@ class Page
181
201
  return
182
202
  end
183
203
 
184
- r=@sunflower.API('action=query&prop=info&inprop=protection&intoken=edit&titles='+CGI.escape(@title))
185
- r=r['query']['pages'][r['query']['pages'].keys[0] ]
186
- r.each{|key,value|
204
+ preload_text
205
+ end
206
+
207
+ def preload_text
208
+ r = @sunflower.API('action=query&prop=revisions&rvprop=content&titles='+CGI.escape(@title))
209
+ r = r['query']['pages'].first
210
+ if r['missing']
211
+ @text = ''
212
+ else
213
+ @text = r['revisions'][0]['*']
214
+ end
215
+
216
+ @orig_text = @text
217
+
218
+ @preloaded_text = true
219
+ end
220
+
221
+ def preload_attrs
222
+ r = @sunflower.API('action=query&prop=info&inprop=protection&intoken=edit&titles='+CGI.escape(@title))
223
+ r = r['query']['pages'].first
224
+ r.each{|key, value|
187
225
  self.instance_variable_set('@'+key, value)
188
226
  }
189
227
 
190
- r=@sunflower.API('action=query&prop=revisions&rvprop=content&titles='+CGI.escape(@title))
191
-
192
- begin
193
- @text=r['query']['pages'][@pageid.to_s]['revisions'][0]['*']
194
- rescue Exception
195
- @text=''
196
- end
197
- @orig_text=@text
228
+ @preloaded_attrs = true
198
229
  end
199
230
 
200
- def dumpto file
231
+ def dump_to file
201
232
  if file.respond_to? :write #probably file or IO
202
233
  file.write @text
203
234
  else #filename?
204
- f=File.open(file.to_s, 'w')
205
- f.write @text
206
- f.close
235
+ File.open(file.to_s, 'w'){|f| f.write @text}
207
236
  end
208
237
  end
209
238
 
210
239
  def dump
211
- self.dumpto @title.gsub(/[^a-zA-Z0-9\-]/,'_')+'.txt'
240
+ self.dump_to @title.gsub(/[^a-zA-Z0-9\-]/,'_')+'.txt'
212
241
  end
213
242
 
214
- def save title=@title, summary=$summary
215
- raise RuntimeError, 'Sunflower - title invalid: '+title if title.index(/[#<>\[\]\|\{\}]/)
216
- raise RuntimeError, 'Sunflower - no summary!' if (summary==nil || summary=='') && @summaryAppend==[]
243
+ def save title=@title, summary=nil
244
+ preload_attrs unless @preloaded_attrs
217
245
 
218
- summary='' if summary==nil
219
- for i in @summaryAppend.uniq
220
- summary+=', '+i
221
- end
222
- summary.sub!(/^, /,'')
246
+ summary = @sunflower.summary if !summary
223
247
 
248
+ raise SunflowerError, 'title invalid: '+title if title =~ INVALID_CHARS_REGEX
249
+ raise SunflowerError, 'no summary!' if (!summary or summary=='') && @summaryAppend.empty?
250
+
251
+ unless @summaryAppend.empty?
252
+ if !summary or summary==''
253
+ summary = @summaryAppend.uniq.join(', ')
254
+ else
255
+ summary = summary.sub(/,\s*\Z/, '') + ', ' + @summaryAppend.uniq.join(', ')
256
+ end
257
+ end
224
258
 
225
259
  if @orig_text==@text && title==@title
226
260
  @sunflower.log('Page '+title+' not saved - no changes.')
@@ -229,7 +263,7 @@ class Page
229
263
 
230
264
 
231
265
 
232
- self.code_cleanup if $alwaysDoCodeCleanup && self.respond_to?('code_cleanup')
266
+ self.code_cleanup if @sunflower.always_do_code_cleanup && self.respond_to?('code_cleanup')
233
267
 
234
268
  r=@sunflower.API("action=edit&bot=1&title=#{CGI.escape(title)}&text=#{CGI.escape(@text)}&summary=#{CGI.escape(summary)}&token=#{CGI.escape(@edittoken)}")# if @sunflower.isBot?
235
269
  end
@@ -4,7 +4,7 @@ class Sunflower
4
4
  def make_list type, *parameters
5
5
  type=type.downcase.gsub(/[^a-z]/, '')
6
6
  first=parameters[0]
7
- firstE=CGI.escape first
7
+ firstE=CGI.escape first.to_s
8
8
 
9
9
  case type
10
10
  when 'file'
@@ -29,11 +29,11 @@ class Sunflower
29
29
 
30
30
  when 'categorieson'
31
31
  r=self.API('action=query&prop=categories&cllimit=500&titles='+firstE)
32
- list=r['query']['pages'].first['categories'].map{|v| v['title']} #extract titles
32
+ list=r['query']['pages'].first['categories'].map{|v| v['title']}
33
33
 
34
34
  when 'category'
35
35
  r=self.API('action=query&list=categorymembers&cmprop=title&cmlimit=5000&cmtitle='+firstE)
36
- list=r['query']['categorymembers'].map{|v| v['title']} #extract titles
36
+ list=r['query']['categorymembers'].map{|v| v['title']}
37
37
 
38
38
  when 'categoryr', 'categoryrecursive'
39
39
  list=[] #list of articles
@@ -48,43 +48,43 @@ class Sunflower
48
48
 
49
49
  when 'linkson'
50
50
  r=self.API('action=query&prop=links&pllimit=5000&titles='+firstE)
51
- list=r['query']['pages'].first['links'].map{|v| v['title']} #extract titles
51
+ list=r['query']['pages'].first['links'].map{|v| v['title']}
52
52
 
53
53
  when 'transclusionson', 'templateson'
54
54
  r=self.API('action=query&prop=templates&tllimit=5000&titles='+firstE)
55
- list=r['query']['pages'].first['templates'].map{|v| v['title']} #extract titles
55
+ list=r['query']['pages'].first['templates'].map{|v| v['title']}
56
56
 
57
57
  when 'usercontribs', 'contribs'
58
58
  r=self.API('action=query&list=usercontribs&uclimit=5000&ucprop=title&ucuser='+firstE)
59
- list=r['query']['usercontribs'].map{|v| v['title']} #extract titles
59
+ list=r['query']['usercontribs'].map{|v| v['title']}
60
60
 
61
61
  when 'whatlinksto', 'whatlinkshere'
62
62
  r=self.API('action=query&list=backlinks&bllimit=5000&bltitle='+firstE)
63
- list=r['query']['backlinks'].map{|v| v['title']} #extract titles
63
+ list=r['query']['backlinks'].map{|v| v['title']}
64
64
 
65
65
  when 'whattranscludes', 'whatembeds'
66
66
  r=self.API('action=query&list=embeddedin&eilimit=5000&eititle='+firstE)
67
- list=r['query']['embeddedin'].map{|v| v['title']} #extract titles
67
+ list=r['query']['embeddedin'].map{|v| v['title']}
68
68
 
69
69
  when 'image', 'imageusage'
70
70
  r=self.API('action=query&list=imageusage&iulimit=5000&iutitle='+firstE)
71
- list=r['query']['imageusage'].map{|v| v['title']} #extract titles
71
+ list=r['query']['imageusage'].map{|v| v['title']}
72
72
 
73
73
  when 'search'
74
74
  r=self.API('action=query&list=search&srwhat=text&srlimit=5000&srnamespace='+(parameters[1]=='allns' ? CGI.escape('0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|100|101|102|103') : '0')+'&srsearch='+firstE)
75
- list=r['query']['search'].map{|v| v['title']} #extract titles
75
+ list=r['query']['search'].map{|v| v['title']}
76
76
 
77
77
  when 'searchtitles'
78
78
  r=self.API('action=query&list=search&srwhat=title&srlimit=5000&srnamespace='+(parameters[1]=='allns' ? CGI.escape('0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|100|101|102|103') : '0')+'&srsearch='+firstE)
79
- list=r['query']['search'].map{|v| v['title']} #extract titles
79
+ list=r['query']['search'].map{|v| v['title']}
80
80
 
81
81
  when 'random'
82
- r=self.API('action=query&list=random&rnnamespace=0&rnlimit='+first.gsub(/\D/))
83
- list=r['query']['random'].map{|v| v['title']} #extract titles
82
+ r=self.API('action=query&list=random&rnnamespace=0&rnlimit='+firstE)
83
+ list=r['query']['random'].map{|v| v['title']}
84
84
 
85
85
  when 'external', 'linksearch'
86
86
  r=self.API('action=query&euprop=title&list=exturlusage&eulimit=5000&euquery='+firstE)
87
- list=r['query']['exturlusage'].map{|v| v['title']} #extract titles
87
+ list=r['query']['exturlusage'].map{|v| v['title']}
88
88
 
89
89
  when 'google'
90
90
  limit=[parameters[1].to_i,999].min
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sunflower
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 13
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- version: "0.2"
8
+ - 3
9
+ version: "0.3"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Matma Rex
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  requirements: []
107
107
 
108
108
  rubyforge_project:
109
- rubygems_version: 1.7.2
109
+ rubygems_version: 1.7.1
110
110
  signing_key:
111
111
  specification_version: 3
112
112
  summary: Sunflower is a lightweight library to provide access to MediaWiki API from Ruby.