天涯望帆

乱想者

batman.js 结合rails 入门

| Comments

batman.js 是一个很令人兴奋的前端mvc框架,不过文档是硬伤,外文资料少的可怜,中文资料更是一个悲剧!
这几天因为一个项目需要用到大量ajax,所以有一个想法用前端mvc来做一个整站的ajax,我看了backbone.js spine.js ember.js 等前端mvc,可是他们各自硬伤啊

  1. banbone.js 他的mvc让我感到陌生,从django到rails等web mvc我没看到这么样的mvc,十分复杂,而且让我写出来的代码不可控制
  2. ember.js 他是一个很好的框架,可是依赖让我却步,比如你得使用jquery低版本,这是我没法接受的,而且ember太大了,文档也不是很好
  3. spine.js 是一个很好的框架,例子和文档都很好,可是他却不能实时更新ui

所以我选择baman.js, 不过这东西会让初学者感到沮丧,因为没有一个好的教程,而且官网文档也少得可怜, 有一个和rails结合的例子,可是作者又是haml拥戴者(这~~)硬着头皮看完,并且看了文档,写一下日志,方便自己以后查看

这里使用rails作为后端

install

在Gemfilel里面添加

1
gem 'batman-rails'

安装目录结构

1
rails g batman:install

因为我们要使用mvc,所以我们最好关闭掉rails的自动生成coffeescipt 在application.rb添加

config.generators.javascripts = false

如果你想把自动生成scss的功能也给关掉可以继续添加上

config.generators.stylesheets = false
这样我们的基础工作就算ok了

来一个demo吧!

最简单也是最好用的一个demo就是写一个blog吧,这里我们用一个blog来开始我们的batman之旅,这个blog有这么几个要求:
1. 文章要有标题,内容和图片 2. 无刷新,整站使用ajax

ruby

我们需要使用使用基础的命令来生成一个项目

1
rails new blog

在我们Gemfile里面添加上

1
2
3
gem 'batman-rails'
gem 'carrierwave' #因为我们需要进行图片上传
gem 'bootstrap-sass', '~> 2.2.2.0' # 为了让我们的demo好看点,我们用这个吧

使用bundle进行gem的安装

之后我们先把自动生产javascipt的功能关掉 在application.rb添加
config.generators.javascripts = false

生成我们需要的api,这里因为我们需要快速完成,使用scaffold

1
rails g scaffold post title:string content:text image:string

然后我们运行migrate来生成数据库

1
rake db:migrate

现在生成root首页

1
rails g controller home index

删除public/index.html,并且把app/views/home/index.html.erb 清空

添加root路由 config/routes.rb

1
2
3
4
BatmatBlog::Application.routes.draw do
  root to: "home#index"
  resources :pots
end

安装batman

1
2
3
rails g batman:install
#在javascipt里面创建views目录
mkdir app/assets/javascipts/views

修改application.css 为application.css.scss 添加以下内容

1
2
3
4
5
6
/*
 *= require_self
 *= require_tree .
 */

@import "bootstrap";

在application.js添加以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//= require jquery
//= require jquery_ujs
//= require bootstrap

// Batman.js and its adapters
//= require batman/es5-shim
//= require batman/batman
//= require batman/batman.jquery
//= require batman/batman.rails

//= require batmat_blog

//= require_tree ./models
//= require_tree ./controllers
//= require_tree ./helpers


//= require_tree .
// Run the Batman app
$(document).ready(function(){
  BatmatBlog.run();
});

因为我们需要上传文件,所以我们需要使用carrierwave来做

1
rails generate uploader image

修改model
app/models/post.rb

1
2
3
4
class Post < ActiveRecord::Base
  attr_accessible :content, :image, :title
  mount_uploader :image, ImageUploader
end

好了接下来我们需要进入batman,batman也为了我们准备了很多很有用生成器

1
2
3
4
5
6
Batman:
  batman:controller
  batman:helper
  batman:install
  batman:model
  batman:scaffold

我们使用batman:scaffold来创建我们需要的东西,也方便我们熟悉batman的写法

1
rails g batman:scaffold post

batman

打开app/assets/javascipts/post.js

1
2
3
4
5
class BatmatBlog.Post extends Batman.Model
  @storageKey: 'posts'
  @persist Batman.RailsStorage

  @encode "title", "content", "image"

下面解释一下上面几个东西,因为我的英语翻译确实比较烂,所以还是直接引用英文了

1
2
@storageKey is a class level option which gives the storage adapters something to interpolate into their specific key generation schemes. In the case of LocalStorage or SessionStorage adapters, the storageKey defines what namespace to store this record under in the localStorage or sessionStorage host objects, and with the case of the RestStorage family of adapters, the storageKey assists in URL generation. See the documentation for the storage adapter of your choice for more information.
The default storageKey is null.
1
@persist is how a Model subclass is told to persist itself by means of a StorageAdapter. @persist accepts either a StorageAdapter class or instance and will return either the instantiated class or the instance passed to it for further modification.
1
2
@encode specifies a list of keys a model should expect from and send back to a storage adapter, and any transforms to apply to those attributes as they enter and exit the world of Batman in the optional encoderObject.
The encoderObject should have an encode and/or a decode key which point to functions. The functions accept the "raw" data (the Batman land value in the case of encode, and the backend land value in the case of decode), and should return the data suitable for the other side of the link. The functions should have the following signatures

接下来我们进入controller

我们可以看到batman为我们生成了基本的index show create update

不过我们因为需要有一个get action来生成new和edit 所以我们加上这两个方法,代码就如下面这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class BatmatBlog.PostsController extends Batman.Controller
  index: (params) ->

  show: (params) ->

  new: (params) ->

  create: (params) ->

  edit: (params) ->

  update: (params) ->

  destroy: (params) ->

现在先放着,不写实现先,我们需要写路由了 这个时候,可以说说路由在那里实现了,我们看以我们项目名称命名的coffee文件 app/assets/javascipts/batmat_blog.js.coffee

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
window.BatmatBlog = class BatmatBlog extends Batman.App

  # @root 'controller#all'
  # @route '/controller/:id', 'controller#show', resource: 'model', action: 'show'

  @on 'run', ->
    console?.log "Running ...."

  @on 'ready', ->
    console?.log "BatmatBlog ready for use."

  @flash: Batman()
  @flash.accessor
    get: (key) -> @[key]
    set: (key, value) ->
      @[key] = value
      if value isnt ''
        setTimeout =>
          @set(key, '')
        , 2000
      value

  @flashSuccess: (message) -> @set 'flash.success', message
  @flashError: (message) ->  @set 'flash.error', message

我们需要添加路由,和指定view的存放位置

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
window.BatmatBlog = class BatmatBlog extends Batman.App

  # @root 'controller#all'
  # @route '/controller/:id', 'controller#show', resource: 'model', action: 'show'
  @root 'posts#index'
  @resources 'posts'

  Batman.ViewStore.prefix = 'assets/views' # 设置view目录

  @on 'run', ->
    console?.log "Running ...."

  @on 'ready', ->
    console?.log "BatmatBlog ready for use."

  @flash: Batman()
  @flash.accessor
    get: (key) -> @[key]
    set: (key, value) ->
      @[key] = value
      if value isnt ''
        setTimeout =>
          @set(key, '')
        , 2000
      value

  @flashSuccess: (message) -> @set 'flash.success', message
  @flashError: (message) ->  @set 'flash.error', message

好了我们来写view, 首先修改appliction.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
  <title>BatmatBlog</title>
  <%= stylesheet_link_tag    "application", :media => "all" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>

<div data-yield="main"></div>

</body>
</html>

这时打开主页,并打开console,看到一个错误 Uncaught DevelopmentError: Please define routingKey on the prototype of PostsController in order for your controller to be minification safe. batman.js:750

这是因为rails js会压缩,如果你不压缩js的话,这个无需介意,不过我们的js需要压缩,所以我们必须在controller里面写入这一行代码,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class BatmatBlog.PostsController extends Batman.Controller

  routingKey: "posts"

  index: (params) ->
    BatmatBlog.Post.load (err, posts) =>
      @set 'posts', posts
  show: (params) ->

  new: (params) ->

  create: (params) ->

  edit: (params) ->

  update: (params) ->

  destroy: (params) ->

现在来写new的view和controller action

新建app/assets/javascipts/views/posts/new.html.erb

1
2
3
4
5
6
<form class="new-post" accept-charset="utf-8" data-event-submit="create" data-formfor-post="post">
  <input type="text" data-bind="post.title" name="post[title]">
  <input type="text" data-bind="post.content" name="post[content]">
  <input type="file" data-bind="post.image" name="post[image]">
  <input type="submit" value="save">
</form>

然后写new和create方法

1
2
3
4
5
6
7
8
9
10
new: (params) ->
    @set "post", new BatmatBlog.Post()

  create: (params) ->
    @get('post').save (err) =>
      if err
        # 做一些错误处理
        throw err
      else
        document.getElementsByName("post[image]")[0].value = "" # 因为图片上传之后,你再次新建的时候记录还是在的,所以我们要手动清楚掉

现在我们写index的view
app/assets/javascipts/posts/index.html.erb

1
2
3
4
5
6
7
8
<div data-foreach-post="posts">
  <div>
    <p data-bind="post.title"></p>
    <a data-route="routes.posts[post]">show</a>
  </div>
</div>

<a data-route="routes.posts.new">new</a>

好了想在写show的view
app/assets/javascipts/posts/show.html.erb

1
2
3
4
<p data-bind="post.title"></p>
<p data-bind="post.content"></p>
<img data-bind-src="post.image.url" alt="">
<a data-route="routes.posts">back</a>

补写controller app/assets/javascipt/controller/posts_controller.js.coffee

1
2
3
show: (params) ->
  @set "post", BatmatBlog.Post.find params.id, (err, post) =>
    throw err if err

现在打开浏览器看看,我们的基本样子就出来了,下一篇将介绍用户登陆,和美化,这篇就到这里

Mac Os 10.8 的rvm注意事项

| Comments

因为xcode4.6把系统的gcc版本改成了llvm,如果没有设置好的话,在安装gem和rubu的时候会出错 下面是解决办法

1
2
3
4
brew update
brew tap homebrew/dupes
brew install apple-gcc42
rvm get stable

edit ~/.zshrc

1
export CC=gcc-4.2

install ruby

1
rvm install ruby-1.9.3 --enable-shared --without-tk --without-tcl

如何在model使用view和controller里面的方法

| Comments

当在些model的时候我们很多时候需要使用一些在hepler方法或者其他,model是使用不了这些的,一般是通过传变量来解决,可是这样的话,总是感觉怪怪的

这个时候可以使用线程来达到我们需要的东西

下面以在model使用devise的current_user为例说明如何使用

在ApplicationController里面写入一个方法

1
2
3
4
5
6
before_filter :set_request_environment

  private
  def set_request_environment
    Thread.current[:current_user] = current_user if current_user
  end

在model里面就可以访问到这个线程的内容

1
2
3
4
5
6
7
def is_teacher?
    if Thread.current[:current_user] == teacher
      return true
    else
      return false
    end
end

Mac 下的vim 美化(iterm2 Zsh Powerline)

| Comments

因为近期一直在写前端,在自己喜欢的emacs下写比较麻烦,所以配置一些vim来写前端

1. 安装oh-my-zsh(这个强大的东西作为一个ruby开发者怎么可以没有)

1
curl -L https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh | sh

并在.zshrc 里面添加或修改ZSH_THEME为ZSH_THEME=”agnoster”
note: 以后需要添加或者修改环境变量的时候记得要在~/.zshrc 里面做

2. 配置好看的主题和字体

  1. 下载并安装需要用到的字体,下载地址如下 https://gist.github.com/qrush/1595572/raw/417a3fa36e35ca91d6d23ac961071094c26e5fad/Menlo-Powerline.otf
  2. 在iterm2里面配置好字体preferences > profiles > text > Non-ASCll font 改为刚刚安装上去的字体
  3. 配置正确的显示色彩 preferences > profiles > Terminal 将Report Terminal Type: 选择为xterm-256color
    并且勾上Set locale variables automatically

3. 配置vim

  1. 安装插件pathogen(用来管理vim的插件的)
1
2
3
mkdir -p ~/.vim/autoload ~/.vim/bundle; \
curl -Sso ~/.vim/autoload/pathogen.vim \
    https://raw.github.com/tpope/vim-pathogen/master/autoload/pathogen.vim

配置插件
打开vim ~/.vimrc
将execute pathogen#infect()添加加在第一行

4. 安装vim-powerline

这个插件必须安装,一个很好vim状态栏

1
2
cd ~/.vim/bundle
git clone https://github.com/Lokaltog/vim-powerline.git

5.打开状态栏

将set laststatus=2 添加到 ~/.vimrc 里面

Golang 部署小技巧

| Comments

golang的部署是很简单的事情,记录我这几天用到的部署技巧

如果你是第一次进行交叉编译, 那么执行下面的操作

1
2
$ cd /usr/local/go/src
$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 ./make.bash

然后运行

1
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build xxx.go

之后把编译好的文件发到服务器就好了

下面配置upstart把项目启动 sudo vim /etc/init/xxx.conf

1
2
3
4
5
6
7
8
9
10
11
12
description     "xxx"
author          "mjason"

start on (net-device-up
          and local-filesystems
          and runlevel [2345])

stop on runlevel [016]

respawn

exec /path/file

最后使用sudo service xxx start

大功告成

然后用server

Ios学习笔记1–cocoapods

| Comments

认识工具

在开发ios过程中不可避免要使用第三方的开发库,对于习惯了ruby,golang,python等编程语言的人来说安装这些东西无疑是痛苦的,总结起来步骤就有一下几个

  1. 下载源码
  2. 根据第三方库的文档进行项目设置
  3. 然后解决掉各种可能会出现的错误

遇到新的项目继续上面的工作,这是没办法忍受的事情,好在ios的黑客都是非常酷的人,他们开发出了像ruby的bundley一样方便的东西名字叫做cocoapods

因为某些原因官网上不了,请各位看官自备梯子。

安装cocoapods

  1. 首先你要安装好ruby,这个网上有很多,这里我就直接写出命令给各位了
1
2
#安装rvm,并安装ruby,在mac下只需要用下面一条命令
curl -L https://get.rvm.io | bash -s stable --ruby
  1. 安装cocoapods
1
gem gem install cocoapods
  1. 好了之后,请进入你的ios项目里面新建一个名字叫做的Podfile的文件,在文件里面写入类似下面的文本
1
2
platform :ios, '5.0'
pod 'AFNetworking', '~>1.1.0'

注解一下:第一版是说明你使用ios的库,并使用5.0的sdk
第二行就是你的需要用的库,格式是pod ‘库的名称’, ‘版本号’

4.使用pod install进行安装库,安装完毕之后是使用项目目录下的xxx.xcworkspace进行编程就可以了

移动api设计注意事项

| Comments

工作室的星星,近期用nodejs写了一套api,看了源码和实现之后,发现有几点十分值得我学习的,

记录如下:

不要把token值放到url里面,原因很简单服务器日志一般会记录这些东西,有风险,把他放到http header上面去是一个好的选择

把系统进行切分: 我们把系统分成了3个部分,每一部分都有forever单独启动,这样,对于我们持续部署是有很大的好处的

瞎扯完毕,继续工作

Golang解析网页入门

| Comments

这段有个小任务,需要写一个爬虫,进行数据的分析 原本使用ruby写,可是目标网站需要多重的跳转,造成网络io延时很高,分析一次目标网站都需要10秒甚者更长。

这两天使用golang重写,把计算负责的和高io延时的全部并发出去,时间从原来的10秒降低到现在的3秒多一些。

使用的golang的库是goquery 下载方式是: go get github.com/PuerkitoBio/goquery

使用这个库需要使用golang的exp库 下载这个库的方法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
% cd $GOPATH/src
% hg clone https://code.google.com/p/go go-exp
requesting all changes
adding changesets
adding manifests
adding file changes
added 13323 changesets with 50185 changes to 7251 files (+5 heads)
updating to branch default
3464 files updated, 0 files merged, 0 files removed, 0 files unresolved
% mv go-exp/src/pkg/exp .
% rm -rf go-exp
% go install exp/...
%

使用这个goquery可以像jquery那样去分析dom

官方的doc可以打开自己本地的 godoc -http=”:9090” 查看 也可以到godoc网站去看

Rails_admin的权限控制

| Comments

rails_admin的权限控制中心

先在rails_admin的配置文件写入

1
2
3
RailsAdmin.config do |config|
  config.authorize_with :cancan
end

之后使用rails 生成器生成cancan的控制文件,编辑文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new
    if user && user.is_superadmin?
      can :access, :rails_admin   #打开路由
      can :dashboard   #打开统计表
      can :manage, :all #这个角色可以读取所有的表
    elsif user.is_admin?
      can :access, :rails_admin
      can :dashboard
      can :manage, [Person] # 这个用户只能管理这个模型
    end
  end
end

简单的rails部署

| Comments

rails的部署现在最流行的肯定是capistrano 刚开始学习的rails的时候,我也是用这个方式部署,不过每一次写部署脚本太伤了,各种错误,各种调试

自从有一次在ruby-toolbox看到了mina,终于有点感觉得到解救了

根据自己的使用mina的特点主要有:

  1. 更加简洁的写法
  2. 更加友好的提示界面

所以部署方式现在采用的是(rvm + mina + unicorn)

这里详细记录操作步骤

安装mina

1
2
3
4
5
#in Gemfile

group :development do
  gem 'mina'
end

初始化mina 配置文件

1
2
3
#cd your app project dir
bundle
mina init

配置mina文件

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
require 'mina/bundler'
require 'mina/rails'
require 'mina/git'
require 'mina/rvm'    # for rvm support. (http://rvm.io)

set :domain, 'foobar.com' #设置你的服务ip地址,或者域名
set :deploy_to, '/var/www/foobar.com' #部署的文件目录
set :repository, 'git://...' # git 地址
set :branch, 'master' # 确定分支

set :shared_paths, ['config/database.yml', 'log']

# Optional settings:
set :user, 'foobar'    # ssh 用的用户名.
#   set :port, '30000'     # SSH 端口,默认22.

task :environment do
  invoke :'rvm:use[ruby-1.9.3-p125@default]'
end

#mina setup 时会执行的操作
task :setup => :environment do
  queue! %[mkdir -p "#{deploy_to}/shared/log"] # 创建日志目录
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/log"] # 设置日志目录的权限

  queue! %[mkdir -p "#{deploy_to}/shared/config"] #创建目录
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/config"] # 目录权限设置

  queue! %[touch "#{deploy_to}/shared/config/database.yml"] # 生成服务器的database.yml
  queue  %[-----> Be sure to edit 'shared/config/database.yml'.] #提示编辑服务器的database.yml, 可以删除 
end

# 进行mina deploy会进行的操作
desc "Deploys the current version to the server."
task :deploy => :environment do
  deploy do
    # Put things that will set up an empty directory into a fully set-up
    # instance of your project.
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'

    to :launch do
      queue 'touch tmp/restart.txt' # 服务器重启服务器用的
    end
  end
end

根据自己的项目和服务器信息进行设置之后运行

1
2
3
4
# 服务器目录初始化
mina setup
# 进行项目部署
mina deploy

这样第一步就完成了

ssh 登录进服务器 然后建立一个专门用来部署的用户,安装rvm和ruby 网上的方法很多,这里就直接贴命令了,基于ubuntu12.04

1
2
3
4
5
6
7
8
9
10
11
12
ssh root@ip
adduser deploy
su deploy
curl -L https://get.rvm.io | bash -s stable
# 安装rvm的依赖
rvm requirements
rvm install 1.9.3
rvm use 1.9.3
gem install unicorn
# bootup可以自由写,推荐写项目名称
rvm wrapper ruby-1.9.3 bootup unicorn_rails
vim $HOME/.rvm/bin/bootup_unicorn_rails

因为mina的gem是安装在项目的vendor/bundle下面 所以需要修改一下rvm自动生成的脚本 将文件中的unicorn_rails 改为 你的项目地址+current/bin/unicorn_rails eg: /home/deploy/test1/current/bin/unicorn_rails

接下来就是写启动脚本了,我的shell是以Ruby China的mimosa配置写的,这里就不重复
直接贴上原帖地址和git地址
原帖地址:http://ruby-china.org/topics/471
他提供的git地址: https://gist.github.com/3547765

收工!!!