William's Blog with Octopress

Octopress is A blogging framework for hackers.

Omniauth-github使用示例

| Comments

这篇文档简单记录一下使用omniauth-github的过程

这里有个能跑的代码可以clone看看效果omniauth-github-demo

首先新建项目:

1
rails new omniauth-github-demo

为了让示例看起来不是很难看,我会把bootstrap也进来:

  • 在Gemfile里加上bootstrap-sass然后运行bundle
  • app/assets/javascripts/application.js加上//= require bootstrap
  • app/assets/stylesheets/application.css加上*= require bootstrap

下一步把omniauth-githubGem加进来: – 在Gemfile里加上omniauth-github并执行bundle – 新建文件config/initializers/omniauth.rb包含以下内容

1
2
3
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET']
end

关于ENV['GITHUB_KEY']ENV['GITHUB_SECRET'],需要在github上新建一个application,得到的Client IDClient Secret就是对应的这两个东西,可以把他们写到~/.bashrc里面

1
2
export GITHUB_KEY='your client id'
export GITHUB_SECRET='your client secret'

别忘了source这个文件或重新打开一个终端让显示生效

还要注意你新建application的时候The full URL to your application's homepageYour application's callback URL对应的端口需要和你的服务对应,比如我这里测试用rails开的3000端口,那我就可以填http://localhost:3000/auth/githubhttp://localhost:3000/auth/github/callback

下面我们需要在页面上加一个通过github登录的link,我直接写到layout里并加上一些div让bootstrap起作用

app/views/layouts/application.html.erb
1
2
3
4
5
6
7
8
9
10
11
<body>
  <div class="navbar navbar-fixed-top">
    <div class="navbar-inner">
      <div class="container">
        <div class="user-nav pull-right">
          <%= link_to "Github Login", "/auth/github" %>
        </div>
      </div>
    </div>
  </div>
...

现在访问还看不到我们新加的内容,因为没有指定root,rails访问到的还是静态的index页面

演示方便我们把root改为application#index并加上需要的action和view

config/routes.rb
1
root 'application#index'
app/controllers/application_controller.rb
1
2
def index
end
app/controllers/application_controller.rb
1
2
mkdir app/views/application/
touch app/views/application/index.html.erb

现在应该能看到这个link了,点一下如果看类似No route matches [GET] "/auth/github/callback"说明已经去github做验证并把结果返回回来了

下一步我们需要验证完的回调

从上面的错误信息可以看到回调地址是/auth/github/callback, 所以我们在routes中加一行

app/controllers/application_controller.rb
1
get "/auth/:provider/callback" => "sessions#create"

然后建立controller和action

app/controllers/application_controller.rb
1
rails g controller sessions create

如果你想看看返回的数据什么样子,可以这样写sessions#create

app/controllers/application_controller.rb
1
2
3
def create
  raise env['omniauth.auth'].to_yaml
end

这样点登录的link就会把返回的信息显示在页面上

得到返回数据怎么处理就看个人需要了,你可能会想建一个表保存返回的数据, 这里我们建立users表来保存用户的nickname,由于github的nickname就是用户名,是唯一的,但是考虑到以后可能会加入其它的认证如twitter,nickname就可能冲突了,所以我们还加个provider

app/controllers/application_controller.rb
1
2
rails g scaffold User nickname provider
rake db:migrate

有了保存数据的地方,我们就可以处理返回的验证了

app/controllers/application_controller.rb
1
2
3
4
5
6
def create
  auth = request.env["omniauth.auth"]
  user = User.find_by_omniauth(auth)
  session[:user_id] = user.id
  redirect_to root_url, :notice => "Signed in!"
end

find_by_omniauth方法放到了model里,它也很简单,能找到就返回,找不到就创建

app/controllers/application_controller.rb
1
2
3
4
5
6
7
8
9
10
11
def self.find_by_omniauth(auth)
  user = User.find_by_provider_and_nickname(auth["provider"], auth["info"]["nickname"])
  user ? user : User.create_with_omniauth(auth)
end

def self.create_with_omniauth(auth)
  create! do |user|
    user.provider = auth["provider"]
    user.nickname = auth["info"]["nickname"]
  end
end

需要修改一下layout才可以看到登录成功的提示

app/views/layouts/application.html.erb
1
2
3
4
5
6
7
8
9
<div class="container" style="margin-top: 60px;">
  <% flash.each do |name, msg| %>
    <div class="alert alert-<%= name == :notice ? "success" : "error" %>">
      <a class="close" data-dismiss="alert">x</a>
      <%= msg  %>
    </div>
  <% end %>
  <%= yield %>
</div>

现在登录已经成功了,我们还需要修改一下github登录的link,让它登录后显示用户名和logout

首先我们需要定义一个帮助方法来判断用户有没登录

app/controllers/application_controller.rb
1
2
3
4
5
6
7
8
9
10
11
class ApplicationController < ActionController::Base

  protect_from_forgery with: :exception
  helper_method :current_user

  private

    def current_user
      @current_user ||= User.find(session[:user_id]) if session[:user_id]
    end
end

然后定义一条logout的路由

app/controllers/application_controller.rb
1
get "signout" => "sessions#destroy", :as => "signout"

以及对应的Action

app/controllers/application_controller.rb
1
2
3
4
def destroy
  session[:user_id] = nil
  redirect_to root_url, :notice => "Signed out!"
end

最后修改layout

app/controllers/application_controller.rb
1
2
3
4
5
6
7
8
<div class="user-nav pull-right">
  <% if current_user %>
    Welcome <%= current_user.nickname %>!
    <%= link_to "Sign out", signout_path %>
  <% else %>
    <%= link_to "Github Login", "/auth/github" %>
  <% end %>
</div>

基本上这样就算完成了

Ruby/Rails小技巧收集

| Comments

收集一些当时做的时候花了好多时间去Google,但本身又很短的小问题的答案,大部分来算StackOverflow

jQuery on的用法

on的基本用法为:

1
2
$(selector1).on 'event', 'selector2', ->
  ...

我遇到的问题是对通过ajax加载的部分on不起作用,解决办法是确保selector1不是通过ajax加载的

simple_form和bootstrap一起使用的问题

simple_form和bootstrap一起使用的时候我遇到的问题是simple_form生成的html没有加进去bootstrap的Class,看不到bootstrap样式加到table的效果,解决办法是在Gemfile里给simple_form指定版本,Rails 4是

1
gem 'simple_form', "~> 3.0.0.rc"

Rails 3是

1
gem 'simple_form, "~> 2.1.0"

Rails 4 要使用remote: true的方法执行js需要在action里加上render layout: false

The Ruby Programming Language Notes

| Comments

  • Global variables are prefixed with $
  • Instance variables are prefixed with @
  • Class variables are prefixed with @@
  • Constant begin with a capital letter
1
2
3
4
/[Rr]uby/   # Matches "Ruby" or "ruby"
/\d{5}/     # Matches 5 consecutive digits
1..3        # All x where 1 <= x <= 3 # has equal both side
1...3       # All x where 1 <= x < 3
  • Ruby case statement matches its expression against each of the possible cases using ===

  • Ruby’s strings are mutable, call freeze method to prevent any future modifications

  • In Ruby world, only nil and false are false, anything else are true

  • print is almost same with puts, except it does not append a newline

  • p almost same with puts, except it use inspect rather than to_s

  • lexical

  • punctuation
  • syntactic
  • arithmetic

  • All numeric objects are immutable

  • Integer division by zero causes a ZeroDivisionError to be thrown.

  • Floating-point division by zero does not cause an error; it simple returns the value Infinity
  • 0.0/0.0 => NaN

  • 4**3**2 is same as 4**9 not 64**2

  • even = (x[0] == 0) # A number is even if the least-significat bit is 0

  • ‘a\b’ == ‘a\b’

  • money = “\u{20AC 20 A3 20 A5}” # => “€ £ ¥”

  • Run system command

1
2
`ls`
%x[ls]

?A # Character literal for the ASCII character A /“ # Character literal for the ASCII character ” /? # Character literal for the ASCII character ? not need any more

1
2
a = 0;
"#{a=a+1} " * 3   # returns "1 1 1", not "1 2 3"
1
2
3
4
5
6
s = "hello"
s[index, length]
s[0, 2] # "he"
s[start_index..end_index]   # include end_index
s[start_index...end_index]  # not include end_index
s[/[aeiou]/] = '*'          # replace first vowel with an asterisk
1
2
3
s = "¥1000"
s.each_char { |x| print "#{x} " } # Prints "¥ 1 0 0 0". Ruby 1.9
0.upto(s.size-1) { |i| print "#{s[i]} " }  # Inefficient with multibyte chars
1
words = %w[this is a test]  # Same as ['this', 'is', 'a', 'test']
1
2
3
4
5
6
a = [1, 1, 2, 2, 3, 3, 4]
b = [5, 5, 4, 4, 3, 3, 2]
a | b  # [1, 2, 3, 4, 5]: duplicates are removed
b | a  # [5, 4, 3, 2, 1]: elements are the same, but order is different
a & b  # [2, 3, 4]
b & a  # [4, 3, 2]
  • equality

  • equal? method is defined by Object to test whether two values refer to exactly the same object

  • == in Object is just alias to equal? but most class redefine it

  • != simple use == and inverts the result

  • eql? strict version of ==

1
2
1 == 1.0    # true: Fixnum and Float objects can be ==
1.eql?(1.0) # false: but they are never eql!
  • === most use in case statement and:
    • Many classes, is the same as ==
    • Range to test whether a value falls within the range
    • Regexp to test whether a string matches the regular expression
    • Class to test whether an object is an instance of that class
    • In Ruby 1.9 Symbol to return true if the righthand operand is the same symbol as the left or if it is a string holding the same text
1
2
3
4
(1..10) === 5  # true: 5 is in the range 1..10
/\d+/ === "123" # true: the string matches the regular expression
String === "s"  # true: "s" is an instance of the class String
:s === "s"      # true in Ruby 1.9
  • =~ pattern match(like Regexp)

  • <=>

    • Return -1 if its left operand is less than its right operand
    • Return 0 if the two operands are equal
    • Return 1 if the left operand is greater than the right operand
    • Return nil if can not meaningfull compared
  • explicit

  • to_s: return a human-readable representation of the object, suitable for end users

  • inspect: is intended for debugging use, and should return a representation that is helpful to Ruby developers
  • default inspect method, inherited from Object, simple calls to_s

  • implicit

  • invocation
  • abbreviated
1
2
3
4
5
# splat (*)
x, y = 1, *[2, 3] # Same as x, y, z = 1, 2, 3
x, *y = 1, 2, 3   # x = 1; y = [2, 3]
x, *y = 1, 2      # x = 1; y = [2]
x, *y = 1         # x = 1; y = []
1
2
3
4
5
6
# Ruby 1.9 only
*x, y = 1, 2, 3   # x = [1, 2]; y = 3
*x, y = 1, 2      # x=[1]; y = 2
*x, y = 1         # x = []; y = 1

x, y *z = 1, *[2, 3, 4] # x = 1;y = 2; z=[3,4]
  • && has higher precedence that ||
1
1 || 2 && nil  # => 1
  • and, or and not operators are low-precedence versions of &&, ||, and ! they have lower precedence that the assignment operator

  • and and or have the same precedence and not is just slightly higher

1
2
x || y && nil   # && is performed first   => x
x or y and nil  # evaluated left-to-right => nil
  • ?: operator is right-associative(same as **)
1
2
3
a ? b : c ? d : e     # This expression ..
a ? b : (c ? d : e)   # is evaluated like this..
(a ? b : c) ? d : e   # NOT like this
  • The loop variable or variables of a for loop are not local to the loop; they remain defined even after the loop exit. Similarly, new variables defined within the body of the loop continue to exist after the loop exits.
1
2
3
squares = [1,2,3].collect {|x| x*x}   # => [1,4,9]
evens = (1..10).select {|x| x%2 == 0} # => [2,4,6,8,10]
odds = (1..10).reject {|x| x%2 == 0}  # => [1,3,5,7,9]
1
2
3
4
data = [2, 5, 3, 4]
sum = data.inject { |sum, x| sum + x }     # => 14    (1+5+3+4)
floatprod = data.inject(1.0) { |p,x| p*x } # => 120.0 (1.0*2*5*3*4)
max = data.inject { |m, x| m>x ? m : x }   # => 5
  • implicit
  • fundamental

  • use return in a block will cause the method that use yield to call the block exit

  • return value in block should use next

  • blocks defined a new variable scope: variables created within a block exist only within that block and are undefined outside of the block

  • the local variables in a method are available to any blocks within that method. so if a block assigns a value to a variable that is already defined outside of the block, this does not create a new block-local variable but instead assigns a new value to the already-existing variable.

  • invocation

  • an important difference between block parameters and method parameters is that block parameters are not allowed to have default values assigned as method parameters are.(seems not any more)

1
[1,2,3].each {|x,y=10| print x*y }  # SyntaxError!
  • intuitive

  • return always causes the enclosing method to return. the enclosing method, also called the lexically enclosing method, is the method that the block appears inside of when you look at the source code

  • note that unlike return, break never causes the lexically enclosing method to return. break can only appear within a lexically enclosing loop or within a block. using it in any other context causes a LocalJumpError

  • manipulate

  • represent

  • An important feature of procs and lambdas is that they are closures: they retain access to the local variables that were in scope when they were defined, even then the proc or lambda is invoked from a different scope

  • subtle

  • invocation
  • respectively

  • Ruby implementations typically treat Fixnum and Symbol values as immediate values rather than as true object references. For this reason, singleton methods may not be defined on Fixnum and Symbol objects. For consistency, singletons are also prohibited on other Numberic objects.

  • Interestingly, undef can be used to undefine inherited method, without affecting the definition of the method in the class from which it is inherited.

  • It cannot be used to undefine a singleton method in the way that def can be used to define such a method.

  • punctuation

  • unary
1
alias aka also_known_as   # alias new_name existing_name
1
2
3
4
5
6
7
8
9
10
11
def hello
  puts 'Hello World'
end

alias original_hello hello

def hello
  puts 'Your attention please'
  original_hello
  puts 'This has been a test'
end
  • overload
  • ambiguous
  • wrinkle
1
2
3
def suffix(s, index=s.size-1)
  s[index, s.size-index]
end
  • In Ruby 1.8, method parameters with default values must appear after all ordinary parameters in the parameter list. Ruby 1.9 relaxes this restriction and allows ordinary parameters to appear after parameters with defaults. It still requires all parameters with defaults to be adjacent in the parameter list – you can’t declare two parameters with default values with an ordinary parameter between them, for example.

  • No more than one parameter may be prefixed with an *. In Ruby 1.8, this parameter must appear after all ordinary parameters and after all parameters with defaults specified. It should be the last parameter of the method, unless the method also has a parameter with an & prefix. In Ruby 1.9, a parameter with an * prefix must still appear after any parameters with defaults specified, but it may be followed by additional ordinary parameters. It must also still appear before any &-prefixed parameter.

  • anonymity

  • Blocks are syntactic structures in Ruby; they are not objects, and cannot be manipulated as objects.

  • It is possible, however, to create an object that represents a block. Depending on how the object is created, it is called a proc or a lambda
  • Procs have block-like behavior and lambdas have method-like behavior. Both, however, are instances of class Proc.

  • Proc.new => procs

  • Kernel.lambda => lambda
  • Kernel.proc => alias of lambda in Ruby 1.8
  • Kernel.proc => alias of Proc.new in Ruby 1.9
1
2
succ = lambda {|x| x+1}  # Ruby 1.8 lambda
succ = ->(x){ x+1 }      # Ruby 1.9 lambda
1
2
# This lambda takes 2 args and declares 3 local vars
f = ->(x,y; i,j,k) { ... }
1
zoom = ->(x,y,factor=2) { [x*factor, y*factor] }   # only on Ruby 1.9
1
2
3
succ = ->x { x+1 }
f = -> x,y; i,j,k { ... }
zoom = ->x,y,factor=2 { [x*factor, y*factor] }
  • Proc Equality – The == method only returns true if on Proc is a clone or duplicate of the other;
1
2
3
4
5
6
7
8
9
p = lambda {|x| x*x }
q = p.dup
q == p                      # => true: the two procs are equal
p.object_id == q.object_id  # => false: they are not the same object

- A proc is the object form of a block, and it behaves like a block.
- A lambda has slightly modified behavior and behaves more like a mthod that a block.

- The return statement in a block does not just return from the block to the invoking iterator, it returns from the method that invoked the interator.

def test puts “entering method” 1.times { puts “entering block”; return } # Makes test method return puts “exiting method” # This line is never executed end test

1
2

- A proc is like a block, so if you call a proc that executes a return statement, it attempts to return from the method that encloses the block that was converted to the proc.

def test puts “entering method” p = Proc.new { puts “entering proc”; return } p.call # Invoking the proc makes method return puts “exiting method” # This line is never executed end test

1
2

- Using a return statement in a proc is tricky, however, because procs are ofter passed around between methods. By the time a proc is invoked, the lexically enclosing method may already have returned:

def procBuilder(message) # Create and return a proc Proc.new { puts message; return } # return returns from procBuilder # but procBuilder has already returned here! end

def test puts “entering method” p = procBuilder(“entering proc”) p.call # Prints “entering proc” and raises LocalJumpError! puts “exiting method” # This line is never executed end test

1
2

- A return statement in a lambda, therefore, returns from the lambda itself, not from the method that surrounds the creation site of the lambda:

def test puts “entering method” p = lambda { puts “entering lambda”; return } p.call # Invoking the lambda does not make the method return puts “exiting method” # This line is executed now end test

1
2

- The fact that return in lambda only returns from the lambda itself means that we never have to worry about LocalJumpError

def lambdaBuilder(message) # Create and return a proc lambda { puts message; return } # return returns from lambda # but procBuilder has already returned here! end

def test puts “entering method” p = lambdaBuilder(“entering lambda”) p.call # Prints “entering lambda” puts “exiting method” # This line is executed end test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

- A top-level next statement works the same in a block, proc, or lambda: ti causes the yield statement or call method that invoked the block, proc, or lambda to return. If next is followed by an expression, then the value of that expression becomes the rturn value of the block, proc, or lambda.

- redo also works the same in procs and lambdas: it transfers control back to the beginning of the proc or lambda.

- retry is never allowed in procs or lambdas: using it always results in a LocalJumpError.

- raise behaves the same in blocks, procs, and lambdas. Exceptions always propagate up the call stack. If a block, proc, or lambda raises an exception and there is no local rescue clause, the exception first propagates to the method that invoked the block with yield or that invoked the proc or lambda with call.

- Argument passing to procs and lambdas
  - The yield statement uses yield semantics
  - method invocation uses invocation semantics
  - Yield semantics are similar to parallel assignment
  - invoking a proc uses yield semantics
  - invoking a lambda uses invocation semantics

p = Proc.new {|x,y| print x,y } p.call(1) # x,y=1: nil used for missing rvalue: Prints 1nil p.call(1,2) # x,y=1,2: 2 lvalues, 2rvalues: Print 12 p.call(1,2,3) # x,y=1,2,3: extra rvalues discarded: Print 12 p.call([1,2]) # x,y=[1,2]: array automatically unpacked: Print 12

1
2
3
4
5
6
7
8

```
l = lambda {|x,y| print x,y }
l.call(1,2)     # This works
l.call(1)       # Wrong number of arguments
l.call(1,2,3)   # Wrong number of arguments
l.call([1,2])   # Wrong number of arguments
l.call(*[1,2])  # Works: explicit splat to unpack the array
  • In Ruby, procs and lambdas are closures.
  • An object that is both an invocable function and a variable binding for that function.
  • When you create a proc or a lambda, the resulting Proc object holds not just the executable block but also bindings for all the variables used by the block.

  • One important difference between Method objects and Proc objects is that Method objects are not closures.

  • encapsulated

  • In addition to being automatically invoked by Point.new, the initialize method is automatically made private. An object can call initialize on itself, but you cannot explicitly call initialize on p to reinitialize its state.

  • coerce

1
Point::NEGATIVE_UNIT_X = Point.new(-1,0)
  • Class Instance Variables

    • An instance variable used inside a class definition but outside an instance method definition
    • Class instance variables cannot be used from instance methods
  • Define Class

1
2
3
4
5
6
class Point
  ...
  def Point.sum(*points)
  ...
  end
end
1
2
3
4
5
6
class Point
  ...
  def self.sum(*points)
    ...
  end
end
1
2
3
4
5
class << Point
  def sum(*points)
    ...
  end
end
1
2
3
4
5
class Point
  class << self
    ...
  end
end
  • Method are normally public unless they are explicitly declared to be private or protected. One exception if the initialize method, which is always implicityly private.

  • Another exception is any “global” method declared outside of a class definition — those methods are defined as private instance methods of Object.

  • A public method can be invoked from anywhere — where are no restrictions on its use.

  • A private method is internal to the implementation of a class, and it can only be called by other instance methods of the class (or, as we’ll see later, its subclasses).

  • Private methods are implicitly invoked on self, and may not be explicitly invoked on an object.

  • If m is a private method, then you must invoke it in functional style as m. You cannot write o.m or even self.m

  • A protected method is like a private method in that it can only be invoked from within the implementation of a class or its subclasses. It differs from a private method in that it may be explicit invocatioin on self.

  • A protected method can be used, for example, to define an accessor that allows instances of a class to share internal state with each other, but does not allow users of the class to access that state.

  • Protected methods are the least commonly defined and also the most difficult to understand. The rule about when a protected method can be invoked can be more formally described as follows: a protected method defined by a class C may be invoked on an object o by a method in the object p if and only if the classes of o and p are both subclasses of, or equal to, the class C.

  • One of the important things to understand about object-oriented programming and subclassing is that when methods are invoked, they are looked up dynamically so that the appropriate definition or redefinition of the method is found. That is, method invocations are not bound statically at the time they are parsed, but rather, are looked up at the time they are executed.

  • If you use super as a bare keyword — with no arguments and no parentheses — then all of the arguments that were passwd to the current method are passed to the superclass method.

  • Ruby’s instance variables are not inherited and have nothing to do with the inheritance mechanism. The reason that they sometimes appear to be inherited is that instance variables are created by the methods that first assign values to them, and those methods are often inherited or chainned.

1
2
3
4
5
6
7
8
9
10
class Point3D < Point
  def initialize(x,y,z)
    super(x,y)
    @z = z;
  end

  def to_s
    "(#@x, #@y, #@z)"
  end
end
  • In this code, Point3D defines an initialize method that chains tothe initialize method of its superclas.. The chained method assigns values to the variables @x and @y, which makes those variables come into existence for a particular instance of Point3D

  • Class variables are inherited

1
2
3
4
5
6
7
8
9
class A
  @@value = 1
  def A.vaue; @@value; end
end
print A.value
class B < A; @@value = 2; end
print A.value
class C < A; @@value = 3; end
print B.value
  • The important difference between constants and methods is that constants are looked up in the lexical scope of the place they are used before they are looked up in the inheritance hierarchy

  • Another way that new objects come into existence is as a result of the dup and clone methods. These methods allocate a new instance of the class of the object on whhich they are invoked. They then copy all the instance variables and the taintedness of the receiver object to the newly allocated object.

  • clone takes this copying a step further that dup — it also copies singleton methods of the receiver object and freezes the copy object if the original is frozen.

  • A third way that objects are created is when Marshal.load is called to re-create object previously marshaled (or “serialized”) with Marshal.dump.

  • A singleton is a class that has only a single instance

  • A singleton method is a method added to a single object rather than to a class of objects

  • A module cannot be instantiated, and it cannot be subclassed

  • Modules are used as namespeces and as mixins

  • Just as a class object is an instance of the Class class, a module object is an instance of the Module class. Class is a subclass of Module. This means that all classes are modules, but not all modules are classes.

  • Class can be used as namespaces, just as modules can. Class cannot, however, be used as mixins.

  • The inclusion of a module affects the type-checking method is_a? and the switch-equality operator ===. For example, String mixes in the Comparable module and, in Ruby 1.8, also mixes in the Enumerable module:

1
2
"text".is_a? Comparable  # => true
Enumerable === "text"    # => true in Ruby 1.8, false in 1.9
  • Note that instance_of? only checks the class of its receiver, not superclasses or modules, so the following is false:
1
"text".instance_of? Comparable  # => false
  • Although every class is a module, the include method does not allow a class to be included within another class. The arguments to include must be modules declared with module, not classes

  • If you want to create a module like Math or Kernel (after included, call method without module name), define you methods as instance methods of the module. Then use module_function to convert those methods to “module functions.”

  • In addition to loading source code, require can also load binary extensions to Ruby.

  • load expects a complete filename including an extension. require is usually passed a library name, with no extension, rather than a filename. In that case, it searches for a file that has the library name as its base name and an appropriate source or native library extension. If a directory contains both an .rb source file and a binary extension file, require will load the source file instead of the binary file.

  • load can load the same file multiple times. require tries to prevent multiple loads of the same file. require keeps track of the files that have been loaded by appending them to the global array $“ (also known as $LOADED_FEATURES). load does not do this.

  • load loads the specified file at the current $SAFE level. require loads the specified library with $SAFE set to 0, even if the code that called require has a higher value for that variable. In theory, therefore, it should be safe for require to load files with a reduced $SAFE level.

  • Files loaded with load or require are executed in a new top-level scope that is different from the one in which load or require was invoked. The loaded file can see all global variables and constants that have been defined at the time it is loaded, but it does not have access to the local scope from which the load was initiated

  • The local variables defined in the scope from which load or require is invoked are not visible to the loaded file.

  • Any local variables created by the loaded file are discarded once the load is complete; they are never visible outside the file in which they are defined.
  • At the start of the loaded file, the value of self is always the main object, just as it is when the Ruby interpreter starts running. That is, invoking load or require whthin a method invocation does not propagate the receiver object to the loaded file.
  • The current module nesting is ignored within the loaded file. You cannot, fir example, open a class and load a file of method definitions. The file will be precessed in a top-level scope, not inside any class or module.

  • Ruby method lookup or method name resolution ( o.m )

    • First, it checkes the eigenclass of o for singleton methods name m.
    • If no method m is found in the eigenclass, Ruby searches the class of o for an instance method named m.
    • If no method m is found in the class, Ruby searches the instance methods of any modules included by the class of o. If that class includes more that one module, then they are searched in the reverse of the order in which they were included. That is the most recently included module is searched first.
    • If no instance method m is found in the class of o or in its modules, then the search moves up the inheritance hierarchy to the superclass. Steps 2 and 3 are repeated for each class in the inheritance hierarchy until each ancestor class and its included modules have ben searched.
    • If no method named m is found after completing the search, then a method named method_missing is invoked instand. In order to find an appropriate definition of this method, the name resolution algorithm start over at step 1. The Kernel module a default implementation of method_messing, so this second pass of name resolution is guranteed to succeed.
  • Class objects are special: they have superclasses.

  • The eigenclasses of class objects are also special: they have superclasses, too.

  • refection

  • introspection
  • examine
  • malicious

  • Note that eval evaluates its code in a temporary scope. eval can alter the value of instance variables that already exist. But any new instance variables it defines are local to the invocation of eval and cease to exist when it returns. (It is as if the evaluated code is run in the body of a block — variables local to a block do not exist outside the block)

  • synonym

  • It is important to understand that define_method is private. You must be inside the class or module you want to use it on in order to call it:

1
2
3
4
5
6
7
8
9
10
# Add an instance method named m to class with body b
def add_method(c, m, &b)
  c.class_eval {
    define_method(m, &b)
  }
end

add_method(String, :greet) { "Hello, " + self }

"world".greet # => "Hello, world"

t = Time.utc(2000, 12, 31, 23, 59, 59, 999999) t.year # => 2000

日期时间选择插件使用

| Comments

以前做过这样的东西,花了好多时间,因为要选择到底用那个插件,还有好多插件要只有日期没有时间,今天做这样的东西又花了不少时间,决定记录下来,下一次就简单多了

我使用的是jQuerydatepicker加上jQuery-Timepicker-Addon

示例代码在这里: datetimepicker-demo,懒得看详细的步骤可以直接看代码

效果类似这样:

  • 首先创建项目
1
rails new datetimepicker-demo
  • 然后我创建了一个叫activity的scaffold,插件会用到start_at这个字段上
1
2
rails g scaffold activity title content:text start_at:datetime
rake db:migrate
  • 下来我们要把需要的插件加进来,jquery-ui我用一个非常方便的gem: jquery-ui-rails, 只要加到Gemfilegem jquery-ui-rails,运行bunder就行了

  • jQuery-Timepicker-Addon我是自己下载下来把需要的文件复制到项目的vender目录下,需要用到三个文件:jquery-ui-timepicker-addon.js, jquery-ui-timepicker-addon.cssi18n目录下的jquery-ui-timepicker-zh-CN.js, js文件复制到vendor/assets/javascripts/, css文件复制到vendor/assets/stylesheets/

  • 需要的文件都加进来了,下面我们需要在include他们

app/assets/javascripts/application.js
1
2
3
4
5
//= require jquery
//= require jquery.ui.datepicker
//= require jquery.ui.slider
//= require jquery-ui-timepicker-addon
//= require jquery-ui-timepicker-zh-CN
app/assets/stylesheets/application.css
1
2
3
 *= require jquery.ui.datepicker
 *= require jquery.ui.slider
 *= require jquery-ui-timepicker-addon
  • 准备工作都做完了,下来就是使用了
app/views/activities/_form.html.erb
1
2
3
4
  <div class="field">
    <%= f.label :start_at %><br>
    <%= f.text_field :start_at, :id => "datetimepicker" %>
  </div>
app/assets/javascripts/activities.js.coffee
1
2
3
4
$ ->
  $("#datetimepicker").datetimepicker
    stepMinute: 5
    dateFormat: "yy-mm-dd"

更多参数和例子可以看这里

这样就算完成了,但是虽然加了那个i18n的文件,月和周的显示还是英文, 这可以直接在js文件里加,也可以在vendor/assets/javascripts/jquery-ui-timepicker-zh-CN.js中加上这两行

app/assets/javascripts/activities.js.coffee
1
2
  monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
  dayNamesMin: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],

资源:

只允许上海的用户注册

| Comments

刚开始听到老板说这个需求的时候,我还傻呼呼一直在想要怎么得到所有上海的IP地址,都打算试着写爬虫去爬这些数据,事实上我只要判断一个客户端的IP是不是属于上海就行了,而这个动作可以由一些第三方提供的API来做,像百度就有提供这个的服务

转过弯来就比较简单了,我的方法在app/controller/application_controller.rb文件里加一个helper方法

app/controller/application_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
class ApplicationController < ActionController::Base
  helper_method :is_shanghai_ip?
...
  def is_shanghai_ip?(ip)
    uri = "http://api.map.baidu.com/location/ip?ak=your_api_key&ip=#{ip}&coor=bd09ll"
    resp = Net::HTTP.get_response(URI.parse(uri))
    data = resp.body
    result = JSON.parse(data)
    return false if result['status'] == 1
    result['content']['address'] == "上海市" ? true : false
  end
...
end

然后在你要做控制的action中调用这个方法,比如我们要限制只有上海的用户可以注册

app/controller/users_controller.rb
1
2
3
4
5
6
def sign_up
  unless is_shanghai_ip?(request.remote_ip)
    redirect_to "/about/us" and return
  end
  ...
end

百度提供的这个服务的地址是: http://developer.baidu.com/map/ip-location-api.htm

另外有一个叫geocoder的gem非常强大,它直接在Rack::Request里塞一个location的方法,你可以直接在controller里这样调用

app/controller/users_controller.rb
1
2
city = request.location.city
country = request.location.country_code

此外它还有一些非常强大的功能如查找经纬度,查找附近,查找距离等等,更详细的信息可以访问它的官方网站, RailsCast还有一期是介绍它的

IE10下图片灰度处理

| Comments

其它浏览器或版本的图片灰度处理可以看这里

上面的方法对IE10是没有效果的,Google了好久发现只有用svg才对IE10才有作用,想知道svg是什么请看这里

下面的代码就可以对图片做灰度的处理

~/tmp/index.html
1
2
3
4
5
6
7
8
9
10
11
<svg xmlns="http://www.w3.org/2000/svg" id="svgroot" viewBox="0 0 100 100" width="100" height="100">
  <defs>
    <filter id="filtersPicture">
      <feComposite result="inputTo_38" in="SourceGraphic" in2="SourceGraphic" operator="arithmetic" k1="0" k2="1" k3="0" k4="0" />
      <feColorMatrix id="filter_38" type="saturate" values="0" data-filterid="38" />
    </filter>
  </defs>
  <image filter="url(&quot;#filtersPicture&quot;)" x="0" y="0" width="100" height="100" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://williamherry.com/images/chef/attributes.jpg" />
</svg>

<img src="http://williamherry.com/images/chef/attributes.jpg">

为了做比较我在后面放了一个不做处理的图片,你可以把上面的代码保存为.html格式文件用浏览器打开看看

上面的代码可以达到我们期望的效果,但是又有新的问题,我给它的高和宽都设置的100,但从浏览器来看它好像并不是正方形.

这里的问题是它是按比例缩放的,例如图片是100X1000,如果我给它设置成100X100,那么最终图片的显示可能是10X100,没有完全填满100X100

对于这个问题我还没有找到完美的解决办法,但有一个办法已经可以满足我自己的需求,这个方法是在<image后面加上preserveAspectRatio="xMidYMid slice"

还用上面的例子,它会把图片按比较放到和短的边一样, 这里是100X1000,然后把100X100的框放中间,多余的裁掉,剩下的就是显示的部分

这样只要保存图片的比例和设置的很接近就可以基本上解决这个问题

更多preserveAspectRatio的信息请看这里

更新

StackOverflow上对这个问题已经有人回答了,让图片按给定的高和宽拉伸只要把preserveAspectRatio设置成none就可以了

理解测试中的stub和mock

| Comments

刚开始学习rspec的时候,stubmock理解起来有点困难,听了TerryaNdReW的讲解后,对它们的理解深入多了,在此表示感谢

先从stub说起,什么是stub呢,CodeSchool给出这样的定义:

Stub:
For replacing a method with code that returns a specified result

简单说就是你可以用stub伪造(fade)一个方法,阻断对原来方法的调用,例如下面来自CodeSchool的例子

我们有一个叫zombiemodel

app/models/zombie.rb
1
2
3
4
5
6
7
8
class Zombie < ActiveRecord::Base
  has_one :weapon

  def decapitate
    weapon.slice(self, :head)
    self.status = "dead again"
  end
end

我们要测试decapitate方法,它里面调用了weaponslice方法,下面是测试代码:

/spec/models/zombie_spec.rb
1
2
3
4
5
6
7
8
9
10
11
describe Zombie do
  let(:zombie) { Zombie.create }

  context "#decapitate" do
    it "sets status to dead again" do
      zombie.weapon.stub(:slice)
      zombie.decapitate
      zombie.status.should == "dead again"
    end
  end
end

上面代码的第6行就是用stub伪造了weaponslice方法,阻断了对原来方法的调用

你可能会问为什么我们要这样做,这是因为我们在做单元测试,weaponslice可能会非常复杂,里面又调用了其它的方法等等,这是集成测试应该做的工作.事实上这里我们是在测试decapitate方法会把zombie.status设置成"dead again"

接下来我们来说mock, CodeSchool上给的定义是这样的:

Mock:
A stub with an expectations that the method gets called.

简单来说mock就是stub + expectation, 说它是stub是因为它也可以像stub一样伪造方法,阻断对原来方法的调用, expectation是说它不仅伪造了这个方法,它还期望你(必须)调用这个方法,如果没有被调用到,这个testfail了,看下面的例子

/spec/models/zombie_spec.rb
1
2
3
4
5
6
7
8
9
10
describe Zombie do
  let(:zombie) { Zombie.create }

  context "#decapitate" do
    it "calls weapon.slice" do
      zombie.weapon.should_receive(:slice)
      zombie.decapitate
    end
  end
end

这里的第6行伪造了weaponslice方法,并期望这个方法在这个测试中被调用.

你可能会想为什么要这样写,这是因为我们仅仅是要测试decapitate这个方法确实调用了weapon.slice, 可以把decapitate想成下面的黑盒,我们蹲在图中的A点,等着看它会不会去调用weapon.slice

这个图是Sandi MetzRailsConf 2013上的演讲The Magic Tricks of Testing

视频

Slides

这里注意一下顺序,一般的测试先是执行一个动作,然后再去判断状态或其它东西,像前面stub的例子,先调用decapitate方法,再去判断status的变化,就好像我踢你一脚,看你会不会喊疼,而这里是先有期望再有动作,这就好比老板对你说这个下周前完成,不然就滚蛋一样

openSUSE 12.3 安装完后需要做的事情

| Comments

今天终于把openSUSE12.3安装上了,记得上次安装openSUSE应该是好几年前吧,那时候刚开始玩Linux.记得当时选发行版的时候很纠结,老想用openSUSE但当时它还是有很多问题,尤其严重的是显示上有点问题,图形界面和字符界面不能同时和显示器边对齐,以当时的水平解决不了这个问题,就放弃而一直用的Ubuntu

今天安装上了最新的12.3,比以前进步太多了,fcitx输入法安装就能用(以前很麻烦的,尤其是环境是英文的),各种驱动自动帮你安好,进系统就可以开启3D,KDE4.10已经非常稳定了,不像以前动不动就Crash. 总的来说我非常满意,以后就用她了

不过openSUSE 12.3的默认字体还是非常难看的,需要做一些配置才可以让人看着舒服

其实根本的问题并不是没有好看的字体,而是字体的渲染不给力,使字体看起来很虚,锯齿很厉害,我从网上找的这个办法可以开启抗锯齿,分享给大家

原文地址在这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Fix: Ugly Fonts in Kubuntu or KDE | Make your fonts crystal clear

By default, KDE Desktop and Kubuntu have a very ugly font behavior compared to GNOME or Ubuntu. But no worries. Fix is easy!

How to fix it?

1- Go to "System Settings > Application Appearance > Fonts"
2- Enable anti-ailising.
3- Set Use sub-pixel rendering to RGB and Slight.
4- Go Advanced tab of Desktop Effects and set scale method to Crisp.

To improve your fonts more, change your fonts to DejaVu and for a better console set console fonts to DejaVu Sans Mono 9.5.

Now you have very cyrstal looking fonts! Have fun!

我刚开始照他的做也看不到效果的,花了好长时间,发现一个很有意思的事情,就是把字体放大到16以后和缩小到8以后字体的渲染开始变得非常漂亮,后来才发现在第三步设置anti-ailising的时候点击后面的configure在弹出的窗口中有Exclude range正好是8和16,我是勾选了的,原来我把8到16之间的字体除过不渲染,去掉之后漂亮的字体终于出来了.现在你可以随便选择你喜欢的字体,都会有很好的效果

PS: 这些文字就是在新的openSUSE 12.3下面写的,看来以后要和Ubuntu说再见了

UPDATE: 无线网络的问题

Release Note中有说安装完无线网没有启动,需要手机重启系统,但是我发现我还遇到一个问题,有时候开机无线网络是没有连上了,点开直接是空的,我在这里找到了解决办法,简单说就是默认他用的dhcpd好像不太稳定,改成dhclient就好了

/etc/sysconfig/network/dhcp中修改下面两行

1
2
DHCLIENT_BIN="dhclient"
DHCLIENT_DEBUG="yes"

加第二行是因为有一个bug (不过好像已经修复了)

UPDATE2: 支付宝插件

需要安装libpng12-0才可以使支付宝的插件安装成功,不然它提示成功但却用不了

UPDATE3: 无线网硬件禁用的问题

从sleep状态resume后无线网会是硬件禁用状态,需要手动按Fn + F3开启,后来发现是驱动冲突,在/etc/modprobe.d/50-blacklist.conf中的最后加上blacklist acer-wmi就好了

还有一直让我很迷惑的Magic Lamp的问题,我很早就发现这个特效会有两种不同的效果,有时会最小化收到状态栏,有时会收到鼠标的位置,后来才知道原来收到鼠标的位置是一种fallback的情形,有的时候收到状态栏不可用,比如程序刚启动的时候,状态栏还没有调整好,这时候这个特效就会收到鼠标的位置

还有默认的配置如果你一段时间不操作电脑,他会出现输入密码的登陆的画面,但只要你动一下鼠标或键盘就消失了,我看的一个youtube上的视频上还有人说这是个bug,其实不是,这只是它的默认屏保画面而已

最简单的在crontab中执行rake命令的方法

| Comments

stackoverflow上很多都说要添加变量什么的,这是我发现的最简单而且能工作的方法,原文在这里

方法很简单,在crontab中可以定义环境变量

1
crontab -e
1
2
3
SHELL = /home/william/.rvm/bin/rvm-shell

* 4 * * * /bin/bash -l -c 'cd /path/to/app && RAILS_ENV=production rake mytask –silent'

Rails图片上传

| Comments

好久没有更新这个了,竟然连rake new_post['title']都忘记了还得查文档,看来以后还是得时不时的写点

这里简单记录一下Rails中如何处理图片上传,我们选择CarrierWave这个gem来处理上传的文件,由于重点是处理图片上传,所以我们会使用scaffolding生成一个article的CURD,把图片绑定给它(一个article有一个图片)

首先我们创建一个Rails项目

1
rails new image-upload-demo

然后用脚手架生成article

1
2
cd image-upload-demo
rails g scaffold article title:string content:text

执行migration生成数据库表结构

1
rake db:migrate

rails s启动服务,访问http://localhost:3000/articles检查有没错误

如果没有问题,下一步我们就可以设定CarrierWave了,首先是安装,在Gemfile里添加一行

1
gem 'carrierwave'

然后执行bundle install进行安装,然后用下面的命令创建uploader

1
rails g uploader photo

这会创建app/uploaders/photo_uploader.rb这个文件,carrierwave的一些设定都在这个文件里面,我们目前先不做更改,只使用默认配置

接着我们给article表加一个字段来存储文件信息

1
rails g migration AddPhotoToArticles photo:string

别忘了执行rake db:migrate生成表结构

然后的article的model里加入调用carrierwave的代码

1
2
3
4
5
6
# app/models/article.rb
class Article < ActiveRecord::Base
  attr_accessible :content, :title, :photo

  mount_uploader :photo, PhotoUploader
end

别忘了把photo也加到attr_accessible里哦 这样Model和数据库就可以处理上传上来的文件了,接下来在view里加入上传图片的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# app/views/articles/_form.html.erb
<%= form_for(@article, :html => {:multipart => true}) do |f| %>
...
  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :content %><br />
    <%= f.text_area :content %>
  </div>
  <div class="field">
    <label>My Photo</label>
    <%= f.file_field :photo %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
...

关键在form_for中添加:html => {:multipart => true},现在就可以使用上传的功能了,并且上传的图片可以简单的使用<%= image_tag @article.photo %>来调用,我们再在显示的view里加入显示图片的代码

1
2
3
4
5
6
7
8
# app/views/articles/show.html.erb
...

<p>
  <b>Photo:</b>
  <%= image_tag @article.photo %>
</p>
...

现在就可以测试了