Following is the main differences between Savon 1.0 and Savon 2.0

1. Client

1.0 client = Savon.client('soap_url')

2.0 client = Savon.client(wsdl: 'soap_url')

2. Actions

1.0 client.wsdl.soap_actions

2.0 client.operations

3. Response

1.0

1
2
3
response = client.request(:target_operation) do
  soap.body = "msg"
end

2.0 response = client.call(:target_operation, message: 'msg')

response.body

4. Reference

https://github.com/savonrb/savon http://savonrb.com/version2/requests.html

1. 下载

instant client 32bit www.oracle.com/technetwork/cn/topics/winsoft-085727.html‎

instant client 64bit www.oracle.com/technetwork/topics/winx64soft-089540.html

2. 解压

D:/instantclient121_32bit

D:/instantclient121_64bit

3. 添加环境变量

TNSADMIN D:/instantclient12164bit

4. 配置PL/SQL

D:/instantclient121_32bit/TNSNAMES.ORA

1
2
3
4
5
6
7
8
9
10
your_config_name =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = your_server_ip)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SID = your_sid)
      (SERVER = DEDICATED)
    )
  )

PL/SQL->Tools->Perferences->Oracle->Connection

Oracle Home=D:/instantclient12132bit OCILibrary=D:/instantclient121_32bit/oci.dll

5. 配置Navicat(64bit)

Navicat自带的oracle客户端有问题,需要手工指定路径。

Tools->Options->OCI

D:\instantclient121_64bit\oci.dll

WARNING: The following is the study notes of "MetaProgramming Ruby". As the only academic research, if you like the content, please support genuine.

以下内容为《Ruby元编程》的读书笔记,仅作为学术研究,如果您喜欢以下内容,请支持正版.

I. The object model

Open Class

1
2
3
4
5
class String
  def to_alphanumeric
    gsub /[^\w\s]/,''
  end
end

Inside Class Definitions

1
2
3
4
5
3.times do
  class C
    puts 'hello'
  end
end

class关键字把你带到类的上下文,让你可以在其中定义方法。

What's in an Object

1
2
3
4
5
6
7
8
class MyClass
  def my_method
    @v = 1
  end
end

obj = MyClass.new
obj.class # => MyClass

实例变量

1
2
obj.my_method
obj.instance_variabes  #=> [:@v]

方法

1
obj.methods.grep(/my/) # => [:my_method]

一个对象仅包含它的实例变量和一个对自身类的引用。

方法存放在类中,而非对象中。

1
2
String.instance_methods == "abc".methods  # => true
String.methods == "abc".methods  # => false

Classes Revisited

类自身也是对象。类也有自己的类,名为Class。类的方法就是Class的实例方法。

1
2
"abc".class # => String
String.class # => Class
1
2
3
String.superclass # => Object
Object.superclass # => BasicObject
BasicObject.superclass # => nil
1
2
Class.superclass # => Module
Module.superclass # => Object
1
2
class MyClass; end
obj1 = MyClass.new

obj1 和 MyClass 都是引用,唯一区别在于,obj1是一个变量,而MyClass是一个常量。

Alt text

Read on →

疑惑

C语言有传址传值的概念,于是Google一下Ruby是否也有。

http://stackoverflow.com/questions/1872110/is-ruby-pass-by-reference-or-by-value, 在stackoverflow答案看到以下两条答案,更加困惑。

1
2
> def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}"
Ruby is pass-by-value
1
2
> def foo(bar) bar.replace 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}"
Ruby is pass-by-reference

解惑

在100赞的答案中找到答案。

1
2
str = 'foo'
str.object_id # => 2000

Alt text

1
2
str = 'bar'
str.object_id # => 2002

Alt text

1
2
str2 = str
str2.object_id # => 2002

Alt text

1
str2.replace 'baz'

Alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
str = 'foo'

def mutate(str2)
  puts "str2: #{str2.object_id}"
  str2.replace 'bar'
  puts "str2: #{str2.object_id}"
  str2 = 'baz'
  puts "str2: #{str2.object_id}"
end

str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004

Summary

函数参数指向传入的对象。如果对参数赋值(=),参数将指向新对象。

References

http://stackoverflow.com/questions/1872110/is-ruby-pass-by-reference-or-by-value

1. Background

随着业务发展,功能越来越多,开发和测试越来越慢。需要进行功能切分和解耦。

尝试将部分功能转移至Mountable Engine,但感觉不理想。

  1. migration麻烦。功能迭代很快,每次都要执行 rake your_engine:install:migrations; rake db:migraterails plugin new my_engine --mountable 生成的目录不包含app/models,估计也是不提倡在Mountable Engine摆models。

  2. 功能依然耦合。各Engine和Main APP实际还是可以互相调用,易写出耦合的东西。

  3. 任何部署仍要deploy整个Main APP

开始尝试单点登录(SSO),谷歌了一下,主要有共享cookie, CAS, OAuth这三种方式,目前先用最简单的共享cookie方法。

2. Create authority server

2.1 设置一致的 sessionstore 和 secrettoken。

1
2
config/initializers/session_store.rb
config/initializers/secret_token.rb

2.2 提供Restful API.

config/routes.rb

1
2
3
4
5
6
7
8
namespace :api do
  resources :users, :only=>[:index, :show] do
    resources :roles, :only=>[:index], :controller=>'user_roles'
  end
  resources :roles, :only=>[:index, :show] do
    resources :users, :only=>[:index], :controller=>'role_users'
  end
end

2.3 登陆后设置session[:currentuserid],方便子应用调用。

app/controllers/application_controller.rb

1
2
3
4
5
6
7
8
9
10
11
def after_sign_in_path_for(resource)
  session[:current_user_id] = current_user.id
  session[:current_user_name] = current_user.name
  super
end

def after_sign_out_path_for(resource_or_scope)
  session[:current_user_id] = nil
  session[:current_user_name] = nil
  super
end

3. Sub APP

3.1 Gemfile

1
gem 'eagle_base', :git => 'https://github.com/richfisher/eagle_base.git', :branch=>'master'

3.2 config/initializers/eagle_base.rb

1
2
AUTHORITY_SERVER_URL = 'http://host/sub_uri'
AUTHORITY_PATH = '/sub_uri'

3.3 设置一致的 sessionstore 和 secrettoken。

1
2
config/initializers/session_store.rb
config/initializers/secret_token.rb

设置完成后,即可实现:

  1. 单点登陆和登出。
  2. 在controllers和elpers均可调用current_user。

4. Base Engine

https://github.com/richfisher/eagle_base

进行登陆验证并提供UserResource和RoleResource

4.1 Models

调用Authority Server的API,并对结果进行封装。

UserResource
1
2
3
4
5
6
UserResource.all(condition)
UserResource.find(condition)

UserResource#roles
UserResource#id
UserResource#name
RoleResource
1
2
3
4
5
6
7
RoleResource.all(condition)
RoleResource.find(condition)

RoleResource#users
RoleResource#id
RoleResource#name
RoleResource#permissions

4.2 Controllers

current_user

config/initializers/current_user.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ActionController::Base.class_eval do
  before_filter :authenticate_user!
  helper_method :current_user, :current_user_id, :current_user_name

  def authenticate_user!
    redirect_to login_path and return if session[:current_user_id].blank?
  end

  def current_user
    UserResource.find(current_user_id)
  end

  def current_user_id
    session[:current_user_id]
  end

  def current_user_name
    session[:current_user_name]
  end

  def login_path
    "#{AUTHORITY_PATH}/users/sign_in"
  end
end
处理 CanCan::AccessDenied

config/initializers/cancanaccessdenied.rb

1
2
3
4
5
6
7
8
9
ActionController::Base.class_eval do
  rescue_from CanCan::AccessDenied do |exception|
    if current_user.blank?
      redirect_to login_path , :alert=>t('please_login_first') and return
    else
      redirect_to '/', :alert => exception.message
    end
  end
end

Mechanize, 一个ruby类库,可以对网站进行交互,如抓取、登录等; OCR, 验证码识别神器.

1. 简单的用户名和密码登陆,无验证码

1
2
3
4
5
@agent = Mechanize.new
form = @agent.get("login_url")
form.username = 'username'
form.password = 'password'
form.submit

2. 带验证码的登陆

首先安装神器 tesseract

1
2
brew install tesseract
gem install tesseract-ocr

不一定能一次识别到,所以通过一个loop直到找到4位数字验证码。

1
2
images = page.search('.checkcode img')
vali_code = get_vali_code(images.first.attributes["src"])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def get_vali_code(src)
  code = ''
  loop do
    image_string = @agent.get(src).body_io.string

    e = Tesseract::Engine.new {|e|
      e.language  = :eng
      e.blacklist = '|'
    }

    code = e.text_for(image_string).strip.match(/\d+/).to_s

    break if code.size == 4
  end
  code
end

3. 一些用到的

Get and post

1
2
page1 = @agent.get(url)
page2 = @agent.post(url, params)

find element

1
page.search('.checkcode img')

current url

1
page.uri.to_s

操作链接

遍历

1
page.links.each {|link| }

获取地址

1
link.href

点击

1
link.click

4. Reference

http://asciicasts.com/episodes/191-mechanize https://github.com/meh/ruby-tesseract-ocr

1. Background

通过Newrelic监控到某个子应用SQL平均执行时间达到了300ms,痛点如下:

1
SELECT COUNT(*) FROM `table_rows` WHERE `table_rows`.`table_id` = 1 AND `table_rows`.`user_id` = 98

已经分别为tableid和userid建立索引。

EXPLAIN EXTRA结果:

1
Using intersect(index_table_rows_on_user_id,index_table_rows_on_table_id); Using where

2. Solutions

建立联合索引

1
rails g migration add table_id_and_user_id_index_to_table_rows
1
2
3
4
5
class AddTableIdAndUserIdIndexToTableRows < ActiveRecord::Migration
  def change
    add_index :table_rows, [:table_id, :user_id]
  end
end
1
rake db:migrate

执行后,开发环境查询时间由40ms提高到8ms。

EXPLAIN EXTRA

1
Using index

will_paginate

对于总量特别大的,可以为willpaginate设置totalentries,以避免count操作。

1
scope.paginate(:page=>params[:page], :total_entries=>1000)

3. Troubleshooting

deploy至production. 测试了一下,查询时间依然没有优化。

EXPLAIN一下

本地EXTRA: Using index

production EXTRA: Using where; Using index

本地mysql版本为5.6.13, production的版本为5.5.34,于是将production的mysql-server升级至5.6。问题解决!

4. Summary

  1. 建立合适的index。
  2. 将mysql-server升级至5.6
  3. 通过where减少scan的范围。

if you have query like SELECT COUNT(*) FROM USER It will be much faster for MyISAM (MEMORY and some others) tables because they would simply read number of rows in the table from stored value. Innodb will however need to perform full table scan or full index scan because it does not have such counter, it also can’t be solved by simple singe counter for Innodb tables as different transactions may see different number of rows in the table.

If you have query like SELECT COUNT(*) FROM IMAGE WHERE USER_ID=5 this query will be executed same way both for MyISAM and Innodb tables by performing index rage scan. This can be faster or slower both for MyISAM and Innodb depending on various conditions.

So remember Innodb is not slow for ALL COUNT() queries but only for very specific case of COUNT() query without WHERE clause.

References

http://www.mysqlperformanceblog.com/2006/12/01/count-for-innodb-tables/

DB

Backup all database.

mysqldump -uroot -p –all-database > 2014.sql mv /var/lib/mysql /var/lib/mysql.bak

Remove old mysql, mysql-server

yum list installed | grep -i mysql

yum remove mysql mysql-*

Download Yum Repository RPM Package

http://dev.mysql.com/downloads/repo/

Install RPM Package

rpm -ivh mysql-community-release-el5-5.noarch.rpm

Install mysql, mysql-server

yum install mysql mysql-server mysql-devel service mysqld start chkconfig mysqld on

Setting Password

mysqladmin -u root password

Import backup data

mysql -u root -p < ./2014.sql

For class_eval, the class scope also becomes the target object, so def creates instance methods for that class/module.

For instance_eval, the class scope becomes the singleton class (aka metaclass, eigenclass) of the target object. Instance methods created on the singleton class for an object become singleton methods for that object. Singleton methods for a class or module are what are commonly (and somewhat inaccurately) called class methods.

Reference

http://stackoverflow.com/questions/900419/how-to-understand-the-difference-between-class-eval-and-instance-eval

Backgound

有两个业务系统,业务人员每天都要进行大量操作。这两个系统不开放接口,唯有通过Geek的方法。

Tools

trixie, IE8下无效。

ie7pro, 附带的东西太多,需要配置,较麻烦。

crossider, 支持IE7+,傻瓜式安装。

最后选用crossider。

Functions

1. Get iframe document

IE的跟节点是contentWindow,而其他是contentDocument.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function get_frame_document(frame_id){
  var doc;
  var iframeObject = document.getElementById(frame_id);
  if(iframeObject == null){
   return undefined;
  }

  if (iframeObject.contentDocument) { // DOM
   doc = iframeObject.contentDocument;
  } 
  else if (iframeObject.contentWindow) { // IE win
   doc = iframeObject.contentWindow.document;
  }

  return doc;
}

2. Overwrite showModalDialog function

showModalDialog 打开的window无法访问其DOM,故重写该方法,使用window.open打开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var myIframeWin = get_frame_window('frameMain')
myIframeWin._showModalDialog = myIframeWin.showModalDialog;
myIframeWin.showModalDialog = function(url,dialogArguments,sFeatures){
  var regexp = new RegExp('my_pattern')
  if(!regexp.test(url)){
    return myIframeWin._showModalDialog(url,dialogArguments,sFeatures)
  }

  var w = myIframeWin.open(url, '_blank');
  w.dialogArguments = dialogArguments;
  w._close = w.close;
  w.close = function(){
    // do something;
    w._close();
  }

}

3. Child window pass variable to parent window

3.1 window.opener not recieve object
1
2
my_object = {}
window.opener.my_var = my_object;

parent window的my_var为undefined.

Solution:将JSON转换成String

1
window.opener.my_var = JSON.stringify(window.returnValue);
3.1 IE7 不支持 JSON.stringify

Solution: include JSON3 lib.

http://bestiejs.github.io/json3/

4. ajax sync not work.

Solution: load script by script tag.

1
2
3
4
function ajax_through_script(url, params){
  var dom = $('<script />').attr('src', url + '?'+params);
  dom.appendTo($('body'));
}