20 Sep 2017
MySQL Character Set基础知识
对于 MySQL 数据库你可以在不同的 Level 设置Character Set 和 Collation,包括:Server Level,Database Level,Table Level,Column Level 还有 Application Level.
Server Level:
可以通过命令行设置,也可以通过配置文件设置
默认: --character-set-server=latin1
latin1_swedish_ci
is the default collation for latin1
还可以通过重新编译时指定参数实现:use the DEFAULT_CHARSET and DEFAULT_COLLATION
作用范围:如果创建数据库时不指定,那么就使用 Server Level 的设置
查看当前的设定,可以查看系统变量:character_set_server and collation_server
Database Level:
可以在创建数据库时设置:
CREATE DATABASE db_name CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
默认值:可以由 character_set_database
and collation_database
系统变量决定.
可以通过下面的命令查看当前的设置:
USE db_name ;
SELECT @@ character_set_database , @@ collation_database ;
或者
SELECT DEFAULT_CHARACTER_SET_NAME , DEFAULT_COLLATION_NAME
FROM INFORMATION_SCHEMA . SCHEMATA WHERE SCHEMA_NAME = 'db_name' ;
作用:如果建表时没有指定,那么会作为表的默认值,同时也是作为 LOAD DATA 的默认值
修改数据库level 的 character set 和 collation
ALTER DATABASE db_name CHARACTER SET utf8 COLLATE utf8_unicode_ci ;
或者单独修改
ALTER DATABASE my_database DEFAULT COLLATE utf8_unicode_ci ;
ALTER DATABASE my_database DEFAULT CHARACTER SET utf8 ;
Table Level:
可以在建表的语句中进行设置
作用:如果字段没有具体制定,那么会作为字段的默认值
note:该功能是 mysql 的一个扩展,不是标准的SQL
使用下面语句可以同时修改 table 和 table 中字段的设置
ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name [ COLLATE collation_name ];
查看某个数据库中的所有表的一些设置信息的语句
SHOW TABLE STATUS FROM db_name ;
Column Level:
N/A
Application Connection Level:
对于 Rails 应用,在 database.yml的数据库连接设置中加上 ?reconnect=true&encoding=utf8&collation=utf8_unicode_ci
查看数据库不同级别的元数据的设置的语句,比如:character set
SELECT DEFAULT_COLLATION_NAME FROM information_schema . SCHEMATA S WHERE schema_name = 'db_name' AND DEFAULT_COLLATION_NAME != 'utf8_unicode_ci' ;
SELECT TABLE_NAME , TABLE_COLLATION FROM information_schema . TABLES WHERE table_schema = 'db_name' AND table_collation != 'utf8_unicode_ci' ;
SELECT * FROM information_schema . COLUMNS WHERE table_schema = 'db_name' AND collation_name != 'utf8_unicode_ci' ;
ConvertDatabaseCharacterSetAndCollationToUtf8 Migration
class ConvertDatabaseCharacterSetAndCollationToUtf8 < ActiveRecord :: Migration
def up
execute <<~ SQL
ALTER DATABASE #{ ActiveRecord :: Base . connection . current_database } CHARACTER SET utf8 COLLATE utf8_unicode_ci;
SQL
end
def down
execute <<~ SQL
ALTER DATABASE #{ ActiveRecord :: Base . connection . current_database } CHARACTER SET latin1 COLLATE latin1_swedish_ci;
SQL
end
end
ConvertTablesCharacterSetAndCollationToUtf8 Migration
class ConvertTablesCharacterSetAndCollationToUtf8 < ActiveRecord :: Migration
def up
execute ( "SET foreign_key_checks = 0" )
latin_tables_sql = <<~ SQL
SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE table_schema = ' #{ ActiveRecord :: Base . connection . current_database } '
AND table_collation != 'utf8_unicode_ci';
SQL
results = ActiveRecord :: Base . connection . execute ( latin_tables_sql )
say "Total: #{ results . count } "
results . each do | result |
alter_table_sql = "ALTER TABLE #{ result [ 0 ] } CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;"
execute alter_table_sql
end
execute ( "SET foreign_key_checks = 1" )
end
end
Rails Migration tips
在调试该功能的时候,学到的一些 tips:
rake db:migrate:status
查看当前migration 的状态,包括版本信息等
rake db:migrate VERSION=33333333
migrate指定 version
rake db:rollback STEP=n
通过 STEP 参数指定回滚的范围
User.connection
可以用来检查当前数据库设置和连接的信息
ActiveRecord::Base.connection.current_database
获取当前连接的数据库
ActiveRecord::Migrator.current_version
查看当前的版本
在 Rails Console 或者 Runner 中执行 SQL语句,可以使用 ActiveRecord::Migration.execute("SQL")
参考文章:
How to change all columns’ and tables’ collation to ‘utf8_bin’ in MySQL
07 Mar 2017
基本使用方法
案例说明: 给已经存在的 Company 增加一个 size 属性, 属性包括 large, medium, small 三个选项
从 Rails4.1 开始,可以通过 ActiveRecord::Enum
来快速实现这样的功能
class Company < ActiveRecord :: Base
enum size: [ :large , :medium , :small ]
end
定义了 enum
后,Rails会自动产生下面这些方法
Company . sizes
# => {"large"=>0, "medium"=>1, "small"=>2}
# Scope methods
Company . large
Company . medium
Company . small
# Query methods
company . large?
company . medium?
# Action methods
company . large!
company . small!
Migration
enum
的实现是基于该字段是一个 integer 类型的。所以添加这个新的字段我们需要下面的 migration
class AddSizeToCompanies < ActiveRecord :: Migration
def change
add_column :companies , :size , :integer , null: false , default: 0
add_index :companies , :size
end
end
赋值操作
通过上面 Company.sizes 方法,我们看到 Rails 默认是从0开始对应的。所以上面的 migration 中默认值是0。当新建一个Company时,默认 company 的 size 是 large
company = Company . new
company . size # => "large"
# 多种赋值操作
company . size = :medium
company . medium? # => true
company . size = 2
company . small? # => true
company . large = 'large'
company . large? # => true
enum
会自动添加一些验证,如果给 size 属性赋错误的值,Rails会抛出异常
company . size = 5
# => ArgumentError: 5 is not a valid billing_category
company . size = :bala
# => ArgumentError: 'bala' is not a valid billing_category
当在前端的Form中填充到下拉列表中,可以这样做
f . input :size , collection: Company . sizes . keys . map { | s | [ s . titleize , s ]}, prompt: "Select a size"
需要注意的地方:
由于存到数据库的仍然是 number,所以如果有别的应用也使用同样的数据库,那么该应用需要知道对应关系。
Rails4.1 及之后的版本中,由于 enum
会自动产生一些方法,所以要特别注意选项的命名问题,尽量用明确的命名。 另外如果你还需要不同的属性,拥有相同的选项,那么你可以考虑这个 gem activerecord-enum-without-methods
同时在 Rails5 中,就可以使用_prefix
和_postfix
选项 来避免相应的问题。·
更好的Migration方法
上面的migration方法对于产品环境已经有数据的情况下,可能会产生问题。
对于Mysql 数据库,新加字段的时候对于已有的记录,mysql 会自动设置 NOT NULL
的已有记录为 default
但是对于 postgreSQL 就会出现下面的错误:
PG::NotNullViolation: ERROR: column “size” contains null values
这时可以先add_column
添加字段,不要加 NOT NULL 的限制,然后更新已有数据,然后再通过change_column_null
来添加 NOT NULL
限制。
17 Feb 2017
1. opacity: 0
相当于设置元素为透明
屏幕阅读器可读, 可以继续交互
2. visibility: hidden
.hide {
visibility: hidden;
}
屏幕阅读器不可读.
重新成visible后就可以重新进行交互了
后续的子节点可以设置为visible而变得可见
3. display: none
该元素不会被渲染, 所有后续的子节点也都是不会显示的
只可以通过dom api进行操作
屏幕阅读器不可读.
4. move out the viewport
.hide {
position: absolute;
top: -9999px;
left: -9999px;
}
该方法的目的是想保留交互的功能,同时不要影响布局.
屏幕阅读器可读.
5. clip-path
.hide {
clip-path: polygon(0px 0px,0px 0px,0px 0px,0px 0px);
}
通过裁剪的方法来隐藏.之前大家会用clip属性,现在clip属性已经废弃了,可以使用新的clip-path属性
想深入了解的可以看: 介绍clip-path属性
不过现在IE对clip-path还不支持
总结
用一张表来总结各种方法的区别:
方法
Occupy its position
User interaction
Screen Reader
Transition Animation
IE Support
opacity: 0
yes
yes
yes
yes
yes
visibility: hidden
yes
no
no
no
yes
display: none
no
no
no
no
yes
move out the viewport
no
yes
yes
yes
yes
clip-path
yes
no
yes
yes
no
17 Oct 2016
最近在找新的工作,澳洲这里几乎所有招Ruby工程师的公司都会要求你先做一个Code Test。就是给你一个需求,用代码实现。
Code Test通过后,才会进入面对面的面试,这其中还会有Pair Programming
,所以一定要自己好好实践。下面是我总结的一些经验:
需求
要很好的理解需求,不要忽略一些细节
不要忘记一些非功能性的需求(比如要提供github地址,或者发送zip包)
面向对象设计
要符合一些基本的OOD的原则, 比如Single Responsibility Principle
, Open Close Principle
充分利用语言的特性,设计好类,接口以及抽象和继承的层次
使用常用的设计模式:Factory Pattern
,Template Pattern
,Observer Pattern
,Command Pattern
,Null Ojbect
设想一两个需求变更的场景,验证自己的代码能够很容易的扩展
系统和架构的设计
尽量采用MVC分层架构模式,有Data Model
,Business Model
,Controller
, Display/Runner Class
异常的处理
自定义异常,然后做好异常的处理
提供友好的错误的提示
Test:
尽可能采用TDD,有清楚的commits,体现代码的更新过程
每个测试尽量保证一个assertion
保证足够高的测试覆盖率,100%是可以有的,可以使用simplecov
来帮助
要有集成测试,有正常的流程测试和非正常的流程测试
一定要测试好各种edge cases
提供易用的rake
tasks,方便运行相关的测试,比如单元测试,集成测试等
良好的接口设计
非常容易安装和执行,减少需要的依赖(比如尽量少的gems,Ruby版本)
有很好的命令行接口和帮助说明
要有一份README
,说明如何安装和使用
注意代码质量和规范:
按照产品级别的代码要求自己
采用Rubocop
进行质量检查
不要遗留任何的TODO
方法尽可能短小,不要超过5行
最后祝所有找工作的朋友好运!
26 Sep 2016
最近在做Spree
的定制开发,翻看其中的代码,发现其中有下面这么一段代码信息量是非常的大。所以就想通过这一小段代码来好好学习一下相关的Ruby知识。
# spree/api/lib/spree/api/engine.rb
def self . activate
Dir . glob ( File . join ( File . dirname ( __FILE__ ), "../../../app/**/*_decorator*.rb" )) do | c |
Rails . configuration . cache_classes ? require ( c ) : load ( c )
end
end
config . to_prepare & method ( :activate ). to_proc
就是这样的一段代码却包含了大量的Ruby和Rails的知识。其中最后一行config.to_prepare &method(:activate).to_proc
是本文重点解释的对象,接下来我们就一个一个来学习。
self.active
self.activate
方法其实不难理解,这是一个类方法,会在引入Spree的应用中查找app
目录下面文件名中带有_decorator
的文件,这些文件都是用来定制扩展Spree功能的文件。在这里,找到后会判断是否设置了Rails.configuration.cache_classes
这个选项,一般在开发环境中,该选项是false,这样当你修改代码后Rails会自动reload相关的文件。而在产品环境中,该选项为true,因为我们不会在产品环境修改代码让Rails自动reload。
后面require
和load
的最重要的区别就是,require
如果发现该文件已经加载过后就不会重新载入,而load
不管之前是否已经载入都会加载该文件。
重点来了,让我们看看下面这行信息量巨大的代码吧。一眼就看明白的同学,请自行绕过!
config . to_prepare & method ( :activate ). to_proc
method(:activate)
method
方法来自于Object
,会在当前的实例(上面的例子就是self)上查找通过参数指定的方法。找到后返回一个Method
的实例,这个Method的对象就是一个封装了方法所属的对象以及该对象的实例变量的闭包。
method(:activate).to_proc
接下来就是调用Method
对象的to_proc
方法,产生一个对应的Proc
对象。
&method(:activate).to_proc
在Proc
对象前面加上&
符号,作为参数传递,相当于给方法传递了一个block
,然后方法内部通过yield
调用block
,例如:
def to_prepare
yield if block_given?
puts 'Done prepare!'
end
相反,如果在接收的block
参数前面加上&
符号,那就相当于给方法传递了一个Proc
的对象,然后方法内部通过proc.call
来调用
def config ( & block )
block . call
puts 'done config!'
end
config do
puts 'I am block!'
end
config.to_prepare
这是来自Rails配置里面功能, 该配置是全局性的,会在所有的initializers运行之后运行。很重要的一点是,该配置中的代码,在production和test环境中默认只会执行一次,而在开发环境dev中,会在每次修改文件,重新发出请求时,Rails完成reload,在实际进行请求处理之前执行这段代码。(好绕的流程)
所以,你会看到上面的代码就是来重新加载一些定制Spree功能的代码。
在Rails中你还可以手动调用ActionDispatch::Reloader.to_prepare
来实现同样地功能。
另外还有一个与之对应的ActionDispatch::Reloader.to_cleanup
,区别是该callback
会在请求处理完成后执行。
上面提到开发环境的不同,主要是受到config.cache_classes
配置的控制。开发环境dev下,该属性为false
,所以Rails会发现有文件修改后,自动realod。
实例
介绍完这些基本的知识点后,你可能会觉得还是有点糊涂,为什么Spree中要这么做呢?
这种做法其实在Ruby的世界是一种常见的用法,主要就是利用闭包的特点,把相关的逻辑更好的组织和封装。关于这一点上面Spree的例子不是很完美。
下面有给出一段利用闭包中封装的实例变量的例子。
class MethodTest
class << self
attr_accessor :root_path
def activate
puts @root_path
puts helper
end
def helper
"I am helper method. #{ @root_path } "
end
end
end
def to_prepare
yield if block_given?
puts 'Done prepare!'
end
MethodTest . root_path = '/root'
to_prepare & MethodTest . method ( :activate ). to_proc