A MarkDown TextEditor Jun 28
网上找了半天没有找到相关的markdown texteditor, 于是自己写了一个gem, 方便与rails3 集成。
网上找了半天没有找到相关的markdown texteditor, 于是自己写了一个gem, 方便与rails3 集成。
Support: Ruby1.9.2/Rails3/Mongoid2.0.2/MongoDB 1.8.1
#
# lib/mongoid/counter_cache.rb
# ruby
#
# Created by Zhang Alex on 2011-06-17.
# Copyright 2011 __ZhangHanDong__. All rights reserved.
#
# ===================================
# class Forum
# references_many :topics
# references_many :posts
# end
#
#
# class Topic
# referenced_in :forum
# include Mongoid::CounterCache
# counter_cache name: :forum, inverse_of: :posts
# end
# ===================================
module Mongoid
module CounterCache
extend ActiveSupport::Concern
module ClassMethods
def counter_cache(metadata)
counter_name = "#{metadata[:inverse_of]}_count"
set_callback(:create, :after) do |document|
relation = document.send(metadata[:name])
if relation
relation.inc(counter_name.to_sym, 1) if relation.class.fields.keys.include?(counter_name)
end
end
set_callback(:destroy, :after) do |document|
relation = document.send(metadata[:name])
if relation && relation.class.fields.keys.include?(counter_name)
relation.inc(counter_name.to_sym, -1)
end
end
end
end #ClassMethods
end #CounterCache
end #Mongoid
项目运行在Ruby1.9.2下,使用了mongoid, 启动的时候,发现无法读取yml配置文件,老是报错,类似:Psych::SyntaxError
在google group里,发现了解决办法:
#在config/boot.rb里添加
require 'yaml'
YAML::ENGINE.yamler= 'syck'
具体原因,可能是RubyGems引起的:
Rubygems 1.5.0 changes the yaml parsing default from syck
to psych and psych doesn't like the ":<<" in yaml files.
官网: http://pow.cx
Pow很不稳定,有时候会出问题,变得无法工作, 解决方法:
使用powder管理pow. 到项目根目录
$> powder restart
如果还不行, 就log out系统。
如果再不行, 就重启系统。
Powder的使用: 安装
gem install powder $> powder --help
如果你的pow抛出这样的错误: Error: unknown process error ... ...
则可能是你项目出了问题, 请你仔细检查,或者启用webrick或者其他服务器,查看日志。
因为pow不会抛出类似的日志。
碰到个奇怪的问题:
#views/
= f.input :typee, :collection => link_collect, :label => false, :prompt => "..."
#link_collect helper method
def link_collect
[['web', 1], ['wap', 2], ['iphone', 3], ['android', 4]]
end
这个prompt无论如何都不工作 。。。
因为用了simple_form还以为是simple_form的问题呢,就跑github的simple_form提了一个issuse , 直到rafaelfranca的解答,我才恍然大悟。
原来是这个字段有默认值,所以令prompt失效。
private
def add_options(option_tags, options, value = nil)
...
if value.blank? && options[:prompt]
...
end
option_tags.html_safe
end
看来有些问题,想偷懒还无法解决,只能去源代码中找答案。
项目里经常会遇到多个model里有相似的方法,或者是公用的代码,我们一直遵循DRY的原则, 我们要发挥懒惰的积极一面 - 少写代码。 把这些公用,相似的代码都放到一个地方,一处修改,多处更新,便于维护。下面拿项目里碰的例子来说明:
下面这三个model是相似代码:
class AppRankDay < ActiveRecord::Base
# 包含了一个通用的search方法
include General::Models::AppRankGeneral
define_search_and_sort_method_for_app_rank
end
class AppRankWeek < ActiveRecord::Base
# 包含了一个通用的search方法
include General::Models::AppRankGeneral
define_search_and_sort_method_for_app_rank
end
class AppRankMonth < ActiveRecord::Base
# 包含了一个通用的search方法
include General::Models::AppRankGeneral
define_search_and_sort_method_for_app_rank
end
它们都包含了相似的search方法和sort方法。
还有其他model, 只用到了缓存相关的代码,并没有search和sort方法,比如:
class App < ActiveRecord::Base
include General::Models
end
class Subject < ActiveRecord::Base
include General::Models
end
我们通过自定义一个module来实现上面的代码DRY:
#general.rb
module General
module Models
extend ActiveSupport::Concern
included do
acts_as_cached :ttl => 30.minutes
after_save :reset_cache
after_destroy :expire_cache
paginates_per 30
end
module ClassMethods
#TODO
end#ClassMethods
module InstanceMethods
#TODO
end#module InstanceMethods
# module AppRankGeneral
module AppRankGeneral
extend ActiveSupport::Concern
include ::General::Models
included do
paginates_per 200
end
module ClassMethods
#TODO
def define_search_and_sort_method_for_app_rank
singleton_class.class_eval do #这里是动态定义类方法
define_method("search") do |keywords|
...
end
define_method("sort") do |sort_ids|
...
end
end#singleton class end
end
end#module ClassMethod
module InstanceMethods
#TODO
end#module InstanceMethods
end#AppRankGeneral
end#Models
end#General
说明: extend ActiveSupport::Concern是Rails3里实现的方法,相当于我们之前经常用的, 具体可以去看API:
def self.included(base)
base.send :include, InstanceMethods
base.send :extend, ClassMethods
end
Rails3 中使用Ajax POST请求, 不会自动加AUTH_TOKEN, 需要我们自己添加, 添加方法:
layout/application.html.haml
= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery?
application.js
jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})
jQuery(document).ready(function() {
// UJS authenticity token fix: add the authenticy_token parameter
// expected by any Rails POST request.
jQuery(document).ajaxSend(function(event, request, settings) {
// do nothing if this is a GET request. Rails doesn't need
// the authenticity token, and IE converts the request method
// to POST, just because - with love from redmond.
if (settings.type == 'GET') return;
if (typeof(AUTH_TOKEN) == "undefined") return;
settings.data = settings.data || "";
settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN);
});
});
介绍:
Rails 3.1 集成了CoffeeScript和JQuery, SASS. 所以CoffeeScript的学习是必要的。 CoffeeScript是纯Ruby实现的一种编程语言,通过编译生成相应的js文件。
安装:
sudo npm install coffee-script
用法:
安装好CoffeeScript,你就可以直接使用coffee命令(需要设置环境变量 - /node_modules下面)来进入REPL来学习语法。
coffee命令接受下列参数:
-c, --compile 把一个.coffee文件编译为同名的.js文件
-i, --interactive 启动一个CoffeeScript会话交互界面,你可以执行一些代码片段。如果使用了rlwrap, 使用起来会更棒。
-o, --output [DIR] 输出指定目录的所有编译过的js文件,与--compile或--watch联合使用。
-j, --join 把所有要编译的脚本按顺序连在一起,然后再编译。这在构建一个大的projects时很有用。
-w, --watch 查看coffee-script脚本修改次数,重编译次数。
-p, --print 直接把JavaScript输出到终端而不生成js文件。
-l, --lint 如果安装了jsl(JavaScript Lint)命令, 用它来检查一个CoffeScript文件的编译。(和--watch联合使用)
译者注:在mac下安装jsl, 需要把jsl.conf置于(/node.js/lib/node_modules/npm/node_modules/coffee-script/lib/../extras/jsl.conf)
-s, --stdio 没看懂,保留原文
Pipe in CoffeeScript to STDIN and get back JavaScript over STDOUT. Good for use with processes written in other languages. An example: cat src/cake.coffee | coffee -sc
-e, --eval 直接在命令行编译和打印一些小代码片段,例如:
coffee -e "puts num for num in [10..1]"
-r, --require 在编译和执行你脚本直接load一个lib库。能被用于编译器的hook(例如增加Growl消息通知)
-b, --bare 编译为没有顶级安全包装函数的JavaScript(一般用于CoffeeScript作为Node.js模块的时候)
-t, --tokens 词法分析,打印出一堆如下的token stream:
[IDENTIFIER square] [ASSIGN =] [PARAM_START (] ...
-n, --nodes 比楼上的高级,词法分析然后解析为解析树
Expressions
Assign
Value "square"
Code "x"
Op *
Value "x"
Value "x"
--nodejs 不要和上面的混淆。
The node executable has some useful options you can set, such as
--debug and --max-stack-size. Use this flag to forward options directly to Node.js.
下面是一些例子:
Compile a directory tree of .coffee files into a parallel tree of .js, in lib:
coffee -o lib/ -c src/
Watch a file for changes, and recompile it every time the file is saved:
coffee --watch --compile experimental.coffee
Concatenate a list of files into a single script:
coffee -o lib/ --join --compile src/*.coffee
coffee -bpe "alert i for i in [0..10]"
coffee
语言参考:
Functions 函数是通过一个可选参数的一对括号,一个箭头,和一个函数体。例如:
#CoffeeScript:
square = (x) -> x * x
cube = (x) -> square(x) * x
#JavaScript:
var cube, square;
square = function(x) {
return x * x;
};
cube = function(x) {
return square(x) * x;
};
函数的参数也可以有默认值。
#CoffeeScript:
fill = (container, liquid = "coffee") ->
"Filling the #{container} with #{liquid}..."
JavaScript:
var fill;
fill = function(container, liquid) {
if (liquid == null) {
liquid = "coffee";
}
return "Filling the " + container + " with " + liquid + "...";
};
(是不是感觉和Ruby用法差不多?)
Objects and Arrays CoffeeScript的对象和数组字面量和它的JavaScript表哥很相似。对象的创建和YAML类似:
#CoffeeScript:
song = ["do", "re", "mi", "fa", "so"]
singers = {Jagger: "Rock", Elvis: "Roll"}
bitlist = [
1, 0, 1
0, 0, 1
1, 1, 0
]
kids =
brother:
name: "Max"
age: 11
sister:
name: "Ida"
age: 9
#JavaScript:
var bitlist, kids, singers, song;
song = ["do", "re", "mi", "fa", "so"];
singers = {
Jagger: "Rock",
Elvis: "Roll"
};
bitlist = [1, 0, 1, 0, 0, 1, 1, 1, 0];
kids = {
brother: {
name: "Max",
age: 11
},
sister: {
name: "Ida",
age: 9
}
};
Lexical Scoping and Variable Safety
#CoffeeScript:
outer = 1
changeNumbers = ->
inner = -1
outer = 10
inner = changeNumbers()
#JavaScript:
var changeNumbers, inner, outer;
outer = 1;
changeNumbers = function() {
var inner;
inner = -1;
return outer = 10;
};
inner = changeNumbers();
未完 。。。
为项目增加submodule:
git submodule add git://github.com/ZhangHanDong/use_db.git vendor/plugin/use_db
删除submodule:
git rm --cache vendor/plugin/use_db
rm -rf vendor/plugin/use_db
使用:
git submodule init
git submodule update
豆瓣读书笔记, 点这里
昨天,突然想知道Ruby 内建的include?方法是否是基于二分查找算法实现的,于是就找来了源码看了看:
# Ruby BuildIn include?实现:
/*
* call-seq:
* ary.include?(obj) -> true or false
*
* Returns <code>true</code> if the given object is present in
* +self+ (that is, if any object <code>==</code> <i>anObject</i>),
* <code>false</code> otherwise.
*
* a = [ "a", "b", "c" ]
* a.include?("b") #=> true
* a.include?("z") #=> false
*/
VALUE
rb_ary_includes(VALUE ary, VALUE item)
{
long i;
for (i=0; i<RARRAY_LEN(ary); i++) {
if (rb_equal(RARRAY_PTR(ary)[i], item)) {
return Qtrue;
}
}
return Qfalse;
}
然后发现, 内建的include?只是简单的循环查找(当然,优势在于支持任何类型元素的Array)。于是就用二分查找实现了一个类似的功能(局限:只能是可排序对象组成的数组 - 因为要求有可比性):
class Array
def binary_search_index(e, l = 0, u = length - 1)
return if l>u
m=(l+u)/2
begin
e.to_s < self[m].to_s ? u=m-1 : l=m+1
e == self[m] ? m : binary_search_index(e,l,u)
rescue
nil
end
end
def b_include? e
begin
temp = self.sort_by{|i| i.to_s}
!temp.binary_search_index(e).nil?
rescue
return false
end
end
end
然后我们和Ruby内建的include?做一个性能比较:
arr = (1..100000000).to_a
Benchmark.bm do |x|
x.report{ arr.include?(99999999) }
x.report{ arr.b_include?(99999999) }
end
# 性能检测:
# user system total real
# include? 7.640000 0.260000 7.900000 ( 8.581378)
# b_include? 0.000000 0.000000 0.000000 ( 0.000142)
背景:
在项目开发中,有时候会碰到一些需求:比如,Rails项目中, 要对params做一些处理,但是不能影响到params本身的数据。这个时候,你也许会这样:
params_clone = params
params_clone.delete(:controller)
然后你拿着 params_clone 就去用了, 殊不知, 你拿的还是params这个对象,在你修改params_clone的同时,也修改了params.
Ruby 1.8 (包括Ruby1.9) 中, =赋值符, 只是把引用指向另一个变量:
a = [1, [1, 1]]
b = a
这里, a 和 b 实际指向的是同一个对象。
a.object_id == b.object_id #=> true
当你意识到这个问题的时候, 你可能会想, 那拷贝一个对象不就完了:
b = a.dup
a.object_id == b.object_id #=> false
你会说,这次是2个对象了吧。但是,打住吧,请看:
b[1][0] = 2
puts b #=> [1, [2, 1]]
puts a #=> [1, [2, 1]]
a也跟着变, 纳闷不?
a[0]保存的是 1的地址, a[ 1 ]保存的是[1,1]的地址, 和 b[ 1 ]指向的是同一个对象, 而dup方法只拷贝了一层。所以不靠谱。
注:**dup vs clone**
都是浅拷贝。 dup只会复制内容, 而clone会连同单例方法一并复制.
ruby字面量(Fixnum,true,false,nil,Symbol)不可以调用dup和clone方法,会报TypeError。
对对象的state(taint,frozen)的改变:dup会把frozen的对象unfrozen,clone不会。
那么如何做比较靠谱呢:
Ruby提供了一个持久化库(Marshal), 提供了两个方法:一个是dump方法,可以导出对象实例obj的持久化数据;另一个是load方法,可以根据持久化的数据,重新还原出了一个对象实例。
于是,我们可以这样:
b = Marshal.load(Marshal.dump(a))
b[1][0] = 2
puts b #=> [1, [2, 1]]
puts a #=> [1, [1, 1]]
a没受影响。
但是,Marshal的局限性是, 如果一个对象需要被还原,那么解释器必须能够找到这个对象的定义。同时,解释器找到的这个对象的类的定义必须和原来的定义相同,否则还原出的对象将不是原来保存的对象。
下面是深层拷贝的一些代码:
clone.rb 来自一位日本朋友
#!ruby
class Object
def deep_clone
_deep_clone({})
end
protected
def _deep_clone(cloning_map)
return cloning_map[self] if cloning_map.key? self
cloning_obj = clone
cloning_map[self] = cloning_obj
cloning_obj.instance_variables.each do |var|
val = cloning_obj.instance_variable_get(var)
begin
val = val._deep_clone(cloning_map)
rescue TypeError
next
end
cloning_obj.instance_variable_set(var, val)
end
cloning_map.delete(self)
end
end
class Array
protected
def _deep_clone(cloning_map)
return cloning_map[self] if cloning_map.key? self
cloning_obj = super
cloning_map[self] = cloning_obj
cloning_obj.map! do |val|
begin
val = val._deep_clone(cloning_map)
rescue TypeError
#
end
val
end
cloning_map.delete(self)
end
end
class Hash
protected
def _deep_clone(cloning_map)
return cloning_map[self] if cloning_map.key? self
cloning_obj = super
cloning_map[self] = cloning_obj
pairs = cloning_obj.to_a
cloning_obj.clear
pairs.each do |pair|
pair.map! do |val|
begin
val = val._deep_clone(cloning_map)
rescue TypeError
#
end
val
end
cloning_obj[pair[0]] = pair[1]
end
cloning_map.delete(self)
end
end
因为图片太大, 加个链接吧!HTTP状态码思维导图