Last active
December 16, 2015 02:39
-
-
Save rubyonrailsworks/5363985 to your computer and use it in GitHub Desktop.
2013年学习笔记
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Include vs. Extend | |
#You can either use include or extend to mix in a module’s functionality into a class. The difference is this: | |
#include makes the module’s methods available to the instance of a class, while | |
#extend makes these methods available to the class itself. | |
require "benchmark" | |
include Benchmark | |
require "fast_open_struct" | |
require "ostruct" | |
puts "Attribute lookups:" | |
bm 14 do |b| | |
b.report "OpenStruct" do | |
os = OpenStruct.new a: 1, b: 2, c: 3 | |
1_000_000.times do | |
os.a | |
os.b | |
os.c | |
end | |
end | |
b.report "FastOpenStruct" do | |
os = FastOpenStruct.new a: 1, b: 2, c: 3 | |
1_000_000.times do | |
os.a | |
os.b | |
os.c | |
end | |
end | |
end | |
#geek@me ~/software/201304/fast_open_struct[master]$ ruby -I./lib bench/attribute_lookup.rb | |
#Attribute lookups: | |
user system total real | |
OpenStruct 0.970000 0.000000 0.970000 ( 0.986715) | |
FastOpenStruct 0.370000 0.000000 0.370000 ( 0.378395) | |
#Quick Hash to a URL Query trick | |
"http://www.example.com?" + { language: "ruby", status: "awesome" }.to_query | |
# => "http://www.example.com?language=ruby&status=awesome" | |
Want to do it in reverse? Use CGI.parse: | |
require 'cgi' # Only needed for IRB, Rails already has this loaded | |
CGI::parse "language=ruby&status=awesome" | |
# => {"language"=>["ruby"], "status"=>["awesome"]} | |
#Rails’ index_by: the easy way to convert an Array to a Hash | |
Post.all.index_by { |post| post.id } | |
# => { 1 => #<Post ...>, 2 => #<Post ...>, ... } | |
Post.all.index_by(&:title) | |
# => { "My first post" => #<Post ...>, "My second post" => #<Post ...>, ... } | |
#Two Ruby tricks using method chaining and Procs | |
[1,2,3].each_with_object(1).map(&:+) | |
# => [2, 3, 4] | |
# Same outcome, even shorter | |
[1, 2, 3].map(&1.method(:+)) | |
# => [2, 3, 4] | |
#Enumerable#each_with_object | |
evens = (1..10).each_with_object([]) {|i, a| a << i*2 } | |
#=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] | |
%w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } | |
# => {'foo' => 'FOO', 'bar' => 'BAR'} | |
#Clear your development logs automatically when they are too large | |
#config/initializers/clear_logs.rb | |
if Rails.env.development? | |
MAX_LOG_SIZE = 2.megabytes | |
logs = File.join(Rails.root, 'log', '*.log') | |
if Dir[logs].any? {|log| File.size?(log).to_i > MAX_LOG_SIZE } | |
$stdout.puts "Runing rake log:clear" | |
`rake log:clear` | |
end | |
end | |
#Two ways of joining Array elements | |
[1, 2, 3].join('+') | |
# => "1+2+3" | |
Array#* has the same effect when you pass it a String: | |
[1, 2, 3] * '+' | |
# => "1+2+3" | |
#Quick shortcut to load a SQL file in Rails | |
rails db < path_to_sql_file.sql # Rails 3 | |
Web服务是一种服务导向架构的技术,通过标准的Web协议提供服务,目的是保证不同平台的应用服务可以互操作。 | |
根据W3C的定义,Web服务(Web service)应当是一个软件系统,用以支持网络间不同机器的互动操作。网络服务通常是许多应用程序接口(API)所组成的,它们透过网络,例如国际互联网(Internet)的远程服务器端,执行客户所提交服务的请求。 | |
尽管W3C的定义涵盖诸多相异且无法介分的系统,不过通常我们指有关于主从式架构(Client-server)之间根据SOAP协议进行传递XML格式消息。无论定义还是实现,WEB服务过程中会由服务器提供一个机器可读的描述(通常基于WSDL)以辨识服务器所提供的WEB服务。另外,虽然WSDL不是SOAP服务端点的必要条件,但目前基于Java的主流WEB服务开发框架往往需要WSDL实现客户端的源代码生成。一些工业标准化组织,比如WS-I,就在WEB服务定义中强制包含SOAP和WSDL。 | |
使用WEB服务的方式 | |
WEB服务实际上是一组工具,并有多种不同的方法调用之。三种最普遍的手段是:远程过程调用(RPC),面向服务架构(SOA)以及表述性状态转移(REST)。 | |
远程过程调用 | |
WEB服务提供一个分布式函数或方法接口供用户调用,这是一种比较传统的方式。通常,在WSDL中对RPC接口进行定义(类似于早期的XML-RPC)。 | |
尽管最初的WEB服务广泛采用RPC方式部署,但针对其过于紧密之耦合性的批评声也随之不断。这是因为RPC式WEB服务实质上是利用一个简单的映射,以把用户请求直接转化成为一个特定语言编写的函数或方法。如今,多数服务提供商认定此种方式在未来将难有作为,在他们的推动下,WS-I基本协议集(WS-I Basic Profile)已不再支 | |
服务导向架构 | |
主条目:服务导向架构 | |
现在,业界比较关注的是遵从服务导向架构(Service-oriented architecture,SOA)概念来构筑WEB服务。在服务导向架构中,通讯由消息驱动,而不再是某个动作(方法调用)。这种WEB服务也被称作面向消息的服务。 | |
SOA式WEB服务得到了大部分主要软件供应商以及业界专家的支持和肯定。作为与RPC方式的最大差别,SOA方式更加关注如何去连接服务而不是去特定某个实现的细节。WSDL定义了联络服务的必要内容。 | |
表述性状态转移 | |
表述性状态转移式(Representational state transfer,REST)WEB服务类似于HTTP或其他类似协议,它们把接口限定在一组广为人知的标准动作中(比如HTTP的GET、PUT、DELETE)以供调用。此类WEB服务关注与那些稳定的资源的互动,而不是消息或动作。 | |
此种服务可以通过WSDL来描述SOAP消息内容,通过HTTP限定动作接口;或者完全在SOAP中对动作进行抽象。 | |
简单对象访问协议(SOAP,全写为Simple Object Access Protocol)是交换数据的一种协议规范,使用在计算机网络Web服务(web service)中,交换带结构信息。SOAP为了简化网页服务器(Web Server)从XML数据库中提取数据时,节省去格式化页面时间,以及不同应用程序之间按照HTTP通信协议,遵从XML格式执行资料互换,使其抽象于语言实现、平台和硬件。此标准由IBM、Microsoft、UserLand和DevelopMentor在1998年共同提出,并得到IBM,莲花(Lotus),康柏(Compaq)等公司的支持,于2000年提交给万维网联盟(World Wide Web Consortium;W3C),目前 SOAP 1.1 版是业界共同的标准,属于第二代的XML协定(第一代具主要代表性的技术为XML-RPC以及WDDX)。 | |
用一个简单的例子来说明 SOAP 使用过程,一个 SOAP 消息可以发送到一个具有 Web Service 功能的 Web 站点,例如,一个含有房价信息的数据库,消息的参数中标明这是一个查询消息,此站点将返回一个 XML 格式的信息,其中包含了查询结果(价格,位置,特点,或者其他信息)。由于数据是用一种标准化的可分析的结构来传递的,所以可以直接被第三方站点所利用。 | |
SOAP 封装(envelop),它定义了一个框架,描述消息中的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理它们; | |
SOAP 编码规则(encoding rules),它定义了一种序列化的机制,用于表示应用程序需要使用的数据类型的实例; | |
SOAP RPC表示(RPC representation),它定义了一个协定,用于表示远程过程调用和应答; | |
SOAP 绑定(binding),它定义了SOAP使用哪种协议交换信息。使用HTTP/TCP/UDP协议都可以。 | |
把 SOAP 绑定到 HTTP 提供了同时利用 SOAP 的样式和分散的灵活性的特点以及 HTTP 的丰富的特征库的优点。在 HTTP 上传送 SOAP 并不是说 SOAP 会覆盖现有的 HTTP 语义,而是 HTTP 上的 SOAP 语义会自然的映射到 HTTP 语义。在使用 HTTP 作为协议绑定的场合中, RPC 请求映射到 HTTP 请求上,而 RPC 应答映射到 HTTP 应答。然而,在 RPC 上使用 SOAP 并不仅限于 HTTP 协议绑定。 | |
SOAP 消息必须用 XML 来编码 | |
SOAP 消息必须使用 SOAP Envelope 命名空间 | |
SOAP 消息必须使用 SOAP Encoding 命名空间 | |
SOAP 消息不能包含 DTD 引用 | |
SOAP 消息不能包含 XML 处理指令 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#git://github.com/ilmotta/to-csv.git | |
#lib/to_csv.rb | |
require RUBY_VERSION < '1.9' ? 'fastercsv' : 'csv' | |
require 'ostruct' | |
require 'active_support/core_ext' | |
require 'to_csv/csv_converter' | |
module ToCSV | |
def to_csv(options = {}, csv_options = {}, &block) | |
return '' if empty? | |
csv_converter = ToCSV::Converter.new(self, options, csv_options, &block) | |
csv_converter.to_csv | |
end | |
end | |
ActiveRecord::NamedScope::Scope.send(:include, ToCSV) if defined?(ActiveRecord::NamedScope::Scope) | |
class Array | |
remove_method :to_csv | |
extend ToCSV | |
include ToCSV | |
end | |
#lib/to_csv/csv_converter.rb | |
# encoding: UTF-8 | |
module ToCSV | |
CSVClass = RUBY_VERSION < '1.9' ? ::FasterCSV : ::CSV | |
class Converter | |
def initialize(data, options = {}, csv_options = {}, &block) | |
@opts = options.to_options.reverse_merge({ | |
:byte_order_marker => ToCSV.byte_order_marker, | |
:locale => ToCSV.locale || ::I18n.locale, | |
:primary_key => ToCSV.primary_key, | |
:timestamps => ToCSV.timestamps | |
}) | |
@opts[:only] = Array(@opts[:only]).map(&:to_s) | |
@opts[:except] = Array(@opts[:except]).map(&:to_s) | |
@opts[:methods] = Array(@opts[:methods]).map(&:to_s) | |
@data = data | |
@block = block | |
@csv_options = csv_options.to_options.reverse_merge(ToCSV.csv_options) | |
@associations = @opts[:include].kind_of?(Array) ? @opts[:include] : [@opts[:include]] if @opts[:include] | |
end | |
def to_csv | |
build_headers_and_rows | |
output = CSVClass.generate(@csv_options) do |csv| | |
csv << @header_row if @header_row.try(:any?) | |
@rows.each { |row| csv << row } | |
end | |
@opts[:byte_order_marker] ? "\xEF\xBB\xBF#{output}" : output | |
end | |
private | |
def build_headers_and_rows | |
send "headers_and_rows_from_#{ discover_data_type }" | |
end | |
def discover_data_type | |
test_data = @data.first | |
return 'ar_object' if instance_of_active_record? test_data | |
return 'hash' if test_data.is_a? Hash | |
return 'unidimensional_array' if test_data.is_a?(Array) && !test_data.first.is_a?(Array) | |
return 'bidimensional_array' if test_data.is_a?(Array) && test_data.first.is_a?(Array) && test_data.first.size == 2 | |
'simple_data' | |
end | |
def instance_of_active_record?(obj) | |
obj.class.base_class.superclass == ActiveRecord::Base | |
rescue Exception | |
false | |
end | |
def headers_and_rows_from_simple_data | |
@header_row = nil | |
@rows = [@data.dup] | |
end | |
def headers_and_rows_from_hash | |
@header_row = @data.first.keys if display_headers? | |
@rows = @data.map(&:values) | |
end | |
def headers_and_rows_from_unidimensional_array | |
@header_row = @data.first if display_headers? | |
@rows = @data[1..-1] | |
end | |
def headers_and_rows_from_bidimensional_array | |
@header_row = @data.first.map(&:first) if display_headers? | |
@rows = @data.map { |array| array.map(&:last) } | |
end | |
def headers_and_rows_from_ar_object | |
attributes = sort_attributes(filter_attributes(attribute_names)) | |
if display_headers? | |
@header_row = human_attribute_names(attributes) | |
@header_row |= human_attribute_names(headers_from_association_ar_object) if @opts[:include] | |
end | |
@rows = if @block | |
attributes_class = Struct.new(*attributes.map(&:to_sym)) | |
@data.map do |item| | |
@block.call(obj = attributes_class.new, item) | |
result = attributes.map { |attribute| obj[attribute] || try_formatting_date(item.send(attribute)) } | |
(result << rows_from_association_ar_object(item);result.flatten!) if @opts[:include] && @row_header_association | |
result | |
end | |
else | |
@data.map do |item| | |
result = attributes.map { |attribute| try_formatting_date item.send(attribute) } | |
(result << rows_from_association_ar_object(item);result.flatten!) if @opts[:include] && @row_header_association | |
result | |
end | |
end | |
end | |
def rows_from_association_ar_object(item) | |
@result = [] | |
@associations.each do |association| | |
@row_header_association = instance_variable_get("@row_header_association_for_#{from_sym_to_string(association)}") | |
association = association.to_sym | |
case item.class.reflect_on_association(association).macro | |
when :has_many, :has_and_belongs_to_many | |
records = item.send(association).to_a | |
unless records.empty? | |
records.collect do |record| | |
@row_header_association.map do |attribute| | |
@result << try_formatting_date(record.send(attribute)) | |
end | |
end | |
end | |
when :has_one, :belongs_to | |
if record = item.send(association) | |
@row_header_association.map do |attribute| | |
@result << try_formatting_date(record.send(attribute)) | |
end | |
end | |
end | |
end | |
@result.flatten | |
end | |
def headers_from_association_ar_object | |
@header_association = [] | |
@associations.each do |association| | |
association = association.to_sym | |
case @data.first.class.reflect_on_association(association).macro | |
when :has_many, :has_and_belongs_to_many | |
records = @data.first.send(association).to_a | |
unless records.empty? | |
instance_variable_set("@row_header_association_for_#{from_sym_to_string(association)}", records.first.attribute_names.map(&:to_s)) | |
@row_header_association = instance_variable_get("@row_header_association_for_#{from_sym_to_string(association)}") | |
(1..records.size).each do |record| | |
@row_header_association.map do |header| | |
@header_association << "#{from_sym_to_string(association)}_#{record}_#{header}" | |
end | |
end | |
end | |
when :has_one, :belongs_to | |
instance_variable_set("@row_header_association_for_#{from_sym_to_string(association)}", @data.first.send(association).attribute_names.map(&:to_s)) | |
@row_header_association = instance_variable_get("@row_header_association_for_#{from_sym_to_string(association)}") | |
@data.first.send(association).attribute_names.map do |header| | |
@header_association << "#{from_sym_to_string(association)}_#{header}" | |
end | |
end | |
end | |
@header_association.flatten | |
end | |
def display_headers? | |
@opts[:headers].nil? || (Array(@opts[:headers]).any? && Array(@opts[:headers]).all? { |h| h != false }) | |
end | |
def human_attribute_names(attributes) | |
@opts[:locale] ? translate(attributes) : humanize(attributes) | |
end | |
def humanize(attributes) | |
attributes.map(&:humanize) | |
end | |
def translate(attributes) | |
::I18n.with_options :locale => @opts[:locale], :scope => [:activerecord, :attributes, @data.first.class.to_s.underscore] do |locale| | |
attributes.map { |attribute| locale.t(attribute, :default => attribute.humanize) } | |
end | |
end | |
def try_formatting_date(value) | |
is_a_date?(value) ? value.to_s(:db) : value | |
end | |
def is_a_date?(value) | |
value.is_a?(Time) || value.is_a?(Date) || value.is_a?(DateTime) | |
end | |
def primary_key_filter(attributes) | |
return attributes if @opts[:primary_key] | |
attributes - Array(@data.first.class.primary_key.to_s) | |
end | |
def timestamps_filter(attributes) | |
return attributes if @opts[:timestamps] | |
return attributes if (@opts[:only] + @opts[:except]).any? { |attribute| timestamps.include? attribute } | |
attributes - timestamps | |
end | |
def timestamps | |
@timestamps ||= %w[ created_at updated_at created_on updated_on ] | |
end | |
def methods_filter(attributes) | |
attributes | @opts[:methods] | |
end | |
def only_filter(attributes) | |
return attributes if @opts[:only].empty? | |
attributes & @opts[:only] | |
end | |
def except_filter(attributes) | |
attributes - @opts[:except] | |
end | |
def attribute_names | |
@data.first.attribute_names.map(&:to_s) | |
end | |
def filter_attributes(attributes) | |
attributes = methods_filter(attributes) | |
attributes = primary_key_filter(attributes) | |
attributes = timestamps_filter(attributes) | |
attributes = @opts[:only].any?? only_filter(attributes) : except_filter(attributes) | |
attributes | |
end | |
def sort_attributes(attributes) | |
attributes = attributes.map(&:to_s).sort | |
return attributes if @opts[:headers].nil? | |
headers = Array(@opts[:headers]).map(&:to_s) | |
headers.delete_if { |attribute| attribute == 'false' } | |
if index = headers.index('all') | |
(headers & attributes).insert(index, (attributes - headers)).flatten | |
else | |
headers + (attributes - headers) | |
end | |
end | |
def from_sym_to_string(sym) | |
sym.to_s.gsub(':','') | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment