livejournal 0.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.
data/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2
+ of this software and associated documentation files (the "Software"), to deal
3
+ in the Software without restriction, including without limitation the rights
4
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5
+ copies of the Software, and to permit persons to whom the Software is
6
+ furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in
9
+ all copies or substantial portions of the Software.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17
+ SOFTWARE.
data/README ADDED
@@ -0,0 +1,50 @@
1
+ =ljrb: LiveJournal Ruby module
2
+
3
+ Copyright:: Copyright (C) 2005 Evan Martin <martine@danga.com>
4
+ Website:: http://neugierig.org/software/livejournal/ruby
5
+
6
+ Example usage:
7
+ require 'livejournal/login'
8
+
9
+ puts "Logging in..."
10
+ user = LiveJournal::User.new('test', 'test')
11
+ login = LiveJournal::Request::Login.new(user)
12
+ login.run
13
+
14
+ puts "Login response:"
15
+ login.dumpresponse
16
+
17
+ puts "User's full name: #{user.fullname}"
18
+
19
+ ==LiveJournal Datatypes
20
+ * LiveJournal::Server
21
+ * LiveJournal::User
22
+ * LiveJournal::Entry
23
+ * LiveJournal::Comment
24
+ * LiveJournal::Friend
25
+
26
+ ==Implemented Requests
27
+ ===Login Requests
28
+ * LiveJournal::Request::Login
29
+ * LiveJournal::Request::SessionGenerate (only useful for syncing)
30
+ ===Friend Requests
31
+ * LiveJournal::Request::Friends
32
+ * LiveJournal::Request::FriendOfs
33
+ * LiveJournal::Request::CheckFriends
34
+ ===Entry Requests
35
+ * LiveJournal::Request::GetEvents
36
+ * LiveJournal::Request::EditEvent
37
+
38
+ ==Journal Offline Synchronization
39
+ * LiveJournal::Sync::Entries
40
+ * LiveJournal::Sync::Comments
41
+ See samples/export for an example of how to use these.
42
+
43
+ ==SQLite3 Support
44
+ * LiveJournal::Database -- storing/loading entries+comments with SQLite3
45
+ Integrates well with syncing. See samples/export.
46
+
47
+ ==Other Features
48
+ * LiveJournal::LogJam -- interface with LogJam (http://logjam.danga.com) 's
49
+ journal exports
50
+
data/Rakefile ADDED
@@ -0,0 +1,60 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/packagetask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/testtask'
6
+
7
+ PKG_NAME = 'livejournal'
8
+ PKG_VERSION = '0.0.1'
9
+
10
+ FILES = FileList[
11
+ 'Rakefile', 'README', 'LICENSE', 'setup.rb',
12
+ 'lib/**/*', 'sample/*', 'test/*'
13
+ ]
14
+
15
+ spec = Gem::Specification.new do |s|
16
+ s.name = PKG_NAME
17
+ s.version = PKG_VERSION
18
+ s.summary = 'module for interacting with livejournal'
19
+ s.description = %q{LiveJournal module. Post to livejournal, retrieve friends
20
+ lists, edit entries, sync journal to an offline database.}
21
+ s.author = 'Evan Martin'
22
+ s.email = 'martine@danga.com'
23
+ s.homepage = 'http://neugierig.org/software/livejournal/ruby/'
24
+
25
+ s.has_rdoc = true
26
+ s.files = FILES.to_a
27
+ end
28
+
29
+ desc 'Build Package'
30
+ Rake::GemPackageTask.new(spec) do |p|
31
+ p.need_tar = true
32
+ p.need_zip = true
33
+ end
34
+
35
+
36
+ desc 'Generate RDoc'
37
+ Rake::RDocTask.new :rdoc do |rd|
38
+ rd.title = "ljrb (LiveJournal Ruby module) Documentation"
39
+ rd.rdoc_dir = 'doc'
40
+ rd.rdoc_files.add 'lib', 'README', 'LICENSE'
41
+ rd.main = 'README'
42
+ end
43
+
44
+ desc 'Run Tests'
45
+ Rake::TestTask.new :test do |t|
46
+ t.test_files = FileList['test/*.rb']
47
+ end
48
+
49
+ desc 'Push data to my webspace'
50
+ task :pushweb => [:rdoc, :package] do
51
+ pkg = "pkg/#{PKG_NAME}-#{PKG_VERSION}"
52
+ target = 'neugierig.org:/home/martine/www/neugierig/htdocs/software/livejournal/ruby'
53
+ sh %{rsync -av --delete doc/* #{target}/doc/}
54
+ sh %{rsync -av #{pkg}.* #{target}/download/}
55
+ end
56
+
57
+ desc 'Push everything'
58
+ task :push => [:pushweb] # XXX push to rubyforge
59
+
60
+ # vim: set ts=2 sw=2 et :
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/ruby
2
+ #--
3
+ # ljrb -- LiveJournal Ruby module
4
+ # Copyright (c) 2005 Evan Martin <martine@danga.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #++
24
+
25
+ module LiveJournal
26
+ # A LiveJournal server. name is currently unused.
27
+ class Server
28
+ attr_accessor :name, :url
29
+
30
+ def initialize(name, url)
31
+ @name = name
32
+ @url = url
33
+ end
34
+ end
35
+ DEFAULT_SERVER = Server.new("LiveJournal.com", "http://www.livejournal.com")
36
+
37
+ # A LiveJournal user. Given a username, password, and server, running a
38
+ # LiveJournal::Request::Login will fill in the other fields.
39
+ class User
40
+ # parameter when creating a User
41
+ attr_accessor :username, :password, :server
42
+ # Set usejournal to log in as user username but act as user usejournal.
43
+ # For example, to work with a community you own.
44
+ attr_accessor :usejournal
45
+ # User's self-reported name, as retrieved by LiveJournal::Request::Login
46
+ attr_accessor :fullname
47
+ def initialize(username=nil, password=nil, server=nil)
48
+ @username = username
49
+ @password = password
50
+ @usejournal = nil
51
+ @server = server || LiveJournal::DEFAULT_SERVER
52
+ end
53
+ def to_s
54
+ "#{@username}: '#{@fullname}'"
55
+ end
56
+ end
57
+ end
58
+
59
+ # vim: ts=2 sw=2 et :
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/ruby
2
+ #--
3
+ # ljrb -- LiveJournal Ruby module
4
+ # Copyright (c) 2005 Evan Martin <martine@danga.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #++
24
+ #
25
+ # LiveJournal comments.
26
+
27
+ # http://www.livejournal.com/developer/exporting.bml
28
+
29
+ module LiveJournal
30
+ class Comment
31
+ attr_accessor :commentid, :posterid, :itemid, :parentid
32
+ # State of the comment. Possible values: {+:active+, +:screened+, +:deleted+}
33
+ attr_accessor :state
34
+ attr_accessor :subject, :body
35
+ # a Ruby Time object
36
+ attr_reader :time
37
+
38
+ def initialize
39
+ @commentid = @posterid = @itemid = @parentid = nil
40
+ @subject = @body = nil
41
+ @time = nil
42
+ @state = :active
43
+ end
44
+
45
+ # Convert a state to the string representation used by LiveJournal.
46
+ def self.state_from_string(str)
47
+ case str
48
+ when nil; :active
49
+ when 'A'; :active
50
+ when 'D'; :deleted
51
+ when 'S'; :screened
52
+ else raise ArgumentError, "Invalid comment state: #{str.inspect}"
53
+ end
54
+ end
55
+
56
+ # Convert a state from the string representation used by LiveJournal.
57
+ def self.state_to_string state
58
+ case state
59
+ when nil; nil
60
+ when :active; nil
61
+ when :deleted; 'D'
62
+ when :screened; 'S'
63
+ else raise ArgumentError, "Invalid comment state: #{state.inspect}"
64
+ end
65
+ end
66
+
67
+ def time=(time)
68
+ raise RuntimeError, "Must use GMT times everywhere to reduce confusion. See LiveJournal::coerce_gmt for details." unless time.gmt?
69
+ @time = time
70
+ end
71
+
72
+ def ==(other)
73
+ [:commentid, :posterid, :state, :itemid, :parentid,
74
+ :subject, :body, :time].each do |attr|
75
+ return false if send(attr) != other.send(attr)
76
+ end
77
+ return true
78
+ end
79
+ end
80
+ end
81
+
82
+ # vim: ts=2 sw=2 et :
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/ruby
2
+ #--
3
+ # ljrb -- LiveJournal Ruby module
4
+ # Copyright (c) 2005 Evan Martin <martine@danga.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #++
24
+ #
25
+ # REXML is pleasant to use but hella slow, so we allow using the expat-based
26
+ # parser as well.
27
+
28
+ require 'livejournal/comment'
29
+ require 'time' # parsing xmlschema times
30
+
31
+ module LiveJournal
32
+ HAVE_XML_PARSER = true
33
+
34
+ require 'rexml/document'
35
+ require 'xml/parser' if HAVE_XML_PARSER
36
+
37
+ module Sync
38
+ module CommentsXML
39
+ def self.optional_int_string(x)
40
+ return nil unless x
41
+ x.to_i
42
+ end
43
+
44
+ def self.load_comment_from_attrs(comment, attrs)
45
+ comment.commentid = attrs['id'].to_i
46
+ comment.posterid = CommentsXML::optional_int_string attrs['posterid']
47
+ comment.itemid = CommentsXML::optional_int_string attrs['jitemid']
48
+ comment.parentid = CommentsXML::optional_int_string attrs['parentid']
49
+ statestr = attrs['state']
50
+ comment.state = LiveJournal::Comment::state_from_string(statestr) if statestr
51
+ end
52
+
53
+ class Base
54
+ attr_reader :maxid, :comments, :usermap
55
+ def initialize(data=nil)
56
+ @maxid = nil
57
+ @comments = {}
58
+ @usermap = {}
59
+ parse data if data
60
+ end
61
+ end
62
+
63
+ class WithREXML < Base
64
+ def parse(data)
65
+ doc = REXML::Document.new(data)
66
+ root = doc.root
67
+
68
+ root.elements.each('maxid') { |e| @maxid = e.text.to_i }
69
+
70
+ root.elements.each('comments/comment') do |e|
71
+ id = e.attributes['id'].to_i
72
+ comment = @comments[id] || Comment.new
73
+ CommentsXML::load_comment_from_attrs(comment, e.attributes)
74
+ e.elements.each('subject') { |s| comment.subject = s.text }
75
+ e.elements.each('body') { |s| comment.body = s.text }
76
+ e.elements.each('date') { |s| comment.time = Time::xmlschema s.text }
77
+ @comments[id] = comment
78
+ end
79
+
80
+ root.elements.each('usermaps/usermap') do |e|
81
+ id = e.attributes['id'].to_i
82
+ user = e.attributes['user']
83
+ @usermap[id] = user
84
+ end
85
+ end
86
+ end
87
+
88
+ if HAVE_XML_PARSER
89
+ class WithExpat < Base
90
+ class Parser < XMLParser
91
+ attr_reader :maxid, :comments, :usermap
92
+ def initialize
93
+ super
94
+ @maxid = nil
95
+ @cur_comment = nil
96
+ @comments = {}
97
+ @usermap = {}
98
+ @content = nil
99
+ end
100
+ def startElement(name, attrs)
101
+ case name
102
+ when 'maxid'
103
+ @content = ''
104
+ when 'comment'
105
+ id = attrs['id'].to_i
106
+ @cur_comment = @comments[id] || Comment.new
107
+ @comments[id] = @cur_comment
108
+ CommentsXML::load_comment_from_attrs(@cur_comment, attrs)
109
+ when 'usermap'
110
+ id = attrs['id'].to_i
111
+ user = attrs['user']
112
+ @usermap[id] = user
113
+ when 'date'
114
+ @content = ''
115
+ when 'subject'
116
+ @content = ''
117
+ when 'body'
118
+ @content = ''
119
+ end
120
+ end
121
+ def character(data)
122
+ @content << data if @content
123
+ end
124
+ def endElement(name)
125
+ return unless @content
126
+ case name
127
+ when 'maxid'
128
+ @maxid = @content.to_i
129
+ when 'date'
130
+ @cur_comment.time = Time::xmlschema(@content)
131
+ when 'subject'
132
+ @cur_comment.subject = @content
133
+ when 'body'
134
+ @cur_comment.body = @content
135
+ end
136
+ @content = nil
137
+ end
138
+ end
139
+ def parse(data)
140
+ parser = Parser.new
141
+ parser.parse(data)
142
+ @maxid = parser.maxid
143
+ @comments = parser.comments
144
+ @usermap = parser.usermap
145
+ end
146
+ end # class WithExpat
147
+ end # if HAVE_XML_PARSER
148
+
149
+ if HAVE_XML_PARSER
150
+ Parser = WithExpat
151
+ else
152
+ Parser = WithREXML
153
+ end
154
+ end # module CommentsXML
155
+ end # module Sync
156
+ end # module LiveJournal
157
+
158
+ # vim: ts=2 sw=2 et :
@@ -0,0 +1,292 @@
1
+ #!/usr/bin/ruby
2
+ #--
3
+ # ljrb -- LiveJournal Ruby module
4
+ # Copyright (c) 2005 Evan Martin <martine@danga.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #++
24
+ #
25
+ # This module interacts with the sqlite export from LogJam.
26
+
27
+ require 'sqlite3'
28
+
29
+ module LiveJournal
30
+ # A SQLite database dump.
31
+ class DatabaseError < RuntimeError; end
32
+ class Database
33
+ EXPECTED_DATABASE_VERSION = "1"
34
+ SCHEMA = %q{
35
+ CREATE TABLE meta (
36
+ key STRING PRIMARY KEY,
37
+ value STRING
38
+ );
39
+ CREATE TABLE entry (
40
+ itemid INTEGER PRIMARY KEY,
41
+ anum INTEGER,
42
+ subject STRING,
43
+ event STRING,
44
+ moodid INTEGER, mood STRING, music STRING, taglist STRING,
45
+ pickeyword STRING, preformatted INTEGER, backdated INTEGER,
46
+ comments INTEGER, year INTEGER, month INTEGER, day INTEGER,
47
+ timestamp INTEGER, security INTEGER
48
+ );
49
+ CREATE INDEX dateindex ON entry (year, month, day);
50
+ CREATE INDEX timeindex ON entry (timestamp);
51
+ CREATE TABLE comment (
52
+ commentid INTEGER PRIMARY KEY,
53
+ posterid INTEGER,
54
+ itemid INTEGER,
55
+ parentid INTEGER,
56
+ state STRING, -- screened/deleted/active
57
+ subject STRING,
58
+ body STRING,
59
+ timestamp INTEGER -- unix timestamp
60
+ );
61
+ CREATE INDEX commententry ON comment (itemid);
62
+ CREATE TABLE users (
63
+ userid INTEGER PRIMARY KEY,
64
+ username STRING
65
+ );
66
+ CREATE TABLE commentprop (
67
+ commentid INTEGER, -- not primary key 'cause non-unique
68
+ key STRING,
69
+ value STRING
70
+ );
71
+ }.gsub(/^ /, '')
72
+
73
+ def self.optional_to_i(x)
74
+ return nil if x.nil?
75
+ return x.to_i
76
+ end
77
+
78
+ attr_reader :db
79
+ def initialize(filename, create=false)
80
+ exists = FileTest::exists? filename if create
81
+ @db = SQLite3::Database.new(filename)
82
+
83
+ # We'd like to use type translation, but it unfortunately fails on MAX()
84
+ # queries.
85
+ # @db.type_translation = true
86
+
87
+ if exists
88
+ version = self.version
89
+ unless version == EXPECTED_DATABASE_VERSION
90
+ raise DatabaseError, "Database version mismatch -- db has #{version.inspect}, expected #{EXPECTED_DATABASE_VERSION.inspect}"
91
+ end
92
+ end
93
+
94
+ #trace!
95
+ if create and not exists
96
+ run_schema!
97
+ self.version = EXPECTED_DATABASE_VERSION
98
+ end
99
+ end
100
+
101
+ def transaction
102
+ @db.transaction { yield }
103
+ end
104
+
105
+ def close
106
+ @db.close
107
+ end
108
+
109
+ def self.db_value(name, sym)
110
+ class_eval %{def #{sym}; get_meta(#{name.inspect}); end}
111
+ class_eval %{def #{sym}=(v); set_meta(#{name.inspect}, v); end}
112
+ end
113
+
114
+ db_value 'username', :username
115
+ db_value 'lastsync', :lastsync
116
+ db_value 'version', :version
117
+
118
+ def run_schema!
119
+ transaction do
120
+ @db.execute_batch(SCHEMA)
121
+ end
122
+ end
123
+
124
+ # Turn tracing on.
125
+ def trace!
126
+ @db.trace() do |data, sql|
127
+ puts "SQL> #{sql.inspect}"
128
+ end
129
+ end
130
+
131
+ # Fetch a specific itemid.
132
+ def get_entry(itemid)
133
+ query_entry("select * from entry where itemid=?", itemid)
134
+ end
135
+
136
+ # Given SQL that selects an entry, return that entry.
137
+ def query_entry(sql, *sqlargs)
138
+ row = @db.get_first_row(sql, *sqlargs)
139
+ return Entry.new.load_from_database_row(row)
140
+ end
141
+
142
+ # Given SQL that selects some entries, yield each entry.
143
+ def query_entries(sql, *sqlargs)
144
+ @db.execute(sql, *sqlargs) do |row|
145
+ yield Entry.new.load_from_database_row(row)
146
+ end
147
+ end
148
+
149
+ # Yield most recent limit entries.
150
+ def each_entry(limit=nil, &block)
151
+ sql = 'SELECT * FROM entry ORDER BY itemid DESC'
152
+ sql += " LIMIT #{limit}" if limit
153
+ query_entries sql, &block
154
+ end
155
+
156
+ # Return the total number of entries.
157
+ def total_entry_count
158
+ @db.get_first_value('SELECT COUNT(*) FROM entry').to_i
159
+ end
160
+
161
+ def store_entry entry
162
+ sql = 'INSERT OR REPLACE INTO entry VALUES (' + ("?, " * 16) + '?)'
163
+ @db.execute(sql, *entry.to_database_row)
164
+ end
165
+
166
+ def last_comment_meta
167
+ Database::optional_to_i(
168
+ @db.get_first_value('SELECT MAX(commentid) FROM comment'))
169
+ end
170
+ def last_comment_full
171
+ Database::optional_to_i(
172
+ @db.get_first_value('SELECT MAX(commentid) FROM comment ' +
173
+ 'WHERE body IS NOT NULL'))
174
+ end
175
+
176
+ def _store_comments(comments, meta_only=true)
177
+ transaction do
178
+ sql = "INSERT OR REPLACE INTO comment "
179
+ if meta_only
180
+ sql += "(commentid, posterid, state) VALUES (?, ?, ?)"
181
+ else
182
+ sql += "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
183
+ end
184
+ @db.prepare(sql) do |stmt|
185
+ comments.each do |id, comment|
186
+ if meta_only
187
+ stmt.execute(comment.commentid, comment.posterid,
188
+ LiveJournal::Comment::state_to_string(comment.state))
189
+ else
190
+ stmt.execute(*comment.to_database_row)
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ def store_comments_meta(comments)
198
+ _store_comments(comments, true)
199
+ end
200
+ def store_comments_full(comments)
201
+ _store_comments(comments, false)
202
+ end
203
+
204
+ def store_usermap(usermap)
205
+ transaction do
206
+ sql = "INSERT OR REPLACE INTO users VALUES (?, ?)"
207
+ @db.prepare(sql) do |stmt|
208
+ usermap.each do |id, user|
209
+ stmt.execute(id, user)
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ def get_meta key
216
+ return @db.get_first_value('SELECT value FROM meta WHERE key=?', key)
217
+ end
218
+ def set_meta key, value
219
+ db.transaction do
220
+ @db.execute('INSERT OR REPLACE INTO meta VALUES (?, ?)', key, value)
221
+ end
222
+ end
223
+ end
224
+
225
+ class Entry
226
+ # Parse an entry from a row from the database.
227
+ def load_from_database_row row
228
+ @itemid, @anum = row[0].to_i, row[1].to_i
229
+ @subject, @event = row[2], row[3]
230
+ @moodid, @mood = row[4].nil? ? nil : row[4].to_i, row[5]
231
+ @music, @taglist, @pickeyword = row[6], row[7], row[8]
232
+ @taglist = if @taglist then @taglist.split(/, /) else [] end
233
+ @preformatted, @backdated = !row[9].nil?, !row[10].nil?
234
+ @comments = case Database::optional_to_i(row[11])
235
+ when nil; :normal
236
+ when 1; :none
237
+ when 2; :noemail
238
+ else raise DatabaseError, "Bad comments value: #{row[11].inspect}"
239
+ end
240
+
241
+ @time = Time.at(row[15].to_i).utc
242
+
243
+ case Database::optional_to_i(row[16])
244
+ when nil
245
+ @security = :public
246
+ when 0
247
+ @security = :private
248
+ when 1
249
+ @security = :friends
250
+ else
251
+ @security = :custom
252
+ @allowmask = row[16]
253
+ end
254
+
255
+ self
256
+ end
257
+ def to_database_row
258
+ comments = case @comments
259
+ when :normal; nil
260
+ when :none; 1
261
+ when :noemail; 2
262
+ end
263
+ security = case @security
264
+ when :public; nil
265
+ when :private; 0
266
+ when :friends; 1
267
+ when :custom; @allowmask
268
+ end
269
+ [@itemid, @anum, @subject, @event,
270
+ @moodid, @mood, @music, @taglist.join(', '), @pickeyword,
271
+ @preformatted ? 1 : nil, @backdated ? 1 : nil, comments,
272
+ @time.year, @time.mon, @time.day, @time.to_i, security]
273
+ end
274
+ end
275
+ class Comment
276
+ def load_from_database_row row
277
+ @commentid, @posterid = row[0].to_i, row[1].to_i
278
+ @itemid, @parentid = row[2].to_i, row[3].to_i
279
+ @state = Comment::state_from_string row[4]
280
+ @subject, @body = row[5], row[6]
281
+ @time = Time.at(row[7]).utc
282
+ self
283
+ end
284
+ def to_database_row
285
+ state = Comment::state_to_string @state
286
+ [@commentid, @posterid, @itemid, @parentid,
287
+ state, @subject, @body, @time.to_i]
288
+ end
289
+ end
290
+ end
291
+
292
+ # vim: ts=2 sw=2 et :