乐者为王

Do one thing, and do it well.

在ListView中显示已安装应用的名字和包名

通过PackageManager获取手机中已安装应用的信息,具体代码如下:

1
2
PackageManager packageManager = this.getPackageManager();
List<PackageInfo> packageInfoList = packageManager.getInstalledPackages(0);

通过以上方法,可以得到所有已安装的应用程序,既包括了手动安装的应用程序的信息,也包括了系统预装的应用程序的信息。要区分这两类应用可使用以下方法:

  1. 从packageInfoList获取的packageInfo,再通过packageInfo.applicationInfo获取applicationInfo;
  2. 判断applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM的值,该值大于0时,表示获取的应用为系统预装的应用,反之则为手动安装的应用。

列表的显示需要三个元素:

  1. 用来显示数据的ListView;
  2. 需要显示的数据;
  3. 绑定数据和ListView的Adapter。

Adapter通常有ArrayAdapter、SimpleAdapter和CursorAdapter等。其中SimpleAdapter有最好的扩充性,可以自定义出各种效果。

1
SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)

参数解释:

  1. context:上下文;
  2. data:数据列表。列表里的每一项都是一个Map对象,都和ListView里的一项进行数据绑定;
  3. resource:布局资源。可以引用系统提供的,也可以自定义;
  4. from:名字数组。每个名字都是用来索引数据列表中每个Map里的Object;
  5. to:资源索引数组,以R.id.xxx的形式表示。

main.xml布局代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:id="@+id/app_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="22px" />
    <TextView
        android:id="@+id/app_package"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

上面代码中的android:orientation="vertical"很重要,没有它两个TextView就不会显示成两行。

继承了ListActivity的MainActivity代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.app.ListActivity;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.Bundle;
import android.widget.SimpleAdapter;

public class MainActivity extends ListActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        SimpleAdapter adapter = new SimpleAdapter(this,
                getInstalledApps(false),
                R.layout.main,
                new String[] {"app_title", "app_package"},
                new int[] {R.id.app_title, R.id.app_package});
        setListAdapter(adapter);
    }

    private List<HashMap<String, Object>> getInstalledApps(boolean getSysPackages) {
        List<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>();

        List<PackageInfo> pkgs = getPackageManager().getInstalledPackages(0);
        for (int i = 0; i< pkgs.size(); i++) {
            PackageInfo pkg = pkgs.get(i);
            if (!getSysPackages && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0) {
                continue;
            }
            String label = pkg.applicationInfo.loadLabel(getPackageManager()).toString();
            String version = pkg.versionName;
            String pkgName = pkg.packageName;

            HashMap<String, Object> map = new HashMap<String, Object>();
            map.put("app_title", label + " " + version);
            map.put("app_package", pkgName);
            list.add(map);
        }
        return list;
    }
}

构建支持OAuth的新浪微博桌面客户端

要使用OAuth,首先要去新浪微博注册一个自己的微博应用。注册之后,会得到微博应用的Consumer key和Consumer secret,都是一个字符串。之后就可以进行OAuth的认证过程了。

https://code.google.com/p/sina-weibo4j/ 下载新浪微博的Java SDK包,当然也可以在 http://open.weibo.com/wiki/index.php/SDK 下载。推荐使用前者,已经打好包了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Properties;
import java.util.PropertyResourceBundle;

import weibo4j.Weibo;
import weibo4j.WeiboException;
import weibo4j.http.AccessToken;
import weibo4j.http.RequestToken;

public class UpdateStatus {
  private static final String ACCESS_SECRET = "access_secret";
  private static final String ACCESS_TOKEN = "access_token";
  private static final String FILE_NAME = "sina_token.properties";

  public static void main(String[] args) {
    // 在这里填写在应用申请中得到的App Key和App Secret
    System.setProperty("weibo4j.oauth.consumerKey", YOUR_CONSUMER_KEY);
    System.setProperty("weibo4j.oauth.consumerSecret", YOUR_CONSUMER_SECRET);

    Weibo weibo = new Weibo();

    try {
      // 读取存储起来的Access Token
      AccessToken accessToken = loadAccessToken();
      if (accessToken == null) {
        String backUrl = "http://zhubabo.appspot.com";

        // 请求Request Token(未授权令牌)
        RequestToken requestToken = weibo.getOAuthRequestToken(backUrl);
        System.out.println("Got request token...");
        System.out.println("Request token: " + requestToken.getToken());
        System.out.println("Request token secret: " + requestToken.getTokenSecret());

        // 将以下打印出的授权链接在浏览器中打开,完成应用授权,会自动跳转到
        // 指定的callback url,并将oauth_verifier一起返回
        System.out.println("Open the following url in a browser");
        System.out.println(" http://api.t.sina.com.cn/oauth/authorize?oauth_token=" + requestToken.getToken());

        // 输入上面得到的oauth_verifier的值,取得Access Token,这个Token是长期有效的
        System.out.println("please input verifier:");
        String verifier = readLine();
        accessToken = weibo.getOAuthAccessToken(
            requestToken.getToken(),
            requestToken.getTokenSecret(),
            verifier);

        // 将Access Token保存下来,以后就可以直接通过此Token向新浪围脖发消息了
        storeAccessToken(accessToken.getToken(), accessToken.getTokenSecret());
      }

      weibo.setToken(accessToken.getToken(), accessToken.getTokenSecret());

      System.out.println("Input message to sina:");
      String message = readLine();
      while (!"exit".equals(message.trim())) {
        weibo.updateStatus(message);
        System.out.println("Input message to sina:");
        message = readLine();
      }
    } catch (WeiboException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  private static void storeAccessToken(String token, String tokenSecret) throws IOException {
    OutputStream out = new FileOutputStream(new File(FILE_NAME));
    Properties p = new Properties();
    p.setProperty(ACCESS_TOKEN, token);
    p.setProperty(ACCESS_SECRET, tokenSecret);
    p.store(out, "sina access token");
    out.flush();
    out.close();
  }

  private static AccessToken loadAccessToken() throws IOException {
    File f = new File(FILE_NAME);
    if (!f.exists()) {
      return null;
    }
    InputStream in = new FileInputStream(f);
    PropertyResourceBundle bundle = new PropertyResourceBundle(in);
    in.close();
    return new AccessToken(bundle.getString(ACCESS_TOKEN), bundle.getString(ACCESS_SECRET));
  }

  private static String readLine() throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    byte[] bs = reader.readLine().getBytes("gbk");
    return new String(bs);
  }
}

Rails 2.3和Paperclip实现多图片上传(改进版)

从Rails 2.3开始,有了新的方法来处理多模型表单,这就是accepts_nested_attributes_for,它允许直接赋值到子对象上,对于标准的属性使用相同的哈希格式。

例如,一个parent有多个children,包含所有类的POST数据将会是下面这样:

1
2
3
4
5
6
7
8
9
10
11
params =>
  action => update
  id => 1
  controller => parents
  parent =>
    first_name => John
    last_name => Doe
    age => 40
    children_attributes =>
      1 => { id => 16, :name => Jack }
      2 => { id => 18, :name => Mary }

注意,那个children_attributes元素在parent的元素中。fields_for会为这些子元素生成必要的POST数据,这些数据会被转换成方便accepts_nested_attributes_for解释的哈希。children_attributes不是数组而是哈希,它的键是一个简单的索引(不是模型的ID),被用来从一个单一的实体聚合属性。

不用ID的理由是简单的:正在被编辑的模型可能不会被保存,发送到了客户端,这时它的ID为nil。因此为每个被显示的children的id关联一个隐藏字段是个好的实践。

下面我们就用accepts_nested_attributes_for来改写上传多图片的代码。

修改albums_controller.rb中的new方法,删除下面的这句代码:

1
1.upto(3) { @album.photos.build }

将album.rb中的

1
2
3
4
5
def photo_attributes=(photo_attributes)
  photo_attributes.each do |attributes|
    photos.build(attributes)
  end
end

代码改成

1
accepts_nested_attributes_for :photos

修改_form.html.erb中的

1
2
3
4
5
6
7
<div id="photos">
  <% if @album.new_record? %>
    <%= render :partial => 'photo', :collection => @album.photos %>
  <% end %>
</div>

<%= link_to_function "Add Photo" do |page| page.insert_html :bottom, :photos, :partial => 'photo', :object => Photo.new end %>

为下面的代码

1
2
3
4
5
6
7
<div id="photos">
  <% if @album.new_record? %>
    <%= render :partial => 'photo', :locals => { :form => f, :photo => @album.photos.build } %>
  <% end %>
</div>

<%= add_object_link("Add Photo", f, @album.photos.build, "photo", "#photos") %>

再将_photo.html.erb中的

1
2
3
4
5
<% fields_for "album[photo_attributes][]", photo do |p| %>
  <%= p.label :photo %><br />
  <%= p.file_field :data, :index => nil %>
  <%= link_to_function "delete", "remove_field($(this), ('.photo'))" %>
<% end %>

改为

1
2
3
4
5
<% form.fields_for :photos, photo, :child_index => (photo.new_record? ? "index_to_replace_with_js" : nil) do |p| %>
  <%= p.label :photo %><br />
  <%= p.file_field :data %>
  <%= link_to_function "delete", "remove_field($(this), ('.photo'))" %>
<% end %>

在albums_helper中添加两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def add_object_link(name, form, object, partial, where)
  html = render(:partial => partial, :locals => { :form => form }, :object => object)
  link_to_function name, %{
    var new_object_id = new Date().getTime();
    var html = jQuery(#{js html}.replace(/index_to_replace_with_js/g, new_object_id)).hide();
    html.appendTo(jQuery("#{where}")).slideDown('slow');
  }
end

def js(data)
  if data.respond_to? :to_json
    data.to_json
  else
    data.inspect.to_json
  end
end

到这里就修改完成了。试试看,是不是和原来的效果一样。

Rails 2.3和Paperclip实现多图片上传

1
2
3
4
5
6
7
8
9
rails upload
cd upload
script/plugin install git://github.com/thoughtbot/paperclip.git
script/plugin install git://github.com/aaronchi/jrails.git
script/generate scaffold album name:string
script/generate model photo album:references
script/generate paperclip photo data
rake db:migrate
script/server

修改album.rb添加photo_attributes属性:

1
2
3
4
5
6
7
8
9
10
class Album < ActiveRecord::Base
  has_many :photos
  validates_presence_of :name

  def photo_attributes=(photo_attributes)
    photo_attributes.each do |attributes|
      photos.build(attributes)
    end
  end
end

修改photo.rb添加附件相关属性:

1
2
3
4
5
6
7
8
9
10
class Photo < ActiveRecord::Base
  belongs_to :album

  has_attached_file :data,
    :url => "/uploads/:style_:basename.:extension",
    :styles => { :thumb => "50x50#", :large => "640x480#" }
  validates_attachment_presence :data
  validates_attachment_content_type :data,
    :content_type => ['image/jpeg', 'image/jpg', 'image/png']
end

修改new.html.erb为以下代码:

1
2
3
4
5
6
7
8
<h1>New album</h1>

<% form_for @album, :html => { :multipart => true } do |f| %>
  <%= render :partial => 'form', :locals => { :f => f } %>
  <p><%= f.submit "Create" %></p>
<% end %>

<%= link_to 'Back', albums_path %>

对edit.html.erb做同样的修改:

1
2
3
4
5
6
7
8
9
<h1>Editing album</h1>

<% form_for @album, :html => { :multipart => true } do |f| %>
  <%= render :partial => 'form', :locals => { :f => f } %>
  <p><%= f.submit "Update" %></p>
<% end %>

<%= link_to 'Show', @album %> |
<%= link_to 'Back', albums_path %>

把从new.html.erb和edit.html.erb中抽取出来的代码保存为_form.html.erb文件:

1
2
3
4
5
6
<%= f.error_messages %>

<p>
  <%= f.label :name %>
  <%= f.text_field :name %>
</p>

然后在后面添加如下代码:

1
2
3
4
5
6
7
<div id="photos">
  <% if @album.new_record? %>
    <%= render :partial => 'photo', :collection => @album.photos %>
  <% end %>
</div>

<%= link_to_function "Add Photo" do |page| page.insert_html :bottom, :photos, :partial => 'photo', :object => Photo.new end %>

创建_photo.html.erb文件,代码如下:

1
2
3
4
5
6
7
8
9
<div class="photo">
  <p>
  <% fields_for "album[photo_attributes][]", photo do |p| %>
    <%= p.label :photo %><br />
    <%= p.file_field :data, :index => nil %>
    <%= link_to_function "delete", "remove_field($(this), ('.photo'))" %>
  <% end %>
  </p>
</div>

再在application.js中添加下列代码,这样就可以删除file字段了。

1
2
3
function remove_field(element, item) {
  element.up(item).remove();
}

然后修改albums_controller.rb中的new方法:

1
2
3
4
5
6
7
8
def new
  @album = Album.new
  1.upto(3) { @album.photos.build }

  respond_to do |format|
    format.html # new.html.erb
  end
end

这样,上传多文件的功能基本就完成了。下面就来实现显示和修改的功能。

在show.html.erb的末尾添加下列代码,上传成功后用来显示图片:

1
2
3
4
#loop through the albums photos
<% for photo in @album.photos %>
  <%= image_tag photo.data.url(:thumb) %>
<% end %>

修改albums_controller.rb中的edit和update方法,用来删除图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def edit
  @album = Album.find(params[:id])
  if @album.photos.first.nil?
    1.upto(3) { @album.photos.build }
  end
end

def update
  params[:photo_ids] ||= []
  @album = Album.find(params[:id])
  unless params[:photo_ids].empty?
    Photo.destroy_pics(params[:id], params[:photo_ids])
  end

  respond_to do |format|
    if @album.update_attributes(params[:album])
      flash[:notice] = 'Album was successfully updated.'
      format.html { redirect_to(@album) }
    else
      format.html { render :action => "edit" }
    end
  end
en

在photo.rb中加上下面的代码:

1
2
3
def self.destroy_pics(album, photos)
  Photo.find(photos, :conditions => { :album_id => album }).each(:destroy)
end

然后新建_album_photo.html.erb,代码如下:

1
2
3
4
<% unless album_photo.new_record? %>
  <%= image_tag album_photo.data.url(:thumb) %>
  <%= check_box_tag "photo_ids[]", album_photo.id %>
<% end rescue nil %>

接着在_form.html.erb中加入下面的代码就可以删除图片了:

1
2
3
<div class="album_photos">
  <%= render :partial => 'album_photo', :collection => @album.photos %>
</div>

在Rails应用中集成支付宝

注意:notify_url为服务器通知,支付宝可以保证99.9999%的通知到达率,前提是你的网络通畅,在notify_url中可以做对数据库的业务操作。return_url中可以做数据库的更新也可以做显示。第一次交易状态改变(即时到帐中的交易完成的交易状态)时,支付宝发起的通知的时间与返回页自动跳转回的时间近乎同时。

支付处理代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class OrdersController < ApplicationController
  before_filter :login_required

  # place order
  def place_order
    parameters = {
      'service' => 'create_direct_pay_by_user',
      'partner' => ALIPAY_ACCOUNT,
      'seller_email' => ALIPAY_EMAIL,
      'out_trade_no' => @order.out_trade_no,
      'subject' => 'payment subject',
      'body' => 'payment body',
      'price' => @order.price.to_s,
      'quantity' => @order.quantity.to_s,
      'payment_type' => '1',
      '_input_charset' => 'utf-8',
      'notify_url' => url_for(:only_path => false, :action => 'notify'),
      'return_url' => url_for(:only_path => false, :action => 'done')
    }

    # 即时到帐中交易状态为“等待买家付款”的状态默认是不会发送通知的,自己手动设置一下
    @order.status = 'WAIT_BUYER_PAY'
    @order.user = current_user
    @order.save

    values = {}
    # 支付宝要求传递的参数必须要按照首字母的顺序传递,所以这里要sort
    parameters.keys.sort.each do |k|
      values[k] = parameters[k];
    end

    # 一定要先unescape后再生成sign,否则支付宝会报ILLEGAL SIGN
    sign = Digest::MD5.hexdigest(CGI.unescape(values.to_query) + ALIPAY_KEY)
    gateway = 'https://www.alipay.com/cooperate/gateway.do?'
    redirect_to gateway + values.to_query + '&sign=' + sign + '&sign_type=MD5'
  end

  # 返回success或fail。如果返回fail,支付宝会每隔一段时间就自动调用notify_url通信接口
  def notify
    render :text => 'success'
  end

  def done
    if verify_sign
      order = Order.find_by_out_trade_no(params[:out_trade_no])
      # 支付宝即时到帐接口只有一种交易状态,就是“交易成功”,更新一下
      order.update_attributes(params[:trade_status])
      render :text => 'Payment successful'
    else
      render :text => 'Alipay Error: ILLEGAL_SIGN'
    end
  end

protected
  def verify_sign
    params.delete("sign_type")
    sign = params.delete("sign")

    values = {}
    params.keys.sort.each do |k|
      values[k] = params[k];
    end

    sign.downcase == Digest::MD5.hexdigest(CGI.unescape(values.to_query) + ALIPAY_KEY)
  end
end

使用wizardly插件创建multi-step wizard

wizardly是一个非常容易使用的创建multi-step wizard的Rails插件,只需要三步就可以。

安装插件:

1
2
gem install wizardly
script/generate scaffold user first_name:string last_name:string age:integer gender:boolean

第一步:

1
2
3
class User < ActiveRecord::Base
  validation_group :step1, :fields => [:first_name, :last_name]
  validation_group :step2, :fields => [:age, :gender]

第二步:

1
2
class UsersController < ApplicationController
  act_wizardly_for :user

第三步:

1
script/generate wizardly_scaffold users

现在你就可以通过访问http://localhost:3000/users/step1来查看效果了。

随机化Paperclip的上传文件名

有时我们希望可以设定上传文件名的格式。一是可以统一文件名,而不是一些乱七八糟的名字;二是可以让文件名不是那么容易被猜测出来。下面的这段代码就是在网上找到的:

1
2
3
4
5
6
7
8
9
10
11
class Photo < ActiveRecord::Base
  has_attached_file :image, :url => "/uploads/:basename.:extension"

  before_create :randomize_file_name

  private
  def randomize_file_name
    extension = File.extname(image_file_name).downcase
    self.image.instance_write(:file_name, "#{Time.now.strftime("%Y%m%d%H%M%S")}#{rand(1000)}#{extension}")
  end
end

代码是从 http://trevorturk.com/2009/03/22/randomize-filename-in-paperclip/ 找到的,这里我把随机参数给改了,这样文件名的格式就类似20110310095632768这样。还有要注意的就是在:url中必须使用:basename参数,因为修改的:file_name就是它。

Rails 2.3和Paperclip实现无刷新异步上传文件

1
2
3
4
5
6
7
rails upload
cd upload
script/plugin install git://github.com/thoughtbot/paperclip.git
script/plugin install git://github.com/markcatley/responds_to_parent.git
script/generate scaffold user name:string
script/generate paperclip user avatar
rake db:migrate

在user.rb中增加:

1
2
3
4
5
  has_attached_file :avatar, :url => "/uploads/:basename.:extension"
  validates_attachment_presence :avatar
  validates_attachment_content_type :avatar,
    :content_type => ['image/jpeg', 'image/jpg', 'image/png']
  validates_attachment_size :avatar, :less_than => 1.megabytes

修改views/users/new.html.erb文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<% form_for(@user, :url => users_path(:format => 'js'),
      :html => { :id => 'upload_form', :multipart => true, :target => 'uframe' }) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :avatar %><br />
    <%= f.file_field :avatar %>
    <div id="avatar">
    <% if @user.avatar.exists? %>
      <%= image_tag @user.avatar.url(:medium) %>
    <% end %>
    </div>
  </p>
  <p>
    <%= f.submit 'Create' %>
  </p>
<% end %>
<iframe id="uframe" name="uframe" style="display: none;"></iframe>

当点击submit按钮后,表单被提交到隐藏的iframe,相应的action被执行,执行结果会被返回给隐藏的iframe,我们需要上传后结果能返回到母窗口,这就是responds_to_parent插件提供的功能。

在views/layouts/user.html.erb中增加:

1
<%= javascript_include_tag :defaults %>

修改users_controller.rb中的create方法,增加:

1
2
3
4
5
6
7
8
9
format.js do
  flash[:notice] = 'avatar created'
  responds_to_parent do
    render :update do |page|
      page.replace_html :avatar, image_tag(@user.avatar.url(:medium))
      page['upload_form'].reset
    end
  end
end

restful_authentication实现用email登录

如果要使用用户名和email并行的登录方式,那么只要在models/user.rb中添加一行代码就可以了:

1
2
3
4
5
6
def self.authenticate(login, password)
  return nil if login.blank? || password.blank?
  u = find_by_login(login.downcase)  # need to get the salt
  u ||= find_by_email(login.downcase)  # 有了这行代码就可以使用email登录了
  u && u.authenticated?(password) ? u : nil
end

假如要使只能用email登录,就要先移除models/user.rb中的下列代码:

1
2
3
validates_presence_of :login
validates_length_of :login
validates_uniqueness_of :login

然后修改self.authenticate方法为:

1
2
3
4
5
def self.authenticate(email, password)
  return nil if email.blank? || password.blank?
  u = find_by_email(email.downcase)  # need to get the salt
  u && u.authenticated?(password) ? u : nil
end

接着将注册页面views/users/new.html.erb中的login块移除,还有就是要将相关邮件模板中的@user.login改为@user.email。

经过这些修改后就实现了只能使用email登录的功能。

uninitialized constant Typus::Authentication::ByPassword

使用的Rails版本是2.3.5,所以在安装typus的时候需要加上-r 2-3-stable。

1
script/plugin install git://github.com/fesplugas/typus.git -r 2-3-stable

在安装完后执行script/generate typus出现了下面的错误:

1
uninitialized constant Typus::Authentication::ByPassword

在网上找到的答案是因为和restful_authentication中Authentication模块有冲突,可以将app/models/user.rb中的:

1
2
3
include Authentication
include Authentication::ByPassword
include Authentication::ByCookieToken

改成

1
2
3
include ::Authentication
include ::Authentication::ByPassword
include ::Authentication::ByCookieToken

再次执行generator时提示:

1
undefined method 'login_regex' for Typus::Authentication:Module

经过再三查找,原来是user.rb文件中还有别的调用Authentication的地方,全部加上::符号,问题解决了。