王筝的博客
ruby学习

When you first started coding, errors were probably the last thing you wanted to see.

After all, it’s not a far stretch to associate “error” with “I messed up”.

Hopefully by now you’ve come to appreciate the value of a good error message. Take a look at the two following errors:

one + 3
NameError: undefined local variable or method 'one' for main:Object
one + 3
TypeError: no implicit conversion of Fixnum into String

Both errors were triggered by the same line of code. Each is a totally different error message, though.

The first, NameError, lets you know that one is a variable that hasn’t been defined. To fix this, you’ll have to actually define the variable one.

The second, TypeError, lets you know that you’re trying to add a Fixnum to a String.

Here, there’s a good chance that one is a variable that is already set to a String.

Because these errors were meaningful errors, they allowed you to debug your program painlessly, and ideally also provided a learning opportunity.

Inheriting an Exception

In Ruby, just about everything is an object, including errors. Turns out that NameError andTypeError are simply the class names for these errors!

All errors inherit their functionality from the Exception class.

There are about twenty or so default errors baked into Ruby (read more about them here) and some indicate more serious issues than others.

Issues that deal with problems in your code (as opposed to your computer being on fire) all inherit from StandardError. This includes errors like NameError and TypeError.

This means that if you’ve got a custom error that you’d like to create, like anInvalidPasswordError, you can easily create one by inheriting from StandardError.

class InvalidPasswordError < StandardError
end

It’s really that easy. You don’t even need anything inside this class!

To manually trigger an exception or error, which can be described as “raising” or “throwing” an exception, use the raise keyword.

raise InvalidPasswordError
InvalidPasswordError: InvalidPasswordError

Easy enough. Now you know that you can raise this InvalidPasswordError whenever it’s appropriate.

May I suggest you use it when a password is… invalid?

But this still isn’t super descriptive. Luckily, you can raise an error with an additional message.

raise InvalidPasswordError, "The password you entered is invalid."
InvalidPasswordError: The password you entered is invalid.

That’s more like it. Now it’s explicit what went wrong when this particular exception was thrown.

This is by no means a comprehensive guide to throwing errors and exceptions.

This material could fill a course by itself, and it is a topic we will return to later in this material.

This is, however, the most common way you’ll see exceptions and errors being thrown in the wild.

Exceptional Errors

When other developers are using your code, it’s a good idea to bake meaningful errors right into your public API.

Let’s see how you might be able to use this InvalidPasswordError in the context of the examples from earlier in the lesson.

class InvalidPasswordError < StandardError
end

class Customer
  attr_reader :funds

  def initialize(funds, password)
    @password = password
    @funds = funds
  end

  def withdraw_securely(amount, password)
    if password == @password
      remove_funds(amount)
    else
      raise InvalidPasswordError, "'#{password}' is not the correct password."
    end
  end

  private

  def remove_funds(amount)
    @funds -= amount
  end
end

Now, if the correct password was entered, the funds are removed as expected.

But this time, if the incorrect password is entered, your new InvalidPasswordError is thrown with a useful little message.

 

kim = Customer.new(1000, "coolpassword")
# => #<Customer:0x007faabc8012b8 @password="coolpassword", @funds=1000>
kim.withdraw_securely(200, "coolpassword")
# => 800
kim.withdraw_securely(150, "badpassword")
InvalidPasswordError: 'badpassword' is not the correct password.

That’s so useful!

 

Often, classes will have shared characteristics with other classes.

Rewriting the same methods for each class over and over again can be pretty cumbersome, and not always necessary.

class BankTeller
  def get_paid
    # method stuff...
  end

  def come_to_work
    # method stuff...
  end

  def go_on_vacation!
    # method stuff...
  end

  def do_bank_teller_stuff
    # method stuff...
  end
end

class LoanSpecialist
  def get_paid
    # method stuff...
  end

  def come_to_work
    # method stuff...
  end

  def go_on_vacation!
    # method stuff...
  end

  def do_loan_specialist_stuff
    # method stuff...
  end
end

I mean, just look at these two classes! They’re nearly identical!

The only differences are thedo_bank_teller_stuff and do_loan_specialist_stuff methods.

Both of these classes have characteristics that are shared across all employees.

Being redundant like this works, but will quickly cause problems if you decide that employees should be paid bi-weekly instead of on a fixed day of the month.

You’d have to update the get_paid method for every single employee class! Right now you’ve only got two,

but what if you had 30 different roles in the company. No thank you.

Here, you can use inheritance to share traits across various classes.

class Employee
  def get_paid
    # method stuff...
  end

  def come_to_work
    # method stuff...
  end

  def go_on_vacation!
    # method stuff...
  end
end

class BankTeller < Employee
  def do_bank_teller_stuff
    # method stuff...
  end
end

class LoanSpecialist < Employee
  def do_loan_specialist_stuff
    # method stuff...
  end
end

You can use the < when defining these classes to indicate that LoanSpecialist and BankTellerare the children of Employee.

When this happens, they inherit all the methods of the parent class. You can continue to define new methods for the child class as you normally would.

Note that a child can only have one parent class.

This technique of inheritance is a handy way to reduce code duplication and logically separate out the concerns of your code.

Feeling Protected

We previously mentioned that in addition to the public and private keywords, there is also aprotected keyword.

private and protected are similar in many ways, with one key difference: private methods are not shared through inheritance,

whereas protected methods are shared across all children classes.

 

When calling an instance method like withdraw_securely, the syntax generally looks something like this:

object.method_being_called(arguments)

One would therefore think it’s safe to assume that an instance method is always preceded by a .,

which is in turn preceded by the object that is calling the method.

Why, then, did this code work in the previous example?

 

# from inside the Customer class

def withdraw_securely(amount, password)
  if password == @password
    remove_funds(amount)
  end
end

Isn’t remove_funds also an instance method? Why is it suddenly exempt from following the sameobject.

method_being_called syntax just because it’s inside a method?

This can be compared to spoken language. If you were asking Diego to tell us his name, you might say to him “Diego, tell us your name.”

But if you were asking me to tell you my name, you’d likely say “Tell me your name”.

Yes, you could have said “You, tell me your name,” but that would have been redundant. “You” isimplicit in “Tell me your name”.

Similarly, when you call an instance method from within a class, there is an implicit object being called: itself.

# from inside the Customer class

def withdraw_securely(amount, password)
  if password == @password
    self.remove_funds(amount)
  end
end

An object can refer to itself using the self keyword. Think of it as an object’s way of saying “me” or “I”.

When you call remove_funds from within the Customer class, you’re saying “remove these funds from myself”.

And since Ruby is all about removing any unnecessary syntax, self in this context is implicit, and can be left out entirely.

An instance variable like @password is scoped to a particular instance of a class.

But what if you wanted a variable that was shared across all instances of a class? Almost like… a class variable.

@@check_out_this_cool_class_variable = "boom."

Boom is right.

A class variable’s syntax is twice as cool as an instance variable, because it has two @’s instead of just one.

Let’s see how one might use a class variable.

 

class Employee
  @@bank = "Udacity International Bank"

  def bank
    @@bank
  end
end

Unfortunately, class variables can’t be accessed using attr_accessor, so you’ll need to create your own bank getter method in this case.

Initialize two instances of an employee to see this working.

 

elana = Employee.new
# => #<Employee:0x007fcdb48c19d0>
corey = Employee.new
# => #<Employee:0x00nfbdm132ejd9>
elana.bank
# => "Udacity International Bank"
corey.bank
# => "Udacity International Bank"

Great, now this @@bank class variable is shared across all instances of an Employee class.

But… Why?

Class variables are used considerably less frequently than instance variables.

Unlike the publickeyword, though, there are some practical use cases for class variables. Here are two of those use cases.

class Customer
  attr_reader :id

  @@id = 1
  @@customers = []

  def initialize
    @id = @@id
    @@id += 1
    @@customers << self
  end
end

If you break this apart, you’ll see two class variables, @@id and @@customers.

Every time a new customer is instantiated, an instance variable of @id is set to the value of theclass variable @@id.

Immediately afterwards, the @@id class variable is incremented by one.

 

larry = Customer.new
# => #<Customer:0x007faaba8a6aa8 @id=1>
christine = Customer.new
# => #<Customer:0x007faaba8a6aa8 @id=2>
larry.id
# => 1
christine.id
# => 2

This way, the Customer class can keep track of the total number of customer objects that have been created.

By assigning the class variable to the instance variable @id, you are capturing the current ID number from when that particular customer object was created.

Similarly, @@customers in this case is an Array that holds all the customer objects that have ever been created.

After a new customer is initialized, self, the particular instance currently being used, is pushed into this @@customers Array.

Unfortunately, we’re left without an appropriate interface for accessing @@customers.

It wouldn’t make sense to create a customers instance method, since an Array of customers isn’t really a property of a particular customer.

If only there were something else…

Class variables can be especially powerful when coupled with class methods.

Class methods differ from instance methods in that they are called directly on the class name, like so:

Customer.this_is_a_class_method

These are used when you need to call a method that doesn’t apply to a specific instance of a class.

Say you need a method to retrieve all the customers you’ve created so far. This method could be called all.

Defining a class method works just like defining an instance method, except it must be preceded by self.

class Customer
  attr_reader :id

  @@id = 1
  @@customers = []

  # ...

  def initialize
    @id = @@id
    @@id += 1
    @@customers << self
  end

  def self.all
    @@customers
  end
end

Now, if at any point you’d like to retrieve an Array of all existing customers, you can simply call the class method.

Customer.all
# => [#<Customer:0x007faaba84c148 @id=1>, #<Customer:0x007faaba82fe30 @id=2>]

 

map is another extremely useful Ruby iterator. It helps us map data from one form to another. It will return a new Array containing the result of running the specific block on each element in the collection. Let’s start with a simple example using an Array of numbers:

nums = [1,2,3,4,5]

 

If we wanted a new Array where every item is double the original item in nums:

double_arr = nums.map {|num| num * 2}

 

map will run num*2 on every item in the nums Array, storing the result in a new Array. That new Array looks like this:

[2, 4, 6, 8, 10]
all_subchannels.each do |subchannel|
   new_subchannel = subchannel.clone
   new_subchannel.version = "1.8.0"
   new_subchannel.save
end

 

<%= link_to t('links.list'), showable_paths[@subchannel_item.showable_type.to_sym], target: '_blank', id: 'showable-link' %>

def showable_paths
{ 
   #Filter: cibn_filterlists_path,
   Video: videos_path,
   Subject: cibn_special_subjects_path,
   Star: stars_path
} 
end

 

[27] pry(main)> c = Channel.find 6
=> #<Channel id: 6, title: "会员", cid: "96", pid: "", channel_position: "", data_type: "", filter_id: "", version: "1.2.0">
[28] pry(main)> c.valid?
=> true
[29] pry(main)>
[30] pry(main)>
[31] pry(main)> c.has_filter
=> "1"
[32] pry(main)> c.cid
=> "96"
[33] pry(main)> c.is_default_channel
=> "1"
[34] pry(main)> c.reload
=> #<Channel id: 6, title: "会员", cid: "96", pid: "", channel_position: "", data_type: "", filter_id: "", version: "1.2.0">
[35] pry(main)> c.previous_changes
=> {}
[36] pry(main)> c.title = c.title + 1
TypeError: no implicit conversion of Fixnum into String
from (pry):43:in `+'
[37] pry(main)> c.title = c.title + '1'
=> "会员1"
[38] pry(main)> c.save
=> true
[39] pry(main)> c.previous_changes
=> {"title"=>["会员", "会员1"], "updated_at"=>[Fri, 08 Apr 2016 08:10:30 CST +08:00, 2016-04-08 12:28:20 +0800]}
[40] pry(main)> a, b = [1,2]
=> [1, 2]
[41] pry(main)> a
=> 1
[42] pry(main)> b
=> 2

 

model里的private方法

def update_auto_activate_job
      if active?  # 对已经设置了自动开启时间的视频进行手动开启操作
        if (change = previous_changes[:state]) && change[0] == 0
          if job = Sidekiq::ScheduledSet.new.find_job(auto_activate_job_id)
            job.delete
          end

          update_attributes auto_activated_at: nil, auto_activate_job_id: nil 
        end
      else  # 针对未开启的视频,设置,取消或者更新定时开启时间
        if change = previous_changes[:auto_activated_at]
          original_value, new_value = change

          if original_value.nil?  # 设置定时开启,id是当前model里的id
            job_id = AutoActivateWorker.perform_at new_value, id
            update_attribute :auto_activate_job_id, job_id
          elsif new_value.nil?  # 取消定时开启
            if job = Sidekiq::ScheduledSet.new.find_job(auto_activate_job_id)
              job.delete
            end

            update_attribute :auto_activate_job_id, nil 
          else # 更新定时开启时间
            if job = Sidekiq::ScheduledSet.new.find_job(auto_activate_job_id)
              job.reschedule new_value
            end
          end
        end
      end 
    end 

 

 

http://ruby-doc.org/

http://ruby-doc.org/core-2.3.0/Array.html#method-i-select

[1,2,3,4,5].select { |num|  num.even?  }   #=> [2, 4]

a = %w{ a b c d e f }
a.select { |v| v =~ /[aeiou]/ }  #=> ["a", "e"]

 

      # TODO: 使用 MySQL 的 FIND_IN_SET,必须过滤掉逗号分割符之间的空格
      # http://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_find-in-set
      custom_subjects = SpecialSubject.where('state = ? AND pid != ? ' , 1, '').select do |subject|
        pids = subject.pid.split(',')
        pids.include? pid
      end  

      custom_subjects.each  do |subject|
        if (homepage_position = subject.homepage_position.to_i) && homepage_position != 0
          result_videos[homepage_position - 1] = subject
        end
      end

 

[31] pry(main)> ‘packages’.classify
=> “Package”

[33] pry(main)> ‘packages’.classify.constantize
=> Package

[40] pry(main)> hash = {key: ‘value’}
=> {:key=>”value”}
[41] pry(main)> hash[:key]
=> “value”
[42] pry(main)> hash[‘key’]
=> nil

[44] pry(main)> hash = {key: ‘value’}.with_indifferent_access
=> {“key”=>”value”}

[45] pry(main)> hash[:key]
=> “value”
[46] pry(main)> hash[‘key’]
=> “value”
[47] pry(main)> hash[‘key’]
=> “value”

[49] pry(main)> app
=> #<RecommendApp _id: 539e995277616e7f74000000, created_at: 2014-06-16 07:14:26 UTC, updated_at: 2014-06-16 07:14:26 UTC, type: 1, position: 0, app_id: BSON::ObjectId(‘53970af477616e1878d40100′)>

[50] pry(main)> app.position
=> 0

[51] pry(main)> app.read_attribute :position
=> 0

[52] pry(main)> app.type
=> 1
[53] pry(main)> app.type_human

 

https://github.com/peleteiro/progressbar

gem install progressbar

gem 'progressbar', '~> 0.21.0'
% irb --simple-prompt -r progressbar
>> pbar = ProgressBar.new("test", 100)
=> (ProgressBar: 0/100)
>> 100.times {sleep(0.1); pbar.inc}; pbar.finish
test:          100% |oooooooooooooooooooooooooooooooooooooooo| Time: 00:00:10
=> nil

>> pbar = ProgressBar.new("test", 100)
=> (ProgressBar: 0/100)
>> (1..100).each{|x| sleep(0.1); pbar.set(x)}; pbar.finish
test:           67% |oooooooooooooooooooooooooo              | ETA:  00:00:03

>> ProgressBar.new("test", 100) do |pbar|
>>   100.times { sleep(0.1); pbar.inc }
>> end
test:          100% |oooooooooooooooooooooooooooooooooooooooo| Time: 00:00:10

def download_apk!
require ‘open-uri’
require ‘digest’
require ‘fileutils’

return false if is_dead_download_url?
return false if is_apk_downloaded?

tmp_file = Tempfile.new [package_name, “.apk”]
File.open(tmp_file, ‘wb’) do |file_to_save|
pbar = nil

open(download_url, “rb”, :content_length_proc => lambda {|t|
if t && 0 < t
pbar = ProgressBar.new(“…”, t)
pbar.file_transfer_mode
end
},
:progress_proc => lambda {|s|
pbar.set s if pbar
}) { |file_to_download| file_to_save.write(file_to_download.read) }
end

apk_md5 = Digest::MD5.file(tmp_file).hexdigest
apk_path = File.join Settings.apks_dir, “#{package_name}.#        {apk_md5}.apk”

FileUtils.mkdir_p Settings.apks_dir
FileUtils.mv tmp_file, apk_path

package_info = dump_package_info apk_path
update_attributes package_info.merge(md5: apk_md5)
apk_path
rescue OpenURI::HTTPError, OpenURI::HTTPRedirect
false
end

————————————————————————————-

Tempfile

默认存储路径是 /tmp
A utility class for managing temporary files. When you create a Tempfile object, it will create a temporary file with a unique filename. A Tempfile objects behaves just like a File object, and you can perform all the usual file operations on it: reading data, writing data, changing its permissions, etc. So although this class does not explicitly document all instance methods supported by File, you can in fact call any File instance method on a Tempfile object.
require 'tempfile'

file = Tempfile.new('foo')
file.path      # => A unique filename in the OS's temp directory,
               #    e.g.: "/tmp/foo.24722.0"
               #    This filename contains 'foo' in its basename.
file.write("hello world")
file.rewind
file.read      # => "hello world"
file.close
file.unlink    # deletes the temp file