# # waste.rb - »¶ºâÆüµ­Library # # Copyright (C) 2001, TADA Tadashi # WASTE_VERSION = '0.9.2' require 'pstore' require 'cgi' require 'lock' # # Integer # class Integer # adding comma each three digits. def to_disp_str self.to_s.reverse.gsub( /\d\d\d/, '\0,' ).reverse.sub( /^([-]{0,1}),/, '\1' ) end end # # CGI # class CGI def valid?( param, idx = 0 ) self[param] and self[param][idx] and self[param][idx].length > 0 end end # # Time # class Time def rfc1123_date was_utc = self.utc? result = self.utc.strftime( '%a, %d %b %Y %X GMT' ) self.localtime if not was_utc result end end # # String # class String def make_link r = %r<(((http[s]{0,1}|ftp)://[\(\)%#!/0-9a-zA-Z_$@.&+-,'"*=;?:~-]+)|([0-9a-zA-Z_.-]+@[\(\)%!0-9a-zA-Z_$@.&+-,'"*-]+))> return self. gsub( ' ', "\001" ). gsub( '<', "\002" ). gsub( '>', "\003" ). gsub( '&', '&' ). gsub( r ){ $1 == $2 ? "#$2" : "#$4" }. gsub( "\003", '>' ). gsub( "\002", '<' ). gsub( "\001", ' ' ). gsub( "\t", ' ' * 8 ) end end # # class Item # class Item attr_reader :name, :price, :note def initialize( name, price, note ) raise ArgumentError::new( "Bad Item [#{name}]" ) if not name or name.length == 0 raise ArgumentError::new( 'Bad price' ) if not price or price.to_i == 0 @name = name @price = price.to_i @note = note end end # # class Diary # class Diary attr_reader :name, :date, :note def initialize( name, date, note ) raise ArgumentError::new( 'Bad name' ) if not name or name.length == 0 raise ArgumentError::new( 'Bad date' ) if not date or /^\d\d\d\d\/\d\d\/\d\d$/ !~ date @name = name @date = date @note = note @items = [] end def add( item ) @items << item end def []( idx ) @items[idx] end def each_item @items.each do |item| yield item end end def item_size @items.length end def total price = 0 @items.each do |i| price += i.price end price end end # # class Waste # class Waste attr_reader :year, :user, :edit, :person def Waste::version WASTE_VERSION end def version Waste::version end def initialize @cgi = CGI::new @diary = nil @user = @cgi.cookies['waste'][0] || '' @edit = nil @person = @cgi.valid?( 'person' ) ? @cgi['person'][0] : nil @waste = {} now = Time::now @last_modified = now.rfc1123_date if @cgi.valid?( 'edit' ) then # edit diary @edit = @cgi['edit'][0] elsif @cgi.valid?( 'name' ) # new entry then save data begin @diary = Diary::new( @cgi['name'][0], @cgi['date'][0], @cgi['note'][0] ) 1.upto( 30 ) do |i| if @cgi["item#{i}"][0] and @cgi["item#{i}"][0].length > 0 then item = Item::new( @cgi["item#{i}"][0], @cgi["price#{i}"][0], @cgi["note#{i}"][0] ) @diary.add( item ) end end rescue ArgumentError end end if @diary then @year = @diary.date[0,4] @month = @diary.date[5,2] elsif @edit @year = @edit[0,4] @month = @edit[5,2] elsif /^\d{6}$/ =~ @cgi['date'][0] @year = @cgi['date'][0][0,4] @month = @cgi['date'][0][4,2] elsif /^\d{4}$/ =~ @cgi['date'][0] @year = @cgi['date'][0] @month = '12' else @year = now.year.to_s @month = '12' end db = PStore::new( File::dirname( @cgi.path_translated ) + "/#{@year}.db" ) Lock::lock( File::dirname( @cgi.path_translated ) + "/lock" ) do db.transaction do begin @waste = db['waste']; rescue; end begin @last_modified = db['last_modified']; rescue; end if @diary then key = @diary.date + @diary.name if @diary.item_size > 0 then # saving new data @waste[key] = @diary else # delete the data if exist @waste.delete( key ) end db['waste'] = @waste db['last_modified'] = @last_modified = now.rfc1123_date end end end end def user? @user.empty? ? false : true end def header param = { 'type' => 'text/html', 'charset' => 'EUC-JP', 'Last-Modified' => @last_modified, } if @cgi.valid?( 'name' ) then param['cookie'] = CGI::Cookie::new({ 'name' => 'waste', 'value' => ["#{@cgi['name'][0]}"], 'path' => File::dirname( @cgi.path_info ), 'expires' => Time.now.gmtime + 24*60*60*365 }) end ERuby.noheader = true @cgi.header( param ) end def diary( user, date ) if @waste[date+user] then @waste[date+user] else Diary::new( user, date, '' ) end end def each_year Dir::glob( File::dirname( @cgi.path_translated ) + "/????.db" ).each do |f| yield File::basename( f, '.db' ) end end def each_diary( person = nil, limit = nil ) re = person ? /^#{person}$/ : /.*/ out = 0 @waste.keys.sort.reverse_each do |date| if re =~ @waste[date].name then yield @waste[date] break if not person and limit and (out += 1) > limit end end end def each_month( times = 3 ) if @waste.empty? then last = @month.to_i else last = @waste.keys.sort[-1][5,2].to_i end (last - times + 1).upto( last ) do |m| yield m if m > 0 end end def each_total( month = 0 ) re = (month == 0) ? /^#{@year}/ : /^#{@year}\/#{"%02d" % month}/ users = Hash::new( 0 ) each_diary do |diary| users[diary.name] += diary.total if re =~ diary.date end totals = {} users.each do |name,total| totals[total] = name end totals.keys.sort.reverse_each do |total| yield totals[total], total end end def each_user_total( user = @person ) re = /^#{@year}\/(\d\d)\/\d\d#{user}$/ months = Hash::new( 0 ) each_diary do |diary| months[$1] += diary.total if re =~ (diary.date + diary.name) end months.keys.sort.each do |month| yield month, months[month] end end end