pastehub 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,70 @@
1
+ #
2
+ # crypt.rb - PasteHub's encrypt/decrypt library
3
+ #
4
+ # Copyright (c) 2009-2011 Kiyoka Nishiyama <kiyoka@sumibi.org>
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # 3. Neither the name of the authors nor the names of its contributors
18
+ # may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+ #
33
+ #
34
+ require 'openssl'
35
+ require 'base64'
36
+
37
+ module PasteHub
38
+ class Crypt
39
+ def initialize( password )
40
+ @password = password
41
+ @des3 = OpenSSL::Cipher.new('des3')
42
+ end
43
+
44
+ def en( str )
45
+ if 0 == str.size
46
+ ""
47
+ else
48
+ @des3.encrypt
49
+ @des3.pkcs5_keyivgen( @password )
50
+ Base64.encode64( @des3.update( str ) + @des3.final )
51
+ end
52
+ end
53
+
54
+ def de( str )
55
+ if 0 == str.size
56
+ ""
57
+ else
58
+ begin
59
+ @des3.decrypt
60
+ @des3.pkcs5_keyivgen( @password )
61
+ ret = @des3.update( Base64.decode64( str ))
62
+ ret += @des3.final
63
+ rescue OpenSSL::Cipher::CipherError => e
64
+ ret = nil
65
+ end
66
+ ret
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,187 @@
1
+ #
2
+ # localdb.rb - PasteHub's localdb manager
3
+ #
4
+ # Copyright (c) 2009-2011 Kiyoka Nishiyama <kiyoka@sumibi.org>
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # 3. Neither the name of the authors nor the names of its contributors
18
+ # may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+ #
33
+ #
34
+ require 'gdbm'
35
+
36
+ module PasteHub
37
+ class MutexSingleton
38
+ include Singleton
39
+
40
+ attr_reader :mutex
41
+
42
+ def self.instance
43
+ @@instance ||= new
44
+ end
45
+
46
+ def initialize( )
47
+ @mutex = Mutex.new
48
+ end
49
+ end
50
+
51
+ ONLINE_STATE_KEY='__ONLINE_STATE'
52
+ LOCAL_DATE_KEY ='__LOCAL_DATE'
53
+ SERVER_DATE_KEY ='__SERVER_DATE'
54
+
55
+ LOCAL_PREFIX = "local::"
56
+ SERVER_PREFIX = "server::"
57
+
58
+ class LocalDB
59
+ def initialize( basepath = "/tmp/")
60
+ @mutex = MutexSingleton.instance.mutex
61
+ @basepath = basepath
62
+ end
63
+
64
+ def open( username, reader = false )
65
+ closed = false
66
+ (60*2).times { |n|
67
+ @mutex.synchronize {
68
+ if reader
69
+ @db = GDBM.new( @basepath + username + ".db", nil, GDBM::READER | GDBM::NOLOCK )
70
+ else
71
+ @db = GDBM.new( @basepath + username + ".db", nil, GDBM::WRCREAT )
72
+ end
73
+ closed = @db.closed?
74
+ }
75
+ break unless closed
76
+ #STDERR.puts "#Warning: DB open fail(locked) retry..."
77
+ sleep 0.5
78
+ }
79
+ @mutex.synchronize {
80
+ closed = @db.closed?
81
+ }
82
+ if closed
83
+ raise RuntimeError, sprintf( "DBM.new open error: file=%s", username + ".db" )
84
+ end
85
+ @username = username
86
+ end
87
+
88
+ def getList( limit = nil )
89
+ arr = self._getList().reject{|x| x.match( /^_/ )}
90
+ if limit
91
+ arr.take( limit )
92
+ else
93
+ arr
94
+ end
95
+ end
96
+
97
+ def _getList( )
98
+ forward_match_keys( LOCAL_PREFIX ).sort {|a,b| -(a <=> b) }.map {|x|
99
+ x[(LOCAL_PREFIX.size)...(x.size)]
100
+ }
101
+ end
102
+
103
+ def getServerList( )
104
+ self._getServerList().reject{|x| x.match( /^_/ )}
105
+ end
106
+
107
+ def _getServerList( )
108
+ forward_match_keys( SERVER_PREFIX ).sort {|a,b| -(a <=> b) }.map {|x|
109
+ x[(SERVER_PREFIX.size)...(x.size)]
110
+ }
111
+ end
112
+
113
+ def getValue( key, fallback = false )
114
+ val = nil
115
+ @mutex.synchronize {
116
+ val = @db[ LOCAL_PREFIX + key ]
117
+ }
118
+ if val
119
+ val.force_encoding("UTF-8")
120
+ else
121
+ fallback
122
+ end
123
+ end
124
+
125
+ def insertValue( key, value )
126
+ @mutex.synchronize {
127
+ @db[ LOCAL_PREFIX + key.force_encoding("ASCII-8BIT") ] = value.force_encoding("ASCII-8BIT")
128
+ }
129
+ end
130
+
131
+ def deleteValue( key )
132
+ ret = false
133
+ @mutex.synchronize {
134
+ val = @db[ LOCAL_PREFIX + key ]
135
+ if val
136
+ @db.delete( LOCAL_PREFIX + key )
137
+ ret = true
138
+ else
139
+ ret = false
140
+ end
141
+ }
142
+ ret
143
+ end
144
+
145
+ def setServerFlag( key )
146
+ @mutex.synchronize {
147
+ @db[ SERVER_PREFIX + key ] = '1'
148
+ }
149
+ end
150
+
151
+ def onServer?( key )
152
+ ret = false
153
+ @mutex.synchronize {
154
+ if @db[ SERVER_PREFIX + key ]
155
+ ret = true
156
+ else
157
+ ret = false
158
+ end
159
+ }
160
+ ret
161
+ end
162
+
163
+ def forward_match_keys( prefix )
164
+ keys = []
165
+ @mutex.synchronize {
166
+ keys = @db.keys( ).select {|key|
167
+ key.match( "^" + prefix )
168
+ }
169
+ }
170
+ keys
171
+ end
172
+
173
+ def clear
174
+ @mutex.synchronize {
175
+ @db.clear
176
+ }
177
+ end
178
+
179
+ def close
180
+ ret = nil
181
+ @mutex.synchronize {
182
+ ret = @db.close
183
+ }
184
+ ret
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,95 @@
1
+ #
2
+ # log.rb - PasteHub's log manager
3
+ #
4
+ # Copyright (c) 2009-2011 Kiyoka Nishiyama <kiyoka@sumibi.org>
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # 3. Neither the name of the authors nor the names of its contributors
18
+ # may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+ #
33
+ #
34
+
35
+ module PasteHub
36
+ class LogBase
37
+ def initialize( hash )
38
+ @user = nil
39
+ if hash.is_a? Hash
40
+ if hash.has_key?( :user )
41
+ @user = hash[ :user ]
42
+ else
43
+ @user = 'UNSPECIFIED'
44
+ end
45
+ if hash.has_key?( :api )
46
+ @api = hash[ :api ]
47
+ else
48
+ raise ArgumentError if not @api
49
+ end
50
+ else
51
+ raise ArgumentError
52
+ end
53
+
54
+ @uri = URI.parse( "http://localhost:9880/pastehub.webapi" )
55
+ @http = Net::HTTP.new(@uri.host, @uri.port)
56
+ #Fluent::Logger::FluentLogger.open(nil, :host=>'localhost', :port=>24224)
57
+ end
58
+
59
+ def write( hashData )
60
+ begin
61
+ jsonStr = hashData.to_json
62
+ resp = @http.post( @uri.request_uri, "json=" + jsonStr )
63
+ if "200" != resp.code
64
+ STDERR.print "Error: can't post to fluentd(http)[1].\n"
65
+ end
66
+ rescue
67
+ STDERR.print "Error: can't post to fluentd(http)[2].\n"
68
+ end
69
+ #Fluent::Logger.post("pastehub.webapi", hashData)
70
+ true
71
+ end
72
+ end
73
+
74
+ class Log < LogBase
75
+ # info without error
76
+ def info( message, moreHash = {} )
77
+ hashData = {
78
+ "user"=>"#{@user}",
79
+ "api"=>"#{@api}",
80
+ "message"=>"#{message}",
81
+ "error" => false}
82
+ write( hashData.merge( moreHash ) )
83
+ end
84
+
85
+ # errors
86
+ def error( message, moreHash = {} )
87
+ hashData = {
88
+ "user"=>"#{@user}",
89
+ "api"=>"#{@api}",
90
+ "message"=>"#{message}",
91
+ "error" => true}
92
+ write( hashData.merge( moreHash ) )
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,66 @@
1
+ #
2
+ # macosx.rb - PasteHub's clipboard sync thread for MacOS X
3
+ #
4
+ # Copyright (c) 2009-2011 Kiyoka Nishiyama <kiyoka@sumibi.org>
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # 3. Neither the name of the authors nor the names of its contributors
18
+ # may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+ #
33
+ #
34
+
35
+ module PasteHub
36
+
37
+ class MacOSX
38
+
39
+ def self.push( data )
40
+ pbcopy = "/usr/bin/pbcopy"
41
+ if File.exist?( pbcopy )
42
+ begin
43
+ IO.popen(pbcopy, "r+") {|io|
44
+ io.write data
45
+ io.close
46
+ }
47
+ STDERR.puts "Info: push to MacOS X clipboard."
48
+ rescue IOError => e
49
+ STDERR.puts( "Error: program #{pbcopy} not found." )
50
+ return false
51
+ end
52
+ end
53
+ end
54
+
55
+ def self.hasNew?( username )
56
+ osxPaste = open( "|/usr/bin/pbpaste" ) { |f|
57
+ f.read
58
+ }
59
+ if 0 < osxPaste.size
60
+ osxPaste
61
+ else
62
+ nil
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,213 @@
1
+ #
2
+ # masterdb.rb - PasteHub's master database manager
3
+ #
4
+ # Copyright (c) 2009-2011 Kiyoka Nishiyama <kiyoka@sumibi.org>
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # 3. Neither the name of the authors nor the names of its contributors
18
+ # may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
+ #
33
+ #
34
+ require 'dynamoid'
35
+ require 'date'
36
+
37
+ module PasteHub
38
+
39
+ # fake DynamoDB on localhost
40
+ Dynamoid.configure do |conf|
41
+ config = PasteHub::Config.instance
42
+ conf.adapter = 'aws_sdk'
43
+ conf.namespace = config.domain # To namespace tables created by Dynamoid from other tables you might have.
44
+ conf.warn_on_scan = config.awsWarn # Output a warning to the logger when you perform a scan rather than a query on a table.
45
+ conf.partitioning = false # Spread writes randomly across the database. See "partitioning" below for more.
46
+ conf.partition_size = 1 # Determine the key space size that writes are randomly spread across.
47
+ conf.read_capacity = 1 # Read capacity for your tables
48
+ conf.write_capacity = 1 # Write capacity for your tables
49
+ conf.access_key = config.dynamoAccessKey # If connecting to DynamoDB, your access key is required.
50
+ conf.secret_key = config.dynamoSecretKey # So is your secret key.
51
+ conf.endpoint = config.dynamoEp # Set the regional endpoint
52
+ conf.port = '443' # Use real dynamo
53
+ if not config.aws
54
+ conf.endpoint = 'localhost' # Set the regional endpoint
55
+ conf.use_ssl = false # Don't use SSL
56
+ conf.port = '4567' # Use fake_dynamo
57
+ end
58
+ end
59
+
60
+ class User
61
+ include Dynamoid::Document
62
+
63
+ table :name => :users, :key => :username
64
+
65
+ field :username
66
+ field :secretKey
67
+ field :created_datetime, :datetime
68
+ field :touched_datetime, :datetime
69
+ #field :friends, :set
70
+
71
+ # index :username
72
+
73
+ validates_presence_of :username
74
+ validates_presence_of :secretKey
75
+ end
76
+
77
+ class Entry
78
+ include Dynamoid::Document
79
+
80
+ table :name => :entries, :key => :userkey
81
+
82
+ field :userkey
83
+ field :username
84
+ # field :comment
85
+ # field :from
86
+ # field :to
87
+ # field :cc
88
+ # field :type
89
+ field :data
90
+
91
+ index [:username]
92
+
93
+ validates_presence_of :userkey
94
+ # validates_presence_of :data
95
+
96
+ def id
97
+ userkey
98
+ end
99
+ end
100
+
101
+
102
+ class Users
103
+ def addUser( username, secretKey )
104
+ user = User.new( :username => username, :secretKey => secretKey, :created_datetime => DateTime.new())
105
+ user.save
106
+ end
107
+
108
+ def updateSecretKey( username, secretKey )
109
+ arr = User.where( :username => username ).all
110
+ if 0 < arr.size
111
+ arr.first.secretKey = secretKey
112
+ arr.first.save
113
+ true
114
+ else
115
+ false
116
+ end
117
+ end
118
+
119
+ def getSecretKey( username )
120
+ arr = User.where( :username => username ).all
121
+ if 0 < arr.size
122
+ arr.first.secretKey
123
+ else
124
+ nil
125
+ end
126
+ end
127
+
128
+ def touch( username )
129
+ arr = User.where( :username => username ).all
130
+ if 0 < arr.size
131
+ arr.first.touched_datetime = DateTime.new()
132
+ arr.first.save
133
+ true
134
+ else
135
+ nil
136
+ end
137
+ end
138
+
139
+ def getList()
140
+ arr = User.all
141
+ arr.map { |x| x.username }.sort
142
+ end
143
+
144
+ def __deleteUser( username )
145
+ user = User.find( username )
146
+ if user
147
+ user.delete
148
+ true
149
+ else
150
+ false
151
+ end
152
+ end
153
+
154
+ end
155
+
156
+
157
+ class Entries
158
+ def initialize( username )
159
+ @holdUsername = username
160
+ end
161
+
162
+ def getList( limit = nil )
163
+ arr = Entry.where( :username => @holdUsername ).map {|x|
164
+ # remove username from `userkey'
165
+ field = x.userkey.split( /::/ )
166
+ field[1]
167
+ }.sort {|a,b| -(a <=> b) }
168
+ if limit
169
+ arr.take( limit )
170
+ else
171
+ arr
172
+ end
173
+ end
174
+
175
+ def getValue( key, fallback = false )
176
+ p "username=#{@holdUsername}"
177
+ p "key=#{key}"
178
+ entry = Entry.find( @holdUsername + "::" + key, :consistent_read => true )
179
+ if entry
180
+ if entry.data
181
+ entry.data.force_encoding("UTF-8")
182
+ end
183
+ else
184
+ fallback
185
+ end
186
+ end
187
+
188
+ def insertValue( key, value )
189
+ entry = Entry.new( :username => @holdUsername,
190
+ :userkey => @holdUsername + "::" + key,
191
+ :data => value.force_encoding("UTF-8") )
192
+ entry.save
193
+ end
194
+
195
+ def deleteLast( )
196
+ arr = getList( )
197
+ if 0 < arr.size
198
+ deleteValue( arr[arr.size-1] )
199
+ end
200
+ end
201
+
202
+ def deleteValue( key )
203
+ # caution: This method is non consistent read.
204
+ entry = Entry.find( @holdUsername + "::" + key )
205
+ if entry
206
+ entry.delete
207
+ true
208
+ else
209
+ false
210
+ end
211
+ end
212
+ end
213
+ end