23 Nov 2015
没有测试就会让写程序的开发者没有自信
这次的新项目没有想当然的选择之前一直在用Rspec测试框架,主要是我感觉Rspec里面有太多的magic,在helper文件中很多tricks, 很快相关的配置文件就非常大。这篇Blog说的跟我的体会很像 7 REASONS I’M STICKING WITH MINITEST AND FIXTURES IN RAILS
minitest是Rails4后默认支持的测试框架,另外rails也提供了很多方便和强大的assert方法。可以参考官方文档
下面我就总结和分享一下,Rails4新项目中minitest和capybara的集成和配置:
Gemfile
minitest-reporters
是为了美化minitest的测试结果输出地,有颜色,有百分比。
mocha
是一个方便进行mock和stub的工具
- 其他几个都是跟capybara相关的工具,具体会在配置中看到。
test_helper中添加的配置内容
在这里创建一个继承自新ActionDispatch::IntegrationTest的FeatureTest类,然后引入Capybara::DSL,作为Feature测试的父类。
测试确认邮件
如果要测试邮件的内容,内容中一般都会有链接,所以需要在config/environments/test.rb中加上:
如果使用了delayed_job可以在其配置中加上:
表明测试环境下,邮件任务都是直接发送出去的
用户注册和确认邮件测试的样例代码
在test目录下面,新建features目录,专门存放网站功能测试代码
test/features/user_authentication_test.rb
所以可以看到用minitest写的代码都非常直接,都是纯ruby代码。等代码增多后,可以按照ruby的方式来组织,重用和重构。
08 Nov 2015
Rails的世界各种开源的优秀解决方案非常多,项目开始的时候比较痛苦地就是要根据自己的原则和项目的特点来做出正确的选择。
前端开发框架的选择
原本打算在新的项目中开始使用ES6,然后前端的开发框架选择容易上手的,轻量级的vue.js。由于Vue.js只支持IE9以及以上的浏览器,考虑到新项目是面向国内用户的产品,用户中还是有很大一部分的IE8,以及以下的浏览器用户。所以,最后还是决定使用Jquery+knockout.js这样的组合。
通过在论坛里面帖子前端框架选择的一个现实的问题评论了解到。jquery也有支持双向绑定的lib: jquerymy.js
还有就是在这种情况下,如果要做SPA的话,可以使用backbone.js,兼容性也是很好的。
不再使用simple-form,主要是感觉它提供的一些功能对现在的项目帮助不大,还有就是simple form的wrapper定制比较麻烦,也会把页面代码弄得很臃肿。在这种前提下还是使用rails自带的form_for就可以了。原则就是如果要使用的gem帮助不是很大,还是最优先用Ruby和Rails自带的一些方案。
表单验证
尝试使用client side validations这个gem,这样前后端尽可能共享验证的规则,减少自己写相关js代码。少些代码,少制造bug。
Rails后台任务框架的选择:
网上已经有很多资料比较Sidekiq,Resque和DelayedJob。我自己也都有简单用过。
所以决定在项目开始时(流量不大)使用DelayedJob,简单易用,不要单独安装和配置Redis,同时使用ActiveJob提供的API,这样以后迁移到Sidekiq或者Resque也就不会很麻烦。
搜索:Elasticsearch
本周也开始看一下搜索功能的实现,以前我做过几年的基于Lucene的搜索开发,搜索服务就准备用elasticsearch,发现相关的Rails的整合方案也很多。有官方的elasticsearch-rails,还有一个比较流行的是searchkick,看了一下感觉功能很强大,所以下周准备拿searchkick试试。
用户认证:Devise
这个选择主要是因为Devise功能强大,相关文档也很多,之前在项目中也用过。
31 Oct 2015
我在之前的项目中使用过Bootstrap和Foundation,确实非常方便使用,把相关的gem放到项目中之后,然后再到网站上找到一些HTML和CSS的例子,基本能够满足需求,但是要说到对这两个框架有多了解,还真是谈不上,这两个都是大而全的东西,在Rails中基本上都是一股脑全部引入,然后就只管用,确实省心。
但是时间长了就发现,用这两个框架写出来的页面代码还是非常啰嗦,易读性和可维护性随着项目的变大越来越差。
之前还有一个项目用到了Compass,Compass
中也是包含了很多内容,我个人一直比较喜欢简单,轻量的东西,所以看到这种庞大的库就有点害怕。
然后我就想有没有其他选择呢?后来,在一个开源项目Hours中就发现了Bourbon,然后又是Neat,还有Bitters和Refills,一开始一下看到四个东西,感觉摸不清思路,都不知道是干什么的。后来,读了几篇文章后,就清楚了Bourbon
整个技术栈的理念。就是把像Bootstrap
这种大而全的框架,清楚地拆分成四个小的工具库,然后可以组合在一起使用,也可以根据需要来选择使用。
下面简单介绍一下四个工具:
另外看到Bourbon
是thoughtbot出品的,我相信这些工具一定在实际项目中得到了验证。我一直以来都很喜欢thoughtbot
开发一些gems,非常实用,而且轻量,比如clearance
等。
其中下面这两篇文章说服了我:
5 Reasons We Chose Bourbon/Neat Over Foundation or Bootstrap
Why I prefer Bourbon over Bootstrap
最吸引我得就是使用Bourbon
和Neat
,可以写出非常易读,易懂的语义化的HTML
特别是当我们使用Haml
或者Slim
这样的模板语言后,在模板中如果都是一些语义化的CSS class
,代码看起来就非常直观。
比如:
用Bootstrap会这样写(如果再加上一些响应式的样式就更加冗长):
使用Borbon可能会是这样,非常清晰,直观:
接下来简单说一下我是如何在新的Rails项目中使用的。
1. 基本安装:
这里没有什么特别需要说明的,大家只要按照官方github上的文档说明,很容易搞定。
2. 使用normalize.css
在Bootstrap中默认是集成了normalize.css
,所以我们不用单独引入,在Bourbon栈中默认是没有的,需要我们单独引用.这里使用normalize-rails这个gem就可以了。
gem “normalize-rails”
3. 使用autoprefixer
Bourbon中有一大块功能是关于vendor prefixes
的。需要开发者自己主动地去使用封装好的一些Sass的mixin,这个对于我一个后端出身的号称全栈的开发者要求有些高了。
同时autoprefixer的处理方式就是非常的傻瓜式,开发者自己不要管vendor prefixes,尽管写标准的CSS,剩下的autoprefixer自动搞定。
Bourbon的开发者也同意这一点,所以在Bourbon5.0
中,关于vendor prefixes的部分也将会删除。
所以,我们只需要使用gem “autoprefixer-rails”就可以了。
gem “autoprefixer-rails”
如果想了解和深入学习Bourbon相关的技术,可以到官方网站上查看详细的使用文档。
这里还有一个youtube视频系列,可以帮大家系统学习:
Awesome CSS with Bourbon, Neat, Bitters & Refills! 国内的朋友记得翻墙哦。
BTW:随着项目的进行,我会逐步写出对于一些架构和工具选择总结的文章。
10 Aug 2015
##Conceptual Aside
Syntax Parsers
A program that reads your code and determines what it does and if it’s grammar is valid
(interpreter/compiler) do extra stuff
Execution Contexts
A wrapper to help manage the code that is running
Lexical Environments
where something sits physically in the code you write
(where you write something is important)
Name/Value Pairs
A name which maps to a unique value
Object
A collection of name value pairs
The Global Environment And the Global Object
Global Object - like (window in browers)
‘this’
Outer Environment
Global -> ‘Not inside a Function’
The execution context: creation and ‘hoisting’
Hoisting: Setup Memory Space for Variables and Functions
All variable in javascript are initially set to undefined
And functions are sitting in memory in their entirety.
Javascript and ‘Undefined’
undefined: the variable hasn’t been set, is javascript special value.
The execution context: Code Execution
Single Threaded:
one command at a time
Synchronous Execution:
one at a time, in order
Function Invocation And The Execution Stack
invocation: running a function, using parenthesis ()
execution context stack
Variable Environments:
where the variables live:
every execution context has its own variable environment
how they relate to each other in memory
The Scope Chain:
Scope: where a variable is available in your code.
ES6
let -> block scope
Asynchronous callbacks:
Types:
Primitive Types:
primitive type: a single value 就是一个独立的值
undefined 也是一个primitive type
js的number是浮点数,只有这一种数字类型
symbol是ES6中的新的类型
assignment is right to left:赋值操作的方向是从右往左
coercion: converting a value from one type to another.
- operator: coerce: number to string
Number(false) = 0
Number(undefined) = NaN
Number(null) = 0
Equality ==:
“3” == 3 #true
false == 0 #true
Strict Equality: === doesn’t do coerce
绝大部分情况都是使用===进行比较,除非明确知道为什么要用==
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
Boolean
Boolean(undefined) #false
Boolean(null) #false
Boolean(“”) #false
Boolean(0) #false
Default value:
name = name || “Default name”
Objects and Functions
person[‘firstname’]
person.firstname
var person = new Object();
Object literal
var person = {};
Fanking Namespaces: Using Object
keep variables and functions with the same name separate
JSON:
JSON.stringfy(obj)
JSON.parse(“”)
Functions are objects
JS的functions也是对象,有自己的name属性等。
First Class Functions:
Everything you can do with other types you can do with functions
Assign them to variables, pass them around, create them on the fly
CODE 也是function对象的一个属性 (Invocable)
Expression:
A Unit of code that results in a value
By value VS by reference:
primitive value: copy the value (by value)
all objects interact by reference
Objects, Functions, ‘this’
var self = this
arguments and Spread:
arguments: the parameters you pass to a function
arguments.length
arguments[0]
function getPerson(){
return { #如果return后面没有{,JS的语法解释器会自动加上分号,导致函数就直接返回。
firstname: ‘tony’
};
}
(function(name){
retrun ‘hello’;
})();
() 里面就可以放入statement,所以可以放入function的statement
Closure:
小心循环的陷阱
Function Factories
Closures And Callbacks
callback function: A function you give to another function to be run when the other function is finished.
Call(), Apply(), Bind()
bind(obj): 返回一个copy的function,然后把function中的this赋值为obj
call(obj, ‘‘,…):会设置this为obj然后执行这个function
apply(obj, []):跟call类似,就是参数需要是array
function borrowing
function currying
使用bind可以预设参数值
Creating a copy of a function but with some preset parameters
very useful in mathematical situations
Functional Programming
underscore.js -> source code # TODO
lodash.com
Object-Orientied Javascript and prototypal inheritance
每个obj都有一个proto属性,指向其prototype
prototype chain:
// don’t do this EVER!
john.proto
Everything is an object (or a primitive)
base object : Object
function的prototype -> function Empty(){} #apply(), bind(), call()
Reflection and Extend
An object can look at itself, listing and changing its properties and methods.
function Person(){} # function constructor
A normal function that is used to construct objects.
The ‘this’ variable points a new empty object, and that object is returned form the function automatically.
Function的prototype属性只有是使用new的情况下次才会用到。
Built-in function constructors
Object.create and Pure Prototypal Inheritance
Polyfill
Code that adds a feature which the engine may lack
ES6 and CLASSES
extends
classes in javascript a phrase that it’s just syntactic sugar
ODDS and ENDS
typeof, instanceof
typeof 3 #number
typeof ‘hello’ #string
typeof {} #object
typeof [] #object
Object.prototype.toString.call(d) #[object Array]
instanceof #检查prototype chain
typeof (function{}) #function
Strict mode
“use strict”;
function myfunc(){
“use strict”;
….
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
Open Source Education
a dollar sign
isNumberic
sizzle
it’s ok to return something from the function constructor (default is this)
可以在返回之前对this做一些操作,然后用return明确得返回
Transpile:
Convert the syntax of one programming language, to another.
TypeScript
Traceur: ES6 -> ES5
To read a list of features existing or coming in ES6, head here: https://github.com/lukehoban/es6features