bell 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/.gitignore +5 -0
 - data/.rvmrc +1 -0
 - data/Gemfile +2 -0
 - data/Gemfile.lock +47 -0
 - data/MIT-LICENSE +20 -0
 - data/README.org +128 -0
 - data/Rakefile +16 -0
 - data/bell.gemspec +30 -0
 - data/bin/bell +5 -0
 - data/features/create_user.feature +16 -0
 - data/features/full_report.feature +84 -0
 - data/features/import_public_contacts.feature +20 -0
 - data/features/import_user_contacts.feature +43 -0
 - data/features/list_all_contacts.feature +19 -0
 - data/features/list_user_contacts.feature +27 -0
 - data/features/list_users.feature +22 -0
 - data/features/remove_user.feature +21 -0
 - data/features/rename_user.feature +25 -0
 - data/features/step_definitions/cli_steps.rb +59 -0
 - data/features/step_definitions/contact_steps.rb +104 -0
 - data/features/step_definitions/database_steps.rb +3 -0
 - data/features/step_definitions/report_steps.rb +17 -0
 - data/features/step_definitions/user_steps.rb +71 -0
 - data/features/support/env.rb +65 -0
 - data/features/user_report.feature +67 -0
 - data/lib/bell/cli.rb +12 -0
 - data/lib/bell/commands/command.rb +58 -0
 - data/lib/bell/commands/contact_command.rb +91 -0
 - data/lib/bell/commands/implosion_command.rb +14 -0
 - data/lib/bell/commands/report_command.rb +69 -0
 - data/lib/bell/commands/user_command.rb +74 -0
 - data/lib/bell/commands.rb +5 -0
 - data/lib/bell/csv_parser.rb +52 -0
 - data/lib/bell/dispatcher.rb +13 -0
 - data/lib/bell/displayable.rb +13 -0
 - data/lib/bell/full_report.rb +145 -0
 - data/lib/bell/handlers/contacts_handler.rb +79 -0
 - data/lib/bell/handlers/implosions_handler.rb +9 -0
 - data/lib/bell/handlers/reports_handler.rb +41 -0
 - data/lib/bell/handlers/users_handler.rb +50 -0
 - data/lib/bell/handlers.rb +4 -0
 - data/lib/bell/message.rb +115 -0
 - data/lib/bell/public_contact.rb +17 -0
 - data/lib/bell/user.rb +16 -0
 - data/lib/bell/user_contact.rb +18 -0
 - data/lib/bell/user_report.rb +88 -0
 - data/lib/bell/util.rb +31 -0
 - data/lib/bell.rb +157 -0
 - data/spec/bell/cli_spec.rb +4 -0
 - data/spec/bell/commands/command_spec.rb +59 -0
 - data/spec/bell/commands/contact_command_spec.rb +112 -0
 - data/spec/bell/commands/implosion_command_spec.rb +31 -0
 - data/spec/bell/commands/report_command_spec.rb +71 -0
 - data/spec/bell/commands/user_command_spec.rb +128 -0
 - data/spec/bell/csv_parser_spec.rb +167 -0
 - data/spec/bell/dispatcher_spec.rb +4 -0
 - data/spec/bell/full_report_spec.rb +4 -0
 - data/spec/bell/handlers/contacts_handler_spec.rb +160 -0
 - data/spec/bell/handlers/implosions_handler_spec.rb +11 -0
 - data/spec/bell/handlers/reports_handler_spec.rb +183 -0
 - data/spec/bell/handlers/users_handler_spec.rb +94 -0
 - data/spec/bell/public_contact_spec.rb +4 -0
 - data/spec/bell/user_contact_spec.rb +4 -0
 - data/spec/bell/user_report_spec.rb +4 -0
 - data/spec/bell/user_spec.rb +4 -0
 - data/spec/spec_helper.rb +21 -0
 - metadata +230 -0
 
| 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Bell
         
     | 
| 
      
 2 
     | 
    
         
            +
              module CSVParser
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend self
         
     | 
| 
      
 4 
     | 
    
         
            +
                include Displayable, Util::String
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def parse_contacts(options)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  contact_rows = CSV.read(options[:path])
         
     | 
| 
      
 8 
     | 
    
         
            +
                rescue Errno::ENOENT
         
     | 
| 
      
 9 
     | 
    
         
            +
                  display(Message.no_such_file_or_directory(options[:path]))
         
     | 
| 
      
 10 
     | 
    
         
            +
                rescue Errno::EISDIR
         
     | 
| 
      
 11 
     | 
    
         
            +
                  display(Message.path_is_a_directory(options[:path]))
         
     | 
| 
      
 12 
     | 
    
         
            +
                rescue CSV::MalformedCSVError
         
     | 
| 
      
 13 
     | 
    
         
            +
                  display(Message.invalid_contacts_file(options[:path]))
         
     | 
| 
      
 14 
     | 
    
         
            +
                else
         
     | 
| 
      
 15 
     | 
    
         
            +
                  user = User.find(:name => options[:user][:name]) if options[:user]
         
     | 
| 
      
 16 
     | 
    
         
            +
                  valid_contacts = []
         
     | 
| 
      
 17 
     | 
    
         
            +
                  contact_rows.each_with_index do |row, index|
         
     | 
| 
      
 18 
     | 
    
         
            +
                    if row.size < 2
         
     | 
| 
      
 19 
     | 
    
         
            +
                      display(Message.row_with_few_columns(row, index + 1))
         
     | 
| 
      
 20 
     | 
    
         
            +
                    elsif row.size > 2
         
     | 
| 
      
 21 
     | 
    
         
            +
                      display(Message.row_with_extra_columns(row, index + 1))
         
     | 
| 
      
 22 
     | 
    
         
            +
                    else
         
     | 
| 
      
 23 
     | 
    
         
            +
                      contact = if options[:user]
         
     | 
| 
      
 24 
     | 
    
         
            +
                        UserContact.new(:name => sanitize(row.first),
         
     | 
| 
      
 25 
     | 
    
         
            +
                                    :number => row.last,
         
     | 
| 
      
 26 
     | 
    
         
            +
                                    :user_id => user.id)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      elsif options[:public]
         
     | 
| 
      
 28 
     | 
    
         
            +
                        PublicContact.new(:name => sanitize(row.first), :number => row.last)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      end
         
     | 
| 
      
 30 
     | 
    
         
            +
                      if contact
         
     | 
| 
      
 31 
     | 
    
         
            +
                        if contact.valid?
         
     | 
| 
      
 32 
     | 
    
         
            +
                          valid_contacts << contact
         
     | 
| 
      
 33 
     | 
    
         
            +
                        else
         
     | 
| 
      
 34 
     | 
    
         
            +
                          display(Message.
         
     | 
| 
      
 35 
     | 
    
         
            +
                                    formatted_contact_errors(contact,
         
     | 
| 
      
 36 
     | 
    
         
            +
                                                             :line_number => index + 1))
         
     | 
| 
      
 37 
     | 
    
         
            +
                        end
         
     | 
| 
      
 38 
     | 
    
         
            +
                      end
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  if contact_rows.size > 0
         
     | 
| 
      
 42 
     | 
    
         
            +
                    if contact_rows.size == valid_contacts.size
         
     | 
| 
      
 43 
     | 
    
         
            +
                      valid_contacts
         
     | 
| 
      
 44 
     | 
    
         
            +
                    else
         
     | 
| 
      
 45 
     | 
    
         
            +
                      raise InvalidContacts
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  else
         
     | 
| 
      
 48 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,145 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Bell
         
     | 
| 
      
 4 
     | 
    
         
            +
              class FullReport
         
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(phone_bill)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @phone_bill = phone_bill
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 10 
     | 
    
         
            +
                  "[resumo]
         
     | 
| 
      
 11 
     | 
    
         
            +
                   #{formatted_total}
         
     | 
| 
      
 12 
     | 
    
         
            +
                   #{formatted_known_total}
         
     | 
| 
      
 13 
     | 
    
         
            +
                   #{formatted_public_total}
         
     | 
| 
      
 14 
     | 
    
         
            +
                   #{formatted_unknown_total}
         
     | 
| 
      
 15 
     | 
    
         
            +
                   #{formatted_fees_total}
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                   [usuários]
         
     | 
| 
      
 18 
     | 
    
         
            +
                   #{users.map { |user| %{#{formatted_user(user)}} }.join("\n")}
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                   [ligações públicas]
         
     | 
| 
      
 21 
     | 
    
         
            +
                   #{public_calls.map do |hash|
         
     | 
| 
      
 22 
     | 
    
         
            +
                     %{#{formatted_call(hash[:call], :contact => hash[:contact])}}
         
     | 
| 
      
 23 
     | 
    
         
            +
                   end.join("\n")}
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                   [ligações desconhecidas]
         
     | 
| 
      
 26 
     | 
    
         
            +
                   #{unknown_calls.map do |call|
         
     | 
| 
      
 27 
     | 
    
         
            +
                     %{#{formatted_call(call)}}
         
     | 
| 
      
 28 
     | 
    
         
            +
                   end.join("\n")}".gsub(/^\ {7,}/, '')
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                private
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def formatted_total
         
     | 
| 
      
 34 
     | 
    
         
            +
                  %{#{"Total:"}}.ljust(25) << sprintf("%.2f", total)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def formatted_known_total
         
     | 
| 
      
 38 
     | 
    
         
            +
                  %{#{"Usuários:"}}.ljust(25) << sprintf("%.2f", known_total)
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def formatted_public_total
         
     | 
| 
      
 42 
     | 
    
         
            +
                  %{#{"Públicas:"}}.ljust(25) << sprintf("%.2f", public_total)
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def formatted_unknown_total
         
     | 
| 
      
 46 
     | 
    
         
            +
                  %{#{"Desconhecidas:"}}.ljust(25) << sprintf("%.2f", unknown_total)
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def formatted_fees_total
         
     | 
| 
      
 50 
     | 
    
         
            +
                  %{#{"Taxas:"}}.ljust(25) << sprintf("%.2f", fees_total)
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def formatted_user(user)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  %{#{user[:name]}}.ljust(25) << sprintf("%.2f", user[:total])
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def formatted_call(call, options = {})
         
     | 
| 
      
 58 
     | 
    
         
            +
                  contact = call.number_called << if options[:contact]
         
     | 
| 
      
 59 
     | 
    
         
            +
                    " (#{options[:contact].name})"
         
     | 
| 
      
 60 
     | 
    
         
            +
                  else
         
     | 
| 
      
 61 
     | 
    
         
            +
                    ''
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  time = call.start_time.to_s
         
     | 
| 
      
 64 
     | 
    
         
            +
                  date = call.date[0, 8].to_s
         
     | 
| 
      
 65 
     | 
    
         
            +
                  cost = sprintf("%.2f", call.cost)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  contact.ljust(30) << if time.empty?
         
     | 
| 
      
 67 
     | 
    
         
            +
                    date.empty? ? ' ' * 20 : ' ' * 10 << "#{date}  "
         
     | 
| 
      
 68 
     | 
    
         
            +
                  else
         
     | 
| 
      
 69 
     | 
    
         
            +
                    date.empty? ? ' ' * 12 << "#{time}" : "#{time}  #{date}  "
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end << cost
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                def calls
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @calls ||= @phone_bill.calls
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                def fees
         
     | 
| 
      
 78 
     | 
    
         
            +
                  @fees ||= @phone_bill.fees
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                def known_calls
         
     | 
| 
      
 82 
     | 
    
         
            +
                  @known_calls ||= calls.inject([]) do |memo, call|
         
     | 
| 
      
 83 
     | 
    
         
            +
                    UserContact.find(:number => call.number_called) ? memo << call : memo
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def public_calls
         
     | 
| 
      
 88 
     | 
    
         
            +
                  @public_calls ||= calls.inject([]) do |memo, call|
         
     | 
| 
      
 89 
     | 
    
         
            +
                    if contact = PublicContact.find(:number => call.number_called)
         
     | 
| 
      
 90 
     | 
    
         
            +
                      memo << { :call => call, :contact => contact }
         
     | 
| 
      
 91 
     | 
    
         
            +
                    else
         
     | 
| 
      
 92 
     | 
    
         
            +
                      memo
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                def unknown_calls
         
     | 
| 
      
 98 
     | 
    
         
            +
                  calls - (public_calls.map { |c| c[:call] } + known_calls)
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                def fees_total
         
     | 
| 
      
 102 
     | 
    
         
            +
                  @fees_total ||= fees.inject(0) { |memo, call| memo += call.cost.to_f }
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                def total
         
     | 
| 
      
 106 
     | 
    
         
            +
                  @total ||= @phone_bill.total
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                def known_total
         
     | 
| 
      
 110 
     | 
    
         
            +
                  @known_total ||= known_calls.inject(0) do |memo, call|
         
     | 
| 
      
 111 
     | 
    
         
            +
                    memo += call.cost.to_f
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                def public_total
         
     | 
| 
      
 116 
     | 
    
         
            +
                  @public_total ||= public_calls.inject(0) do |memo, hash|
         
     | 
| 
      
 117 
     | 
    
         
            +
                    memo += hash[:call].cost.to_f
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                def unknown_total
         
     | 
| 
      
 122 
     | 
    
         
            +
                  @unknown_total ||= unknown_calls.inject(0) do |memo, call|
         
     | 
| 
      
 123 
     | 
    
         
            +
                    memo += call.cost.to_f
         
     | 
| 
      
 124 
     | 
    
         
            +
                  end
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                def user_total(user_name)
         
     | 
| 
      
 128 
     | 
    
         
            +
                  user = User.find(:name => user_name)
         
     | 
| 
      
 129 
     | 
    
         
            +
                  subtotal = calls.inject(0) do |total, call|
         
     | 
| 
      
 130 
     | 
    
         
            +
                    if UserContact.find(:number => call.number_called, :user_id => user.id)
         
     | 
| 
      
 131 
     | 
    
         
            +
                      total += call.cost.to_f
         
     | 
| 
      
 132 
     | 
    
         
            +
                    else
         
     | 
| 
      
 133 
     | 
    
         
            +
                      total
         
     | 
| 
      
 134 
     | 
    
         
            +
                    end
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
                  subtotal + (unknown_total + fees_total) / User.all.size
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                def users
         
     | 
| 
      
 140 
     | 
    
         
            +
                  User.all.inject([]) do |users_hash, user|
         
     | 
| 
      
 141 
     | 
    
         
            +
                    users_hash << { :name => user.name, :total => user_total(user.name) }
         
     | 
| 
      
 142 
     | 
    
         
            +
                  end
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
              end
         
     | 
| 
      
 145 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,79 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Bell
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Handlers
         
     | 
| 
      
 5 
     | 
    
         
            +
                class ContactsHandler
         
     | 
| 
      
 6 
     | 
    
         
            +
                  include Displayable
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def self.list(params = {})
         
     | 
| 
      
 9 
     | 
    
         
            +
                    if params.empty?
         
     | 
| 
      
 10 
     | 
    
         
            +
                      if UserContact.empty?
         
     | 
| 
      
 11 
     | 
    
         
            +
                        display Message.no_contacts_created
         
     | 
| 
      
 12 
     | 
    
         
            +
                      else
         
     | 
| 
      
 13 
     | 
    
         
            +
                        display formatted_contact_list(UserContact.all + PublicContact.all)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      end
         
     | 
| 
      
 15 
     | 
    
         
            +
                    else
         
     | 
| 
      
 16 
     | 
    
         
            +
                      if user = User.find(:name => params[:user][:name])
         
     | 
| 
      
 17 
     | 
    
         
            +
                        if user.contacts.empty?
         
     | 
| 
      
 18 
     | 
    
         
            +
                          display Message.contact_list_empty(user.name)
         
     | 
| 
      
 19 
     | 
    
         
            +
                        else
         
     | 
| 
      
 20 
     | 
    
         
            +
                          display formatted_contact_list(user.contacts,
         
     | 
| 
      
 21 
     | 
    
         
            +
                                                         :user_contacts => true,
         
     | 
| 
      
 22 
     | 
    
         
            +
                                                         :csv => params[:csv])
         
     | 
| 
      
 23 
     | 
    
         
            +
                        end
         
     | 
| 
      
 24 
     | 
    
         
            +
                      else
         
     | 
| 
      
 25 
     | 
    
         
            +
                        display Message.user_does_not_exist(params[:user][:name])
         
     | 
| 
      
 26 
     | 
    
         
            +
                      end
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def self.import(params)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    if params[:user]
         
     | 
| 
      
 32 
     | 
    
         
            +
                      user = User.find(:name => params[:user][:name])
         
     | 
| 
      
 33 
     | 
    
         
            +
                      if user
         
     | 
| 
      
 34 
     | 
    
         
            +
                        if contacts = CSVParser.parse_contacts(params)
         
     | 
| 
      
 35 
     | 
    
         
            +
                          contacts.each do |contact|
         
     | 
| 
      
 36 
     | 
    
         
            +
                            contact.save && display(Message.contact_created(contact))
         
     | 
| 
      
 37 
     | 
    
         
            +
                          end
         
     | 
| 
      
 38 
     | 
    
         
            +
                        end
         
     | 
| 
      
 39 
     | 
    
         
            +
                      else
         
     | 
| 
      
 40 
     | 
    
         
            +
                        display(Message.user_does_not_exist(params[:user][:name]))
         
     | 
| 
      
 41 
     | 
    
         
            +
                      end
         
     | 
| 
      
 42 
     | 
    
         
            +
                    elsif params[:public]
         
     | 
| 
      
 43 
     | 
    
         
            +
                      if contacts = CSVParser.parse_contacts(params)
         
     | 
| 
      
 44 
     | 
    
         
            +
                        contacts.each do |contact|
         
     | 
| 
      
 45 
     | 
    
         
            +
                          contact.save &&
         
     | 
| 
      
 46 
     | 
    
         
            +
                            display(Message.contact_created(contact, :public => true))
         
     | 
| 
      
 47 
     | 
    
         
            +
                        end
         
     | 
| 
      
 48 
     | 
    
         
            +
                      end
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  rescue InvalidContacts
         
     | 
| 
      
 51 
     | 
    
         
            +
                    display(Message.no_contacts_created)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  private
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  def self.text_contact_list(contacts, options)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    contacts.inject('') do |list, contact|
         
     | 
| 
      
 58 
     | 
    
         
            +
                      list << "#{contact.name} (#{contact.number})"
         
     | 
| 
      
 59 
     | 
    
         
            +
                      if contact.is_a?(PublicContact)
         
     | 
| 
      
 60 
     | 
    
         
            +
                        list << " - público"
         
     | 
| 
      
 61 
     | 
    
         
            +
                      else
         
     | 
| 
      
 62 
     | 
    
         
            +
                        list << " - #{contact.user.name}" unless options[:user_contacts]
         
     | 
| 
      
 63 
     | 
    
         
            +
                      end
         
     | 
| 
      
 64 
     | 
    
         
            +
                      list << "\n"
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  def self.csv_contact_list(contacts)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    contacts.inject('') do |list, contact|
         
     | 
| 
      
 70 
     | 
    
         
            +
                      list << "\"#{contact.name}\",#{contact.number}\n"
         
     | 
| 
      
 71 
     | 
    
         
            +
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  def self.formatted_contact_list(contacts, options = {})
         
     | 
| 
      
 75 
     | 
    
         
            +
                    options[:csv] ? csv_contact_list(contacts) : text_contact_list(contacts, options)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,41 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Bell::Handlers
         
     | 
| 
      
 2 
     | 
    
         
            +
              class ReportsHandler
         
     | 
| 
      
 3 
     | 
    
         
            +
                include Bell::Displayable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def self.full_report(params = {})
         
     | 
| 
      
 6 
     | 
    
         
            +
                  phone_bill = Embratel::PhoneBill.new(params[:path])
         
     | 
| 
      
 7 
     | 
    
         
            +
                rescue Errno::ENOENT
         
     | 
| 
      
 8 
     | 
    
         
            +
                  display Bell::Message.no_such_file_or_directory(params[:path])
         
     | 
| 
      
 9 
     | 
    
         
            +
                rescue Errno::EISDIR
         
     | 
| 
      
 10 
     | 
    
         
            +
                  display Bell::Message.path_is_a_directory(params[:path])
         
     | 
| 
      
 11 
     | 
    
         
            +
                rescue Bell::CSV::MalformedCSVError
         
     | 
| 
      
 12 
     | 
    
         
            +
                  display Bell::Message.malformed_csv_file(params[:path])
         
     | 
| 
      
 13 
     | 
    
         
            +
                rescue Embratel::NonCSVFileError
         
     | 
| 
      
 14 
     | 
    
         
            +
                  display Bell::Message.non_csv_file(params[:path])
         
     | 
| 
      
 15 
     | 
    
         
            +
                rescue Embratel::InvalidRowsError
         
     | 
| 
      
 16 
     | 
    
         
            +
                  display Bell::Message.invalid_rows(params[:path], $!.message)
         
     | 
| 
      
 17 
     | 
    
         
            +
                else
         
     | 
| 
      
 18 
     | 
    
         
            +
                  display Bell::FullReport.new(phone_bill).to_s
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def self.user_report(params = {})
         
     | 
| 
      
 22 
     | 
    
         
            +
                  phone_bill = Embratel::PhoneBill.new(params[:path])
         
     | 
| 
      
 23 
     | 
    
         
            +
                rescue Errno::ENOENT
         
     | 
| 
      
 24 
     | 
    
         
            +
                  display Bell::Message.no_such_file_or_directory(params[:path])
         
     | 
| 
      
 25 
     | 
    
         
            +
                rescue Errno::EISDIR
         
     | 
| 
      
 26 
     | 
    
         
            +
                  display Bell::Message.path_is_a_directory(params[:path])
         
     | 
| 
      
 27 
     | 
    
         
            +
                rescue Bell::CSV::MalformedCSVError
         
     | 
| 
      
 28 
     | 
    
         
            +
                  display Bell::Message.malformed_csv_file(params[:path])
         
     | 
| 
      
 29 
     | 
    
         
            +
                rescue Embratel::NonCSVFileError
         
     | 
| 
      
 30 
     | 
    
         
            +
                  display Bell::Message.non_csv_file(params[:path])
         
     | 
| 
      
 31 
     | 
    
         
            +
                rescue Embratel::InvalidRowsError
         
     | 
| 
      
 32 
     | 
    
         
            +
                  display Bell::Message.invalid_rows(params[:path], $!.message)
         
     | 
| 
      
 33 
     | 
    
         
            +
                else
         
     | 
| 
      
 34 
     | 
    
         
            +
                  if Bell::User.find(:name => params[:user][:name])
         
     | 
| 
      
 35 
     | 
    
         
            +
                    display Bell::UserReport.new(phone_bill, params[:user][:name]).to_s
         
     | 
| 
      
 36 
     | 
    
         
            +
                  else
         
     | 
| 
      
 37 
     | 
    
         
            +
                    display Bell::Message.user_does_not_exist(params[:user][:name])
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Bell::Handlers
         
     | 
| 
      
 2 
     | 
    
         
            +
              class UsersHandler
         
     | 
| 
      
 3 
     | 
    
         
            +
                include Bell::Displayable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def self.create(params = {})
         
     | 
| 
      
 6 
     | 
    
         
            +
                  if Bell::User.find(:name => params[:user][:name])
         
     | 
| 
      
 7 
     | 
    
         
            +
                    display Bell::Message.user_already_exists(params[:user][:name])
         
     | 
| 
      
 8 
     | 
    
         
            +
                  else
         
     | 
| 
      
 9 
     | 
    
         
            +
                    Bell::User.create(:name => params[:user][:name])
         
     | 
| 
      
 10 
     | 
    
         
            +
                    display Bell::Message.user_created(params[:user][:name])
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def self.rename(params = {})
         
     | 
| 
      
 15 
     | 
    
         
            +
                  if user = Bell::User.find(:name => params[:user][:source_name])
         
     | 
| 
      
 16 
     | 
    
         
            +
                    if Bell::User.find(:name => params[:user][:target_name])
         
     | 
| 
      
 17 
     | 
    
         
            +
                      display Bell::Message.user_already_exists(params[:user][:target_name])
         
     | 
| 
      
 18 
     | 
    
         
            +
                    else
         
     | 
| 
      
 19 
     | 
    
         
            +
                      user.name = params[:user][:target_name]
         
     | 
| 
      
 20 
     | 
    
         
            +
                      user.save
         
     | 
| 
      
 21 
     | 
    
         
            +
                      display Bell::Message.user_renamed(params[:user])
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  else
         
     | 
| 
      
 24 
     | 
    
         
            +
                    display Bell::Message.user_does_not_exist(params[:user][:source_name])
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def self.list(params = {})
         
     | 
| 
      
 29 
     | 
    
         
            +
                  if Bell::User.empty?
         
     | 
| 
      
 30 
     | 
    
         
            +
                    display Bell::Message.no_created_users
         
     | 
| 
      
 31 
     | 
    
         
            +
                  else
         
     | 
| 
      
 32 
     | 
    
         
            +
                    display formatted_user_list
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def self.remove(params = {})
         
     | 
| 
      
 37 
     | 
    
         
            +
                  if user = Bell::User.find(:name => params[:user][:name])
         
     | 
| 
      
 38 
     | 
    
         
            +
                    user.destroy
         
     | 
| 
      
 39 
     | 
    
         
            +
                    display Bell::Message.user_removed(params[:user][:name])
         
     | 
| 
      
 40 
     | 
    
         
            +
                  else
         
     | 
| 
      
 41 
     | 
    
         
            +
                    display Bell::Message.user_does_not_exist(params[:user][:name])
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                private
         
     | 
| 
      
 46 
     | 
    
         
            +
                def self.formatted_user_list
         
     | 
| 
      
 47 
     | 
    
         
            +
                  Bell::User.all.map(&:name).join("\n")
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,4 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'handlers', 'users_handler')
         
     | 
| 
      
 2 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'handlers', 'contacts_handler')
         
     | 
| 
      
 3 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'handlers', 'reports_handler')
         
     | 
| 
      
 4 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), 'handlers', 'implosions_handler')
         
     | 
    
        data/lib/bell/message.rb
    ADDED
    
    | 
         @@ -0,0 +1,115 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Bell
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Message
         
     | 
| 
      
 5 
     | 
    
         
            +
                extend self
         
     | 
| 
      
 6 
     | 
    
         
            +
                include Util
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def no_created_users
         
     | 
| 
      
 9 
     | 
    
         
            +
                  "Nenhum usuário criado."
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def user_created(user_name)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  "Usuário '#{user_name}' criado."
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def user_already_exists(user_name)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  "erro: o usuário '#{user_name}' já existe."
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def user_does_not_exist(user_name)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  "erro: o usuário '#{user_name}' não existe."
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def user_removed(user_name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  "Usuário '#{user_name}' removido."
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def user_renamed(user)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  "Usuário '#{user[:source_name]}' renomeado para '#{user[:target_name]}'."
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def no_contacts_created
         
     | 
| 
      
 33 
     | 
    
         
            +
                  "Nenhum contato criado."
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def contact_list_empty(user_name)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  "A lista de contatos do usuário '#{user_name}' está vazia."
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def contact_created(contact, options = {})
         
     | 
| 
      
 41 
     | 
    
         
            +
                  "'#{contact.name} (#{contact.number})' adicionado à lista de " <<
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if options[:public]
         
     | 
| 
      
 43 
     | 
    
         
            +
                    "contatos públicos."
         
     | 
| 
      
 44 
     | 
    
         
            +
                  else
         
     | 
| 
      
 45 
     | 
    
         
            +
                    "contatos do usuário '#{contact.user.name}'."
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def contact_removed(contact_name)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  "Contato '#{contact_name}' removido."
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def contact_name_taken(contact_name)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  contact = UserContact.find(:name => contact_name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  "erro: este nome já é usado pelo contato '#{contact.name} (#{contact.number})' do usuário '#{contact.user.name}'.\nCrie um contato com nome diferente." if contact
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def contact_number_taken(contact_number)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  contact = UserContact.find(:number => contact_number)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  "erro: este número já é usado pelo contato '#{contact.name} (#{contact.number})' do usuário '#{contact.user.name}'.\nCrie um contato com número diferente." if contact
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                def contact_number_bad_format(contact_number)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  "erro: '#{contact_number}' não é um número de telefone válido.\nVeja 'bell --help' para saber mais sobre o formato aceito."
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                def no_such_file_or_directory(path)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  "erro: o arquivo/diretório '#{path}' não existe."
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def path_is_a_directory(path)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  "erro: '#{path}' é um diretório."
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def malformed_csv_file(path)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  "erro: '#{path}' é um arquivo CSV danificado."
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def non_csv_file(path)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  "erro: '#{path}' não é um arquivo CSV."
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                def invalid_rows(path, message)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  lines = message.match(/(?:\d+, )*(?:\d+)$/).to_a
         
     | 
| 
      
 85 
     | 
    
         
            +
                  "erro: '#{path}' tem " <<
         
     | 
| 
      
 86 
     | 
    
         
            +
                    lines.size == 1 ? 'erro na linha ' : 'erros nas linhas ' <<
         
     | 
| 
      
 87 
     | 
    
         
            +
                    lines.join(', ')
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                def invalid_contacts_file(path)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  "erro: '#{path}' não é um arquivo de contatos válido."
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                def row_with_extra_columns(row, line_number)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  "erro: a linha #{line_number}: '#{row.to_s}' tem mais de duas colunas.\nCada linha deve ter duas colunas, sendo a primeira o nome e a segunda o telefone do contato."
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def row_with_few_columns(row, line_number)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  "erro: a linha #{line_number}: '#{row.to_s}' tem mais de duas colunas.\nCada linha deve ter duas colunas, sendo a primeira o nome e a segunda o telefone do contato."
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                def row_with_short_number(row, line_number)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  "erro: o número de telefone #{row.to_s} na linha '#{line_number}' é muito curto.\nNúmeros de telefone devem ter 10 dígitos, sendo os 2 primeiros o DDD."
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                def formatted_contact_errors(contact, options = {})
         
     | 
| 
      
 107 
     | 
    
         
            +
                  contact_error = contact.errors.first.last.first
         
     | 
| 
      
 108 
     | 
    
         
            +
                  if options[:line_number]
         
     | 
| 
      
 109 
     | 
    
         
            +
                    contact_error.split("\n").first.insert(4, " na linha #{options[:line_number]}")
         
     | 
| 
      
 110 
     | 
    
         
            +
                  else
         
     | 
| 
      
 111 
     | 
    
         
            +
                    contact_error
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
              end
         
     | 
| 
      
 115 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Bell
         
     | 
| 
      
 2 
     | 
    
         
            +
              class PublicContact < Sequel::Model
         
     | 
| 
      
 3 
     | 
    
         
            +
                plugin :validation_helpers
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def validate
         
     | 
| 
      
 6 
     | 
    
         
            +
                  super
         
     | 
| 
      
 7 
     | 
    
         
            +
                  validates_unique(:number,
         
     | 
| 
      
 8 
     | 
    
         
            +
                                   :message => Message.contact_number_taken(number))
         
     | 
| 
      
 9 
     | 
    
         
            +
                  validates_format(/^\d{10}$/,
         
     | 
| 
      
 10 
     | 
    
         
            +
                                   :number,
         
     | 
| 
      
 11 
     | 
    
         
            +
                                   :message => Message.contact_number_bad_format(number))
         
     | 
| 
      
 12 
     | 
    
         
            +
                  if UserContact.find(:number => number)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    errors.add(:number, Message.contact_number_taken(number))
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/bell/user.rb
    ADDED
    
    | 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Bell
         
     | 
| 
      
 2 
     | 
    
         
            +
              class User < Sequel::Model
         
     | 
| 
      
 3 
     | 
    
         
            +
                one_to_many(:contacts, :class => UserContact)
         
     | 
| 
      
 4 
     | 
    
         
            +
                plugin(:validation_helpers)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def after_destroy
         
     | 
| 
      
 7 
     | 
    
         
            +
                  super
         
     | 
| 
      
 8 
     | 
    
         
            +
                  contacts.each(&:delete)
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def validate
         
     | 
| 
      
 12 
     | 
    
         
            +
                  super
         
     | 
| 
      
 13 
     | 
    
         
            +
                  validates_unique(:name)
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Bell
         
     | 
| 
      
 2 
     | 
    
         
            +
              class UserContact < Sequel::Model
         
     | 
| 
      
 3 
     | 
    
         
            +
                many_to_one(:user)
         
     | 
| 
      
 4 
     | 
    
         
            +
                plugin(:validation_helpers)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def validate
         
     | 
| 
      
 7 
     | 
    
         
            +
                  super
         
     | 
| 
      
 8 
     | 
    
         
            +
                  validates_unique(:number,
         
     | 
| 
      
 9 
     | 
    
         
            +
                                   :message => Message.contact_number_taken(number))
         
     | 
| 
      
 10 
     | 
    
         
            +
                  validates_format(/^\d{10}$/,
         
     | 
| 
      
 11 
     | 
    
         
            +
                                   :number,
         
     | 
| 
      
 12 
     | 
    
         
            +
                                   :message => Message.contact_number_bad_format(number))
         
     | 
| 
      
 13 
     | 
    
         
            +
                  if PublicContact.find(:number => number)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    errors.add(:number, Message.contact_number_taken(number))
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,88 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Bell
         
     | 
| 
      
 4 
     | 
    
         
            +
              class UserReport
         
     | 
| 
      
 5 
     | 
    
         
            +
                include Bell::Util::String
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(phone_bill, user_name)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @phone_bill = phone_bill
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @user_name = user_name
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def user
         
     | 
| 
      
 13 
     | 
    
         
            +
                  Bell::User.find(:name => @user_name)
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def calls
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @phone_bill.calls.inject([]) do |calls, call|
         
     | 
| 
      
 18 
     | 
    
         
            +
                    if Bell::UserContact.find(:user_id => user.id, :number => call.number_called)
         
     | 
| 
      
 19 
     | 
    
         
            +
                      calls << call
         
     | 
| 
      
 20 
     | 
    
         
            +
                    else
         
     | 
| 
      
 21 
     | 
    
         
            +
                      calls
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def total
         
     | 
| 
      
 27 
     | 
    
         
            +
                  calls.inject(0) { |total, call| total += call.cost.to_f }
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 31 
     | 
    
         
            +
                  "#{formatted_header}\n#{formatted_contact_calls}\n\n#{formatted_total}"
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                private
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def formatted_header
         
     | 
| 
      
 37 
     | 
    
         
            +
                  "Data".ljust(15) <<
         
     | 
| 
      
 38 
     | 
    
         
            +
                  "Contato".ljust(20) <<
         
     | 
| 
      
 39 
     | 
    
         
            +
                  "Número".ljust(15) <<
         
     | 
| 
      
 40 
     | 
    
         
            +
                  "Horário".ljust(20) <<
         
     | 
| 
      
 41 
     | 
    
         
            +
                  "Custo"
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def contact_calls
         
     | 
| 
      
 45 
     | 
    
         
            +
                  calls.inject([]) do |contact_calls, call|
         
     | 
| 
      
 46 
     | 
    
         
            +
                    if contact = Bell::UserContact.find(:number => call.number_called)
         
     | 
| 
      
 47 
     | 
    
         
            +
                      contact_calls << { :contact => contact, :call => call }
         
     | 
| 
      
 48 
     | 
    
         
            +
                    else
         
     | 
| 
      
 49 
     | 
    
         
            +
                      contact_calls
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def formatted_total
         
     | 
| 
      
 55 
     | 
    
         
            +
                  "Total: R$ #{sprintf("%.2f", total)}"
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def formatted_contact_name(contact_name)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  if contact_name.size > 20
         
     | 
| 
      
 60 
     | 
    
         
            +
                    shortened_contact_name = contact_name[0, 15].rstrip.concat('...')
         
     | 
| 
      
 61 
     | 
    
         
            +
                  else
         
     | 
| 
      
 62 
     | 
    
         
            +
                    shortened_contact_name = contact_name
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  multibyte_characters = shortened_contact_name.size -
         
     | 
| 
      
 66 
     | 
    
         
            +
                                           multibyte_length(shortened_contact_name)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  formatted_contact_name = shortened_contact_name.
         
     | 
| 
      
 68 
     | 
    
         
            +
                                             ljust(20 + multibyte_characters)
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def formatted_contact_call(contact_call)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  contact = contact_call[:contact]
         
     | 
| 
      
 73 
     | 
    
         
            +
                  call = contact_call[:call]
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  call.date[0, 8].ljust(15) <<
         
     | 
| 
      
 77 
     | 
    
         
            +
                  formatted_contact_name(contact.name) <<
         
     | 
| 
      
 78 
     | 
    
         
            +
                  call.number_called.ljust(15) <<
         
     | 
| 
      
 79 
     | 
    
         
            +
                  call.start_time.ljust(20) <<
         
     | 
| 
      
 80 
     | 
    
         
            +
                  sprintf("%.2f", call.cost)
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                def formatted_contact_calls
         
     | 
| 
      
 84 
     | 
    
         
            +
                  contact_calls.
         
     | 
| 
      
 85 
     | 
    
         
            +
                    map { |contact_call| formatted_contact_call(contact_call) }.join("\n")
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
            end
         
     |