haml

最近做公司的项目,接触到了haml。真是耳目一新,语法太简洁了,页面代码量也少了,也不会出现标签不匹配的问题了,用起来非常爽!下面我来介绍如何在rails中使用haml

1. 创建测试工程:

$rails test_haml
$cd test_haml

2. 生成post模型:

$script/generate scaffold post title:string content:text
$rake db:migrate

3. 安装haml插件:

$script/plugin install git://github.com/nex3/haml.git

4. 修改view文件的erb为haml:

# app/views/layouts/posts.html.haml
!!! XML
!!!
%html{html_attrs('en')}
  %head
    %meta{'http-equiv' = 'content-type', :content = 'text/html;charset=UTF-8'}
    %title= Posts: #{controller.action_name}
    = javascript_include_tag :defaults
  %body
    %p{:style = color: green}= flash[:notice]
    = yield
# app/views/posts/index.html.haml
%h1 Listing posts

%table
  %tr
    %th Title
    %th Content

  - @posts.each do |post|
    %tr
      %td=h post.title
      %td=h post.content
      %td= link_to 'Show', post
      %td= link_to 'Edit', edit_post_path(post)
      %td= link_to 'Destroy', post, :confirm = 'Are you sure?', :method = :delete
%br

= link_to 'New post', new_post_path
# app/views/posts/show.html.haml
%p
  %b Title:
  =h @post.title

%p
  %b Content:
  =h @post.content

= link_to 'Edit', edit_post_path(@post)
=  |
= link_to 'Back', posts_path
# app/views/posts/new.html.haml
%h1 New post

- form_for(@post) do |f|
  = f.error_messages

  %p
    = f.label :title
    %br
    = f.text_field :title

  %p
    = f.label :content
    %br
    = f.text_area :content

  %p
    = f.submit 'Create'

= link_to 'Back', posts_path
# app/views/posts/edit.html.haml
%h1 Editing post

- form_for(@post) do |f|
  = f.error_messages

  %p
    = f.label :title
    %br
    = f.text_field :title

  %p
    = f.label :content
    %br
    = f.text_area :content

  %p
    = f.submit 'Update'

= link_to 'Show', @post
=  |
= link_to 'Back', posts_path

5. 用sass代替css:

$ mkdir public/stylesheets/sass
$ css2sass public/stylesheets/scaffold.css > public/stylesheets/sass/scaffold.sass

下面你就可以享受haml为你带来的高效啦!当然你完全可以找到生成haml的generator,这里只是展示一下haml的简洁而已

Posted in  rails plugins


counter_cache的migration

counter_cache在提升查询性能上带来了很大的帮助,在rails中用起来也是非常的方便,增加一个xxx_count在数据表中,在belongs_to的声明后增加:counter_cache => true。

但是今天在增加counter_cache的migration上却遇到了麻烦,比如增加Person的blog_posts_count,migration如下:

def self.up
  add_column :people, :blog_posts_count, ;integer, :default => 0

  Person.reset_column_information
  Person.all.each do |p|
    p.update_attribute(:blog_posts_count, p.blogs.posts.length)
  end
end

运行之后,发现blog_posts_count的值总是为null,即使在console下面运行person的update_attribute方法也没有任何作用,errors却是空的,再看sql语句,只更新了updated_at。试了很久都没有结果,google之后发觉原来是rails 2.0之后把counter_cache的attribute设置为read_only,怪不得怎么也改不了呢。rails 2.0提供了update_counters方法来修改counter_cache的column,于是migration改成了:

def self.up
  add_column :people, :blog_posts_count, ;integer, :default => 0

  Person.reset_column_information
  Person.all.each do |p|
    Person.update_counters p.id, :blog_posts_count, p.blogs.posts.length
  end
end

Posted in  rails


Rails源码分析——delegate

Delegate是一种应用composite来代替extend的机制,可以有效地降低代码的耦合性。

Rails 2.2增加了delegate方法,可以十分方便地实现delegate机制。来看看源码吧:

def delegate(*methods)
  options = methods.pop
  unless options.is_a?(Hash) && to = options[:to]
    raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
  end

  if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
    raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
  end

  prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"

  methods.each do |method|
    module_eval(<<-EOS, "(__DELEGATION__)", 1)
      def #{prefix}#{method}(*args, &block)
        #{to}.__send__(#{method.inspect}, *args, &block)
      end
    EOS
  end
end

delegate方法首先检查传入的参数,正确参数形式为:method1, :method2, ..., :methodN, :to => klass[, :prefix => prefix]

delegate要求参数的最后必须是一个Hash,:to表示需要代理的类,:prefix表示代理的方法是否要加前缀,如果:prefix => true,则代理的方法名为klass_method1, klass_method2, ..., klass_methodN,如果:prefix => prefix (prefix为string),则代理的方法名为prefix_method1, prefix_method2, ..., prefix_methodN。

最终通过module_eval动态生成每个方法定义。通过send方法调用:to类的方法。

来看看调用的例子:

简单的调用:

class Greeter  ActiveRecord::Base
  def hello()   "hello"   end
  def goodbye() "goodbye" end
end

class Foo  ActiveRecord::Base
  delegate :hello, :goodbye, :to => :greeter
end

Foo.new.hello   # => "hello"
Foo.new.goodbye # => "goodbye"

增加:prefix = true:

class Foo  ActiveRecord::Base
  delegate :hello, :goodbye, :to => :greeter, :prefix => true
end

Foo.new.greeter_hello   # => "hello"
Foo.new.greeter_goodbye # => "goodbye"

自定义前缀名:

class Foo  ActiveRecord::Base
  delegate :hello, :goodbye, :to => :greeter, :prefix => :foo
end

Foo.new.foo_hello   # => "hello"
Foo.new.foo_goodbye # => "goodbye"

ruby的动态性再一次发挥了强大的功能!

Posted in  rails


flex的RSL编译方式

flex编译出来swf文件往往很大,尤其有了图形界面,要想少于100K几乎是不可能的,但是在网页上加载这么大的flash又是很耗时的。

不过adobe提供了runtime share libraries机制,它允许客户端缓存flex类库,这样大大减少了网络数据的传输。

RSL分为两种,一种是签名的,只有Adobe的类库才能使用签名的方式,用的是framework_3.x.x.xxx.swz。只要客户端的flash版本大于等于9.0.115,就可以利用签名方式的RSL,类库将由flash来缓存;另一种是非签名的,用的是framework_3.x.x.xxx.swf。类库是由客户端的浏览器缓存的。

RSL的编译参数如下:

mxmlc test.mxml -runtime-shared-library-path=F:\FlashTools\flex3sdk\frameworks\libs\framework.swc,framework_3.1.0.2710.swz,,framework_3.1.0.2710.swf

本来test.mxml编译出来有140多K,现在只有50多K,减少了60%多哦!

Posted in  flex


flex的调试日志

Flash的debug是个头疼的问题,最好的方法是使用flash的logging来做日志调试。

要使用Flash的debug,首先要安装adobe的debug版flash,然后要配置mm.cfg,具体路径和配置方式见以下链接:http://livedocs.adobe.com/blazeds/1/blazeds_devguide/help.html?content=services_logging_2.html,之后就可以使用Flash的logging。

下面给个实例吧:

import mx.logging.*;
import mx.logging.targets.*;

private var myLogger : ILogger;
public function printLog(level:Number):void {
    if(level ==2) myLogger.debug("This is debug click");
    if(level == 4) myLogger.info("This is info click");
    if(level == 6) myLogger.warn("This is warn click");
    if(level == 8) myLogger.error("This is error click");
    if(level ==1000) myLogger.fatal("This is fatal click");
}

private function initLog():void {
    // Create a target.
    var logTarget:TraceTarget = new TraceTarget();
    logTarget.filters=["*"];
    logTarget.level = LogEventLevel.ALL;
    // Add date, time, category, and log level to the output.
    logTarget.includeDate = true;
    logTarget.includeTime = true;
    logTarget.includeCategory = true;
    logTarget.includeLevel = true;
    // Begin logging.
    Log.addTarget(logTarget);
    myLogger = Log.getLogger("test");
}

点击之后的结果如下:

1/4/2009 20:04:41.149 [FATAL] test This is fatal click
1/4/2009 20:04:41.740 [ERROR] test This is error click
1/4/2009 20:04:42.695 [WARN] test This is warn click
1/4/2009 20:04:43.283 [INFO] test This is info click
1/4/2009 20:04:44.056 [DEBUG] test This is debug click

Posted in  flex


Fork me on GitHub