乐者为王

Do one thing, and do it well.

改写Rails 2.x generator到Rails 3.x时的一些记录

因为有个基于Rails 2.x的项目用到了feedback插件,在把项目迁移到Rails 3.x版本后,需要让feedback也支持Rails 3.x。但是原作者早已不再更新该插件,所以只能自己动手。花了两天时间查找资料,修改代码,终于完成。

Rails 3.x中生成generator可用下面的命令:

1
2
3
4
5
rails g generator feedback_form
      create  lib/generators/feedback_form
      create  lib/generators/feedback_form/feedback_form_generator.rb
      create  lib/generators/feedback_form/USAGE
      create  lib/generators/feedback_form/templates
1
2
3
class InitializerGenerator < Rails::Generators::NamedBase
  source_root File.expand_path("../templates", __FILE__)
end

生成基类也从Rails::Generators::Base变成了Rails::Generators::NamedBase。它们的区别是Rails::Generators::NamedBase期望至少一个参数。

USAGE文件里的内容是输入-h选项时显示的文档:

1
2
3
rails generate feedback_form --help
Usage:
  rails generate feedback_form NAME [options]

Rails 2.x中的manifest可以删除了,因为在3.x的generator中每个public方法在生成过程中会被自动调用。

用Rails 3.2打造简单记账应用

Rails已经到3.2版本了,和以前的2.3版本有了很大的不同,决定把原来老版本的记账应用重新写一遍,更新部分插件。

首先是创建项目:

1
2
3
4
rails new qianbao
cd qianbao
rails g scaffold entry amount:decimal tags:string comment:text effective_date:date
rails g controller home index

删除public/index.html,在config/routes.rb中添加根路由:

1
root :to => 'home#index'

现在要给应用加上一个认证系统(注册、激活、登录、登出),这次使用devise插件实现。

在Gemfile中添加:

1
gem 'devise'

然后执行下列命令:

1
2
3
4
bundle install
rails g devise:install
rails g devise user
rails g devise:views

创建一个迁移任务,把User和Entry模型关联起来:

1
2
3
4
5
6
7
class AddUserIdToEntries < ActiveRecord::Migration
  def change
    add_column :entries, :user_id, :integer

    add_index :entries, :user_id
  end
end

修改Entry和User模型:

1
2
3
4
5
6
7
8
9
class Entry < ActiveRecord::Base
  validates :effective_date, :presence => true
  validates :amount,         :presence => true,
                             :numericality => { :greater_than => 0.0 },
                             :format => { :with => /^\d+??(?:\.\d{0,2})?$/ }
  validates :tags,           :presence => true,
                             :length => { :within => 1..255 }

  belongs_to :user
1
2
class User < ActiveRecord::Base
  has_many :entries

修改app/views/layouts/application.html.erb文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
  <% if user_signed_in? %>
    <p>
      <strong><%= link_to current_user.email, edit_user_registration_path %></strong>
      <%= link_to 'Logout', destroy_user_session_path, :method => :delete %>
    </p>
    <%= link_to "All Entries", entries_path %>
  <% else %>
    <p>
      <strong>You are currently not logged in.</strong>
      <%= link_to 'Sign in', new_user_session_path %> or
      <%= link_to 'Sign up', new_user_registration_path %>
    </p>
  <% end %>

  <%= yield %>
</body>

修改app/controller/entries_controller.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
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
class EntriesController < ApplicationController
  before_filter :authenticate_user!

  # GET /entries
  # GET /entries.json
  def index
    @entries = current_user.entries

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @entries }
    end
  end

  # GET /entries/1
  # GET /entries/1.json
  def show
    @entry = current_user.entries.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @entry }
    end
  end

  # GET /entries/new
  # GET /entries/new.json
  def new
    @entry = Entry.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @entry }
    end
  end

  # GET /entries/1/edit
  def edit
    @entry = current_user.entries.find(params[:id])
  end

  # POST /entries
  # POST /entries.json
  def create
    @entry = Entry.new(params[:entry])
    @entry.user = current_user

    respond_to do |format|
      if @entry.save
        format.html { redirect_to @entry, notice: 'Entry was successfully created.' }
        format.json { render json: @entry, status: :created, location: @entry }
      else
        format.html { render action: "new" }
        format.json { render json: @entry.errors, status: :unprocessable_entity }
      end
    end
  end

  # PUT /entries/1
  # PUT /entries/1.json
  def update
    @entry = current_user.entries.find(params[:id])

    respond_to do |format|
      if @entry.update_attributes(params[:entry])
        format.html { redirect_to @entry, notice: 'Entry was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @entry.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /entries/1
  # DELETE /entries/1.json
  def destroy
    @entry = current_user.entries.find(params[:id])
    @entry.destroy

    respond_to do |format|
      format.html { redirect_to entries_url }
      format.json { head :no_content }
    end
  end
end

执行以下命令启动应用:

1
2
rake db:migrate
rails s

以前的版本用的CSS框架是Blueprint,这次使用比它更方便快捷的Bootstrap。Bootstrap建立在Less上,有个less-rails-bootstrap,不过在安装libv8时竟然要提示缺少Python,拜托!我用的是Ruby,要我装Python,啥意思啊!还是找个Sass版的Bootstrap吧。

下载sass-twitter-bootstrap

1
git clone https://github.com/jlong/sass-twitter-bootstrap.git

将sass-twitter-bootstrap/lib中的文件拷贝到app/assets/stylesheets/twitter目录下。然后将app/assets/stylesheets/application.css中的

1
*= require_tree .

修改为

1
*= require twitter/bootstrap

再在文件末尾添加:

1
body { padding-top: 60px; }

现在就可以使用Bootstrap来布局美化应用了。

使用showDialog()创建的ProgressDialog再次打开时进度条不变化

先来看段代码:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Copy Contacts"
        android:onClick="copyContacts" />
</LinearLayout>
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
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;

public class DialogActivity extends Activity {
    private static final int COPY_CONTACTS = 0;

    private ProgressDialog mProgressDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    public void copyContacts(View target) {
        showDialog(COPY_CONTACTS);
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case COPY_CONTACTS:
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setTitle("Copy contacts");
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mProgressDialog.setMax(100);
            new AsyncCopyContactsTask().execute();
            break;
        }
        return mProgressDialog;
    }

    private class AsyncCopyContactsTask extends AsyncTask<Void, Void, Integer> {
        @Override
        protected Integer doInBackground(Void... params) {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
                publishProgress();
            }
            return 0;
        }

        protected void onProgressUpdate(Void... progress) {
            mProgressDialog.incrementProgressBy(1);
        }

        protected void onPostExecute(Integer result) {
            mProgressDialog.dismiss();
        }
    }
}

第一次点击按钮会显示一个进度条,上面的数字处于变动当中,到达最大值后进度条会消失不见;再次点击按钮后,就会发现进度条上的数值和消失前相同,并且不再变化,而且进度条也不会消失。这是为什么呢?

Activity中有三个和对话框显示有关的方法:showDialog(),dismissDialog()和removeDialog()。其中showDialog()用于显示一个对话框;dismissDialog()使对话框消失,但仍然处于内存中,只是不显示而已,如果再次调用showDialog()方法,则缓存在内存中的对话框会重新显示,而不需要重新创建。removeDialog()使对话框消失,并从内存中将对话框清除,如果再次调用showDialog()来显示它,则在显示之前需要重新创建对话框。

在一个对话框的声明周期内,onCreateDialog()仅被调用一次,但onPrepareDialog()方法会每次被调用。比如,第一次调用 showDialog()方法显示某个对话框时,会先触发onCreateDialog()的执行,然后再触发onPrepareDialog()方法;如果将某个对话框 removeDialog()后再showDialog(),也会先触发onCreateDialog(),接着触发onPrepareDialog()方法;如果某个对话框是被dismiss()掉的,再调用 showDialog()方法来显示它时就不会触发onCreateDialog()方法的执行,只会触发onPrepareDialog()方法。

这是因为第一次调用showDialog()时,Android从你的Activity中调用onCreateDialog()方法,得到返回的Dialog对象。把当前Activity设置为该对话框的所有者,从而把对话框挂靠到Activity上,让Activity自动管理该对话框的状态。这样,下次调用showDialog()时就不用重新创建Dialog对象,而是复用旧的。

所以,解决再次打开ProgressDialog时进度条不变化的最好方法就是将mProgressDialog.dismiss()改成removeDialog(COPY_CONTACTS)。

注意:如果你决定在onCreateDialog()方法之外创建一个对话框,它将不会被附着到活动上。不过,你可以通过setOwnerActivity()把它附着到一个Activity上。

史蒂夫·乔布斯的辞职信

英文原文:http://www.apple.com/pr/library/2011/08/24Letter-from-Steve-Jobs.html

致苹果董事会和苹果社区:

我一直说,如果有一天我不能履行作为苹果CEO的职责和期望,我会第一个让你们知道。遗憾的是,这一天已经来临。

我在此辞去苹果CEO的职务。如果董事会同意,我将愿意担任董事长、董事或者苹果员工。

就我的继任者而言,我强烈建议执行我们的接班计划,任命Tim Cook为苹果的CEO。

我相信,苹果最辉煌和最具创新的日子就在前方。我期望在新的岗位看到它的成功,并为此做出自己的贡献。

在苹果,我结交了一些我一生中最好的朋友。谢谢你们大家,多年来能和你们一起工作。

史蒂夫

《货币战争》是如何混淆读者视听的

这是早就写好了的。《货币战争》刚发行就买过来读,结果看到三分之一的时候就看不下去,准备写篇文章驳斥书中的观点。下面是列出的提纲,不过因为文笔太差的缘故,一直没能把内容扩充起来,到现在也没了这个心思。这里把已经写的贴出来给大家看看吧。

  1. 读者们喜欢阴谋论;
  2. 美联储是私有的从来就不是什么秘密。因为金融机构和公有还是私有无关,而是和财力及信用有关;
  3. 偷换概念,将金融家们的被动行为说成是主动行为,造成一种金融家们无所不能的环境;
  4. 放大作者想写的,缩小作者不想说的。只要是美国总统遇刺身亡就说是不符合金融家们的利益导致,那为什么不说别的国家呢?不要忘记军队掌握在谁的手中,给你个叛国罪啊什么的就能叫金融家们玩完,况且国家还有那么多秘密机构呢!想想希特勒时期的罗斯柴尔德家族吧。

货币战争真实存在,亚洲金融风暴、索罗斯狙击英镑这些都是货币战争。金融家们能调动数亿万计的财富,确实也很强大,但是不应该把他们的能力无限放大。

对美联储性质感兴趣的可以到它的官方网站看看。这里还有篇评论宋鸿兵和他书的文章:http://wenyidaobk.blog.163.com/blog/static/1243789052009712928966/

工作日志:从拒绝到接受再到喜欢

刚毕业,在苏州一家公司工作,需要写工作日志,心里不喜欢,但不得不写,每天应付了事。现在想来大概是因为不知道写什么,怎么写的缘故(自觉语言贫乏,感觉好多话要说,但写出来却是干巴巴的几句,所以对一切需要动笔的如思想汇报、工作小结、年终总结等都特别不乐意,更别提上学时候的写作文了)。

到第二家公司,刚开始不要写,后来公司作了调整就要写了,这时是不再拒绝,以一种无所谓的态度接受了。

在第三家后,也是开始不要写,后来调整要写,不过由于有具体的日志格式,所以对这个还是非常接受的。

今天凌晨醒来后睡不着,在床上胡思乱想,突然想到工作日志的事。从管理人员的角度思考了工作日志的问题,有了一些新的想法,发现工作日志其实是管理人员了解工作进度的这么一个东西。在一个项目组里,项目经理可以通过你每天的工作日志对你今天的工作有一个大致的了解,综合所有小组成员的工作日志就可以对项目所处的状态有个大约的概念。否则,每天只见你上班下班,坐在那里做事,但不知道你做了什么,做的怎么样,对项目状态不了解,项目就非常容易失控。

工作日志格式:

1
2
3
4
5
6
7
[main content]
今天做了什么,完成度怎么样?有哪些问题?
明天的计划是什么?

[questions]

[suggestions]

我觉得明天的计划这一项非常重要,通过明天的计划以及当天工作完成的情况我们就可以了解我们制定的计划是否合理有效。如果每天制定的计划都不能按时完成,那么说明自己对自己的能力估计过高,需要降低期望值。而项目经理可以通过某个组员一段时间内工作计划的完成度来了解组员的工作状态,如果组员一直处于计划不能按时完成状态,那么就要考虑是否要与组员沟通这个情况,帮助组员解决情况。

为什么要有个工作日志系统,而不是采用邮件的方式?

  1. 有文字记录(这个邮件也可以做到);
  2. 日志集中放置(这个邮件无法做到);
  3. 可以通过分析日志得出一些有用的统计(这个邮件无法做到)。

2014/12/23更新

其实把日志格式中的“今天”改成“昨天”,“明天”改成“今天”就是每日例会的模板了。

如何破解华为HG526电信猫

把家里的电信从旧的4M套餐改为新的4M套餐,每月费用省了40多块,还送了个华为的HG526电信猫和iTV。拿到HG526后发现它带有无线路由功能,想着是不是可以让它自动拨号,以替换原来的ADSL猫和无线路由器。网上搜索一下,发现有很多教程,参考着资料一番实践,终于把设备给换了下来。这里做个笔记,把实践中碰到的问题和疑惑记录下来,分享给大家。

破解HG526的关键是获得超级用户telecomadmin的密码,通过以下步骤实现:

  1. 准备U盘,在根目录下建立一个文件夹e8_Config_backup,插入华为HG526;
  2. 使用HG526的默认帐号(用户名useradmin)登录路由器192.168.1.1;
  3. 在浏览器地址栏里输入http://192.168.1.1/html/ehomeclient/cfgUSBRestore.cgi?coverusbpath=usb1_1回车(注意大小写),提示备份成功;
  4. 把U盘插入电脑,e8_Config_backup文件夹下多了一个文件ctce8_HG526.cfg;
  5. 用附件中的工具把配置文件解密,找到telecomadmin帐号的密码;
  6. 用超级用户telecomadmin登录,把远程管理关闭(不然密码就会被电信更改的)。

路由拨号配置

登录后,进入“网络” -> “宽带设置”,在“连接名称”右边,选择“INTERNET_R_8_35”,按照下图配置,修改自己的上网账号和密码。

hg526

这里有个问题,某些地方的VPI/VCI值是不一样的,本人所在区域值是0/100,所以上面应该选“INTERNET_B_0_100”。其它地方如果刷了不能用建议咨询下电信维护人员。

将破解的配置文件恢复到HG526中

在U盘的e8_Config_backup文件夹里放入破解修改后的ctce8_HG526.cfg文件,插入HG526。用超级用户登录,在“管理” -> “设备管理”页面中,在“启用USB自动恢复配置文件”后面打上钩。然后点击“设备重启”。注意观察HG526以及U盘的指示灯,等HG526上所有的灯稳定了,配置也就刷进去了。

删除TR069协议,修改远程管理网址和域名

正常情况下“TR069_R_8_46”连接项在配置页面中无法删除,但我们可以使用Web Developer插件来做到。在安装了Web Developer插件的Firefox中访问路由器,用telecomadmin登录,进入“网络” -> “宽带设置”,在页面中点击右键,在Web Developer工具栏中选择“表单” -> “开启 被禁用的表单”。然后你就可以删除TR069协议了。远程管理的修改与之类似。

关于TR069的解释:TR-069是由DSL论坛所开发的技术规范之一,其全称为“CPE广域网管理协议”。它提供了对下一代网络中家庭网络设备进行管理配置的通用框架和协议,用于从网络侧对家庭网络中的网关、路由器、机顶盒等设备进行远程集中管理。电信通过TR069协议修改我们的无线猫的配置,比如超级用户名密码,用户权限等,所以我们的配置文件里要关闭TR069,大家注意不要自己打开,否则一旦被改了超级用户名密码就惨了!

HG526配置文件加/解密工具下载:https://github.com/dohkoos/hw-hg526

7个方法养成立即行动的习惯

英文原文:http://www.pickthebrain.com/blog/grow-the-action-habit/

所有顶级成功人士都有一个共同的品质——那就是立即行动。这种品质可以取代智商、天赋和人际关系,来决定你的薪资范围和晋升速度。

尽管这个观念很简单,但是真正掌握它的人却是极少数。行动的习惯——把想法转换成立即行动的习惯——是完成任务必不可少的习惯。这里有7个方法让你养成立即行动的习惯:

1. 不要等到条件都完美了才行动

如果你想等条件都完美了才开始行动,你可能永远都不会开始。因为总是会有些事情不是那么好。或是错过时机,行情不好,或是竞争太激烈。现实世界中没有完美的开始时间。你必须立即行动,等问题出现时再去解决它。开始行动的最佳时间是去年,其次便是现在。

2. 做个实干家

行动胜于空想。你想开始实践吗?你有没有好的创意要告诉老板?今天就行动起来吧。想法停留在你脑子里的时间越长,它就会变得越弱。几天之后,细节就会变得模糊。一周过去,你已经完全忘记它了。要想成为一名实干家,你需要做更多的事情,并且在此过程中激发新的想法。

3. 记住,想法本身并不能带来成功

想法是很重要,但是它只有在被执行后才有价值。一个被付诸行动的平凡想法,比许多“某一天”或者“正确时机”再去实施的卓越创意更有价值。如果你有一个觉得真的很不错的想法,那么就去实现它。如果你不行动起来,那么这个想法永远不会被实现。

4. 用行动去克服恐惧

你有没有注意到,在公众演讲中最恐惧的时刻是轮到你上场前那段等待的时刻?即使是专业的演讲者和演员也经历过表演前的焦虑。他们一旦开始,恐惧就会消失。行动是治疗恐惧的最佳方法。万事开头难。一旦行动起来,你将建立起自信,事情也会变得简单。通过行动来克服恐惧,通过行动来建立自信。

5. 机械地开启你的创造力

人们对创造性工作的一大误解就是认为它只会在有灵感的时候产生。如果你一直等待灵感来敲门,那么你的作品就会很少。与其等待,不如机械地开启你的创造马达。如果你需要写些东西,那么强制自己坐下来后再写。笔尖在纸上滑动,思绪展开,开始写作,用你的笔激起你的伟大想法吧。

6. 先顾眼前

把注意力集中在你当下可以做的事情上。不要去烦恼上周本该完成的事,也不要担心明天要完成的任务。你唯一能左右的时间就是现在。如果你过于考虑从前或者将来,你不会完成任何事情。明天或下周的事经常永远都不会发生。

7. 立即切入正题

人们在开会前一般都会做些社交活动或着聊聊天。对于个体工作者也是如此。你在开始真正工作前多久会查看一次Email或RSS feeds呢?如果你不去绕过它们直接开始工作的话,这些分心的事情将会耗费你大量的时间。如果你做到了这点,你就会成为一个高效的人,别人也会把你当作领导者来看待。

没有上级的指示就采取行动,这需要勇气。或许这就是为何主动性是如此稀缺的才能,以至于大多数经理都在寻找这种人才。争取主动,当你有一个好的想法的时候,立即去实现它,不要等别人来告诉你。一旦人们看到你在很认真地完成事情,他们就会想要加入你。成功人士不需要别人告诉他们该做什么。如果你想成为这样的人,那就应该习惯独立行动。

实现Android底部工具栏

在网上的大部分教程中,底部工具栏通常由TabHost和RadioGroup结合完成,每个工具栏项对应一个Activity。不过,我们要实现的是多个工具栏项在单独的一个Activity上起作用。

简单实现Android顶部工具栏和底部工具栏就是这样的。不过该实现对每个工具栏项设置了固定宽度80dip,导致工具栏项或屏幕大小不定时代码布局会有问题。

这里对它做了些改进,使之能做到适应不定大小的工具栏项或屏幕。改进后的布局代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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">
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1.0">
        <ScrollView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:scrollbars="vertical"
            android:fadingEdge="vertical">
            <TextView
                android:id="@+id/content"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:textSize="17dip" />
        </ScrollView>
    </LinearLayout>

    <include layout="@layout/toolbar" />
</LinearLayout>

以下是工具栏toolbar.xml的布局代码:

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/toolbar_bg">
    <ImageButton
        android:id="@+id/btn_index"
        android:src="@drawable/index"
        android:text="@string/index"
        style="@style/toolbar" />
    <ImageButton
        android:id="@+id/btn_prev"
        android:src="@drawable/btn_prev_bg"
        android:text="@string/prev"
        style="@style/toolbar" />
    <ImageButton
        android:id="@+id/btn_next"
        android:src="@drawable/btn_next_bg"
        android:text="@string/next"
        style="@style/toolbar" />
    <ImageButton
        android:id="@+id/btn_zoomin"
        android:src="@drawable/zoomin"
        android:text="@string/zoomin"
        style="@style/toolbar" />
    <ImageButton
        android:id="@+id/btn_zoomout"
        android:src="@drawable/zoomout"
        android:text="@string/zoomout"
        style="@style/toolbar" />
</LinearLayout>

从已有项目创建Maven archetype

手里有这样的一个项目,其它项目都基于该项目创建,只是对包名做些修改,还有就是替换部分图片和文本内容。每次手工重复类似的修改工作很是繁琐,简直让人发指,需要寻找自动化的解决方案。

Maven界有这么一句话:遇到重复的Maven项目初始配置,就创建自己的archetype。其实不光是Maven项目,其它也是如此。《测试驱动开发》中有个Triangulation法则,《重构》中也有Rule of three,都是用来指导如何解决类似重复问题的。第一次是特殊解决,第二次还是特殊解决,第三次就要抽象解决了。

Maven Archetype Plugin允许从当前存在的项目创建archetype,这样以后用户就可以基于该archetype创建项目了。

archetype-overview

那么如何通过现有的项目创建archetype呢?首先清理项目中那些不必要的文件和目录,然后在根目录下执行:

1
2
3
mvn archetype:create-from-project
cd target/generated-sources/archetype
mvn clean install  # 本地安装

现在就可以使用上面创建的archetype来建立新项目了。在新的目录中执行以下命令即可:

1
2
3
4
5
6
7
8
mvn archetype:generate                          \
  -DarchetypeCatalog=local                      \
  -DarchetypeGroupId=<archetype-groupId>        \
  -DarchetypeArtifactId=<archetype-artifactId>  \
  -DgroupId=<your-groupId>                      \
  -DartifactId=<your-artifactId>                \
  -Dpackage=<your-package>                      \
  -Dversion=1.0

这里要注意的是,不要在target/generated-sources/archetype目录下运行上述命令,否则会生成失败,报如下错误:

1
2
3
[ERROR] Failed to execute goal archetype:generate:
  org.apache.maven.archetype.exception.InvalidPackaging:
  Unable to add module to the current project as it is not of packaging type 'pom'