#Ruby

0x00 前言

Rails 6.0 发布已经有一段时间了,之前没有什么项目可以练手,最近总算遇到可以使用Rails 6开发的场景了。Rails 6中新增了一些功能和特性,有一些内容官方文档里并没有写的很具体,所以在摸索的过程中也遇到了一些坑点。这里以一个新工程从头到尾的构建,来简单记录在Rails6中使用jQuery和Bootstrap的过程。希望能够给新来的同学做个参考。

编程和码字的水平有限,如有错漏敬请指教,也请多包涵!下面就开始吧。

0x01 环境准备

  • 操作系统: Debian 10 ( 4.19.0-8-amd64 )
  • Ruby版本: ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
  • Rails版本: Rails 6.0.2.1
1
2
3
4
5
6
7
test@debian:~$uname -a
Linux debian 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64 GNU/Linux
test@debian:~$ruby -v
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
test@debian:~$rails -v
Rails 6.0.2.1

0x02 创建项目

这里新建一个项目“TestApp”,并在其中建立控制器“Test”和测试方法“test”:

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
test@debian:~$rails new TestApp
create
create README.md
create Rakefile
create .ruby-version
create config.ru
create .gitignore
create Gemfile
...
├─ [email protected]
├─ [email protected]
└─ [email protected]
Done in 59.74s.
Webpacker successfully installed 🎉 🍰
test@debian:~/TestApp$rails g controller Test test --no-stylesheets
Running via Spring preloader in process 22413
create app/controllers/test_controller.rb
route get 'test/test'
invoke erb
create app/views/test
create app/views/test/test.html.erb
invoke test_unit
create test/controllers/test_controller_test.rb
invoke helper
create app/helpers/test_helper.rb
invoke test_unit
invoke assets
invoke scss
test@debian:~$

创建好之后,修改test页面,添加一个表单和按钮,用于稍后测试Bootstrap。(在执行创建控制器和方法的命令后,Rails已经为我们自动添加了到Test控制器test方法的路由,所以不需要我们再新增路由。)

app/views/test/test.html.erb 代码:

1
2
3
4
5
6
7
<h1>Test#test</h1>
<p>Find me in app/views/test/test.html.erb</p>

<form>
<input id="test" type="text"/>
<button id="test_btn">点我</button>
</form>

启动Rails服务,访问http://localhost:3000/test/test:

1
2
3
4
5
6
7
8
9
10
11
test@debian:~/TestApp$rails s
=> Booting Puma
=> Rails 6.0.2.1 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.1 (ruby 2.6.5-p114), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3000
* Listening on tcp://[::1]:3000
Use Ctrl-C to stop

此时应用已经创建成功。

0x03 添加jQuery和Bootstrap库

这里采用yarn作为Javascript包管理器。在TestApp目录中运行bin/yarn,如果出现以下内容,说明yarn没有安装:

1
2
3
test@debian:~/TestApp$bin/yarn
Yarn executable was not detected in the system.
Download Yarn at https://yarnpkg.com/en/docs/install

安装方法在这里, 以当前使用的Debian 10为例,安装的方法是:

1
2
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list

安装好yarn之后,就可以用yarn添加jQuery包和Bootstrap包:

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
test@debian:~/TestApp$yarn add jquery 
yarn add v1.21.1
[1/4] Resolving packages...
[2/4] Fetching packages...
info [email protected]: The platform "linux" is incompatible with this module.
info "[email protected]" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
warning " > [email protected]" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning "webpack-dev-server > [email protected]" has unmet peer dependency "webpack@^4.0.0".
[4/4] Building fresh packages...

success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ [email protected]
info All dependencies
└─ [email protected]
Done in 9.98s.
test@debian:~/TestApp$yarn add bootstrap
yarn add v1.21.1
[1/4] Resolving packages...
[2/4] Fetching packages...
info [email protected]: The platform "linux" is incompatible with this module.
info "[email protected]" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
warning " > [email protected]" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning "webpack-dev-server > [email protected]" has unmet peer dependency "webpack@^4.0.0".
warning " > [email protected]" has unmet peer dependency "popper.js@^1.16.0".
[4/4] Building fresh packages...

success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ [email protected]
info All dependencies
└─ [email protected]
Done in 13.33s.

由于Bootstrap还需要基于popper.js,不装的话会报依赖错误,所以安装一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
test@debian:~/TestApp$yarn add popper.js
yarn add v1.21.1
[1/4] Resolving packages...
warning [email protected]: You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1
[2/4] Fetching packages...
info [email protected]: The platform "linux" is incompatible with this module.
info "[email protected]" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
warning " > [email protected]" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
warning "webpack-dev-server > [email protected]" has unmet peer dependency "webpack@^4.0.0".
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ [email protected]
info All dependencies
└─ [email protected]
Done in 11.78s.

安装的执行结果:

因为众所周知的原因,网速慢的同学就尴尬了,这里可能要等很久,所以你需要选一个科学一点的方式。

此时查看TestApp/node_modules目录,所需的前端库已经添加好了:

1
2
3
4
5
6
7
test@debian:~/TestApp$ls node_modules/jquery/
AUTHORS.txt bower.json dist external LICENSE.txt package.json README.md src
test@debian:~/TestApp$ls node_modules/bootstrap/
dist js LICENSE package.json README.md scss
test@debian:~/TestApp$ls node_modules/popper.js/
dist index.d.ts index.js.flow package.json README.md src
test@debian:~/TestApp$

0x04 使用Bootstrap

跟Rails5有所不同的是,Rails6采用webpack打包的方式,把需要打包的资源统一放在一个文件里(Rails6之前是采用Gemfile的方式引入第三方库,再通过Asset Pipeline汇聚)。

于是在安装好所需的前端库之后,就需要在app/javascript/packs/application.js中先引用它:

以Bootstrap为例,在其中添加import 'bootstrap'引入语句:

与此同时还需要在application.scss中引入Bootstrap的CSS样式表:

app/assets/stylesheets/application.scss文件中添加@import "bootstrap/dist/css/bootstrap";


2020/05/28 Patch

纠个错,如果想要通过@import "bootstrap/dist/css/bootstrap";的方式引用bootstrap相关的CSS,需要把app/assets/stylesheets/application.css文件重命名为app/assets/stylesheets/application.scss
如果直接使用app/assets/stylesheets/application.css的话,那么采用如下方式来引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* File: app/assets/stylesheets/application.css
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require bootstrap/dist/css/bootstrap
*= require_tree .
*= require_self
*/

Ending Patch


接着继续修改app/views/test/test.html.erb文件中的代码,添加相应的CSS类来测试Bootstrap是否已经引入成功:

1
2
3
4
5
6
7
8
9
<h1>Test#test</h1>
<p>Find me in app/views/test/test.html.erb</p>

<form>
<div class="form-group">
<input id="test" type="text" class="form-control"/>
<button id="test_btn" class="btn btn-success">点我</button>
</div>
</form>

启动应用,再次访问测试页面:

虽然界面有点丑,但是从渲染效果来看,Bootstrap已经成功引入了。

0x05 使用jQuery

继续修改app/javascript/packs/application.js,添加用于测试jQuery的代码:

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
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")


// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

import 'bootstrap'

$(document).on('turbolinks:load', function(){
$("#test_btn").click(function(){
alert($("#test").val());
});
});

保存并运行,刷新https://localhost:3000/test/test页面,发现添加的代码无法执行,控制台中有报错:

1
2
ReferenceError: $ is not defined 
application.js:21

这是因为jQuery库也需要引入一下,但是引入的方式有些不同,并不是在页面中直接引入,而是需要在config/webpack/environment.js中添加以下引入代码:

1
2
3
4
5
6
var webpack = require('webpack');
environment.plugins.append( 'Provide',
new webpack.ProvidePlugin({
$: 'jquery',
})
)

通过声明一个全局的导出,就可以在全局的JS代码文件中使用”$”符号了。

刷新并再次访问,代码能够运行,控制台也没有再报错,说明jQuery已经成功集成:

另一种方法

还有一种方法是通过在JS文件按中使用import $ from 'jquery'来使”$”符生效,但是这样比较繁琐,需要在每个JS文件中出现,所以不再介绍了,当然,也提供一下详细的代码参考

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
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")


// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

import 'bootstrap'
import $ from 'jquery' // 在每个JS文件中加上这一行,也可以替代声明全局变量。

$(document).on('turbolinks:load', function(){
$("#test_btn").click(function(){
alert($("#test").val());
});
});

0x06 总结

在网上主要参考了这篇文章。Rails 6中的变化还是挺大的,不知道webpack会不会真的成为Rails的未来,感觉之前使用coffee script来写JS的同学不是很多,但是我倒是已经非常习惯于使用它来开发前端功能了。

但是想必Rails的先驱们有着他们更成熟的考虑吧,那就一起继续探索好了。

在CentOS 6 机器中安装sassc Gem,报如下的错误:

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
[root@localhost ~ ]# gem install sassc
Building native extensions. This could take a while...
ERROR: Error installing sassc:
ERROR: Failed to build gem native extension.

current directory: /usr/local/rvm/gems/ruby-2.6.2/gems/sassc-2.0.1/ext
/usr/local/rvm/rubies/ruby-2.6.2/bin/ruby -rrubygems /usr/local/rvm/rubies/ruby-2.6.2/lib/ruby/gems/2.6.0/gems/rake-12.3.2/exe/rake RUBYARCHDIR\=/usr/local/rvm/gems/ruby-2.6.2/extensions/x86_64-linux/2.6.0/sassc-2.0.1 RUBYLIBDIR\=/usr/local/rvm/gems/ruby-2.6.2/extensions/x86_64-linux/2.6.0/sassc-2.0.1
cd libsass
make lib/libsass.so
mkdir lib
cc -Wall -O2 -I /usr/local/rvm/gems/ruby-2.6.2/gems/sassc-2.0.1/ext/libsass/include -fPIC -fPIC -c -o src/cencode.o src/cencode.c
src/cencode.c: In function ‘base64_encode_block’:
src/cencode.c:50: warning: empty declaration
src/cencode.c:64: warning: empty declaration
g++ -Wall -O2 -std=c++0x -I /usr/local/rvm/gems/ruby-2.6.2/gems/sassc-2.0.1/ext/libsass/include -fPIC -fPIC -c -o src/ast.o src/ast.cpp
In file included from src/ast.cpp:2:
src/ast.hpp: In member function ‘virtual size_t Sass::Vectorized<T>::hash()’:
src/ast.hpp:336: error: expected initializer before ‘:’ token
src/ast.hpp:339: error: expected primary-expression before ‘}’ token
src/ast.hpp:339: error: expected ‘;’ before ‘}’ token
src/ast.hpp:339: error: expected primary-expression before ‘}’ token
src/ast.hpp:339: error: expected ‘)’ before ‘}’ token
src/ast.hpp:339: error: expected primary-expression before ‘}’ token
src/ast.hpp:339: error: expected ‘;’ before ‘}’ token
src/ast.hpp: In constructor ‘Sass::Hashed::Hashed(size_t)’:
src/ast.hpp:373: error: ‘class Sass::ExpressionMap’ has no member named ‘reserve’
src/ast.hpp: In member function ‘Sass::Hashed& Sass::Hashed::operator+=(Sass::Hashed*)’:
src/ast.hpp:402: error: expected initializer before ‘:’ token
src/ast.hpp:406: error: could not convert ‘Sass::Hashed::reset_duplicate_key()’ to ‘bool’
src/ast.hpp:407: error: expected primary-expression before ‘return’
src/ast.hpp:407: error: expected ‘)’ before ‘return’
In file included from src/ast.cpp:2:
src/ast.hpp: In member function ‘virtual size_t Sass::Map::hash()’:
src/ast.hpp:1153: error: expected initializer before ‘:’ token
src/ast.hpp:1157: error: expected primary-expression before ‘}’ token
src/ast.hpp:1157: error: expected ‘;’ before ‘}’ token
src/ast.hpp:1157: error: expected primary-expression before ‘}’ token
src/ast.hpp:1157: error: expected ‘)’ before ‘}’ token
src/ast.hpp:1157: error: expected primary-expression before ‘}’ token
src/ast.hpp:1157: error: expected ‘;’ before ‘}’ token
src/ast.hpp: In member function ‘virtual size_t Sass::Function_Call::hash()’:
src/ast.hpp:1522: error: expected initializer before ‘:’ token
src/ast.hpp:1524: error: expected primary-expression before ‘}’ token
src/ast.hpp:1524: error: expected ‘;’ before ‘}’ token
src/ast.hpp:1524: error: expected primary-expression before ‘}’ token
src/ast.hpp:1524: error: expected ‘)’ before ‘}’ token
src/ast.hpp:1524: error: expected primary-expression before ‘}’ token
src/ast.hpp:1524: error: expected ‘;’ before ‘}’ token
src/ast.hpp: In member function ‘virtual size_t Sass::Number::hash()’:
src/ast.hpp:1614: error: expected initializer before ‘:’ token
src/ast.hpp:1616: error: expected primary-expression before ‘for’
src/ast.hpp:1616: error: expected ‘;’ before ‘for’
src/ast.hpp:1616: error: expected primary-expression before ‘for’
src/ast.hpp:1616: error: expected ‘)’ before ‘for’
src/ast.hpp:1616: error: expected initializer before ‘:’ token
src/ast.hpp:1618: error: expected primary-expression before ‘}’ token
src/ast.hpp:1618: error: expected ‘;’ before ‘}’ token
src/ast.hpp:1618: error: expected primary-expression before ‘}’ token
src/ast.hpp:1618: error: expected ‘)’ before ‘}’ token
src/ast.hpp:1618: error: expected primary-expression before ‘}’ token
src/ast.hpp:1618: error: expected ‘;’ before ‘}’ token
src/ast.hpp: In member function ‘bool Sass::String_Schema::has_interpolants()’:
src/ast.hpp:1791: error: expected initializer before ‘:’ token
src/ast.hpp:1794: error: expected primary-expression before ‘return’
src/ast.hpp:1794: error: expected ‘;’ before ‘return’
src/ast.hpp:1794: error: expected primary-expression before ‘return’
src/ast.hpp:1794: error: expected ‘)’ before ‘return’
src/ast.hpp: In member function ‘virtual size_t Sass::String_Schema::hash()’:
src/ast.hpp:1801: error: expected initializer before ‘:’ token
src/ast.hpp:1803: error: expected primary-expression before ‘}’ token
src/ast.hpp:1803: error: expected ‘;’ before ‘}’ token
src/ast.hpp:1803: error: expected primary-expression before ‘}’ token
src/ast.hpp:1803: error: expected ‘)’ before ‘}’ token
src/ast.hpp:1803: error: expected primary-expression before ‘}’ token
src/ast.hpp:1803: error: expected ‘;’ before ‘}’ token
src/ast.hpp: In member function ‘virtual size_t Sass::Compound_Selector::hash()’:
src/ast.hpp:2750: error: ‘template<class T> class Sass::Vectorized’ used without template parameters
src/ast.hpp: In member function ‘virtual size_t Sass::Selector_List::hash()’:
src/ast.hpp:2997: error: ‘template<class T> class Sass::Vectorized’ used without template parameters
src/ast.hpp: In member function ‘virtual void Sass::Selector_List::set_media_block(Sass::Media_Block*)’:
src/ast.hpp:3014: error: expected initializer before ‘:’ token
src/ast.hpp:3017: error: expected primary-expression before ‘}’ token
src/ast.hpp:3017: error: expected ‘;’ before ‘}’ token
src/ast.hpp:3017: error: expected primary-expression before ‘}’ token
src/ast.hpp:3017: error: expected ‘)’ before ‘}’ token
src/ast.hpp:3017: error: expected primary-expression before ‘}’ token
src/ast.hpp:3017: error: expected ‘;’ before ‘}’ token
src/ast.hpp: In member function ‘virtual bool Sass::Selector_List::has_placeholder()’:
src/ast.hpp:3019: error: expected initializer before ‘:’ token
src/ast.hpp:3022: error: expected primary-expression before ‘return’
src/ast.hpp:3022: error: expected ‘;’ before ‘return’
src/ast.hpp:3022: error: expected primary-expression before ‘return’
src/ast.hpp:3022: error: expected ‘)’ before ‘return’
src/ast.cpp: In member function ‘virtual bool Sass::Selector_List::find(bool (*)(Sass::AST_Node_Obj))’:
src/ast.cpp:35: error: expected initializer before ‘:’ token
src/ast.cpp:39: error: expected primary-expression before ‘return’
src/ast.cpp:39: error: expected ‘;’ before ‘return’
src/ast.cpp:39: error: expected primary-expression before ‘return’
src/ast.cpp:39: error: expected ‘)’ before ‘return’
src/ast.cpp: In member function ‘virtual bool Sass::Compound_Selector::find(bool (*)(Sass::AST_Node_Obj))’:
src/ast.cpp:45: error: expected initializer before ‘:’ token
src/ast.cpp:49: error: expected primary-expression before ‘return’
src/ast.cpp:49: error: expected ‘;’ before ‘return’
src/ast.cpp:49: error: expected primary-expression before ‘return’
src/ast.cpp:49: error: expected ‘)’ before ‘return’
src/ast.cpp: In member function ‘virtual void Sass::Arguments::set_delayed(bool)’:
src/ast.cpp:98: error: expected initializer before ‘:’ token
src/ast.cpp:102: error: expected primary-expression before ‘}’ token
src/ast.cpp:102: error: expected ‘)’ before ‘}’ token
src/ast.cpp:102: error: expected primary-expression before ‘}’ token
src/ast.cpp:102: error: expected ‘;’ before ‘}’ token
src/ast.cpp: In member function ‘virtual bool Sass::Compound_Selector::has_parent_ref() const’:
src/ast.cpp:171: error: expected initializer before ‘:’ token
src/ast.cpp:174: error: expected primary-expression before ‘return’
src/ast.cpp:174: error: expected ‘;’ before ‘return’
src/ast.cpp:174: error: expected primary-expression before ‘return’
src/ast.cpp:174: error: expected ‘)’ before ‘return’
src/ast.cpp: In member function ‘virtual bool Sass::Compound_Selector::has_real_parent_ref() const’:
src/ast.cpp:179: error: expected initializer before ‘:’ token
src/ast.cpp:182: error: expected primary-expression before ‘return’
src/ast.cpp:182: error: expected ‘;’ before ‘return’
src/ast.cpp:182: error: expected primary-expression before ‘return’
src/ast.cpp:182: error: expected ‘)’ before ‘return’
src/ast.cpp: In member function ‘virtual bool Sass::Compound_Selector::is_superselector_of(Sass::Selector_List_Obj, std::string)’:
src/ast.cpp:846: error: expected initializer before ‘:’ token
src/ast.cpp:849: error: expected primary-expression before ‘return’
src/ast.cpp:849: error: expected ‘;’ before ‘return’
src/ast.cpp:849: error: expected primary-expression before ‘return’
src/ast.cpp:849: error: expected ‘)’ before ‘return’
src/ast.cpp: In member function ‘Sass::Selector_List* Sass::Complex_Selector::resolve_parent_refs(std::vector<Sass::SharedImpl<Sass::Selector_List>, std::allocator<Sass::SharedImpl<Sass::Selector_List> > >&, Sass::Backtraces&, bool)’:
src/ast.cpp:1408: error: expected initializer before ‘:’ token
src/ast.cpp:2226: error: expected primary-expression at end of input
src/ast.cpp:2226: error: expected ‘;’ at end of input
src/ast.cpp:2226: error: expected primary-expression at end of input
src/ast.cpp:2226: error: expected ‘)’ at end of input
src/ast.cpp:2226: error: expected statement at end of input
src/ast.cpp:2226: error: expected ‘}’ at end of input
src/ast.cpp:2226: error: expected ‘}’ at end of input
src/ast.cpp: At global scope:
src/ast.cpp:2226: error: expected ‘}’ at end of input
src/units.hpp:11: warning: ‘Sass::PI’ defined but not used
make: *** [src/ast.o] Error 1
rake aborted!
Command failed with status (2): [make lib/libsass.so...]
/usr/local/rvm/gems/ruby-2.6.2/gems/sassc-2.0.1/lib/tasks/libsass.rb:31:in `block (2 levels) in <top (required)>'
/usr/local/rvm/gems/ruby-2.6.2/gems/sassc-2.0.1/lib/tasks/libsass.rb:13:in `block (3 levels) in <top (required)>'
/usr/local/rvm/gems/ruby-2.6.2/gems/sassc-2.0.1/lib/tasks/libsass.rb:12:in `block (2 levels) in <top (required)>'
Tasks: TOP => lib/libsass.so
(See full trace by running task with --trace)

rake failed, exit code 1

Gem files will remain installed in /usr/local/rvm/gems/ruby-2.6.2/gems/sassc-2.0.1 for inspection.
Results logged to /usr/local/rvm/gems/ruby-2.6.2/extensions/x86_64-linux/2.6.0/sassc-2.0.1/gem_make.out

经排查,是因为GCC的版本过低导致的。

按照这个步骤,检查下GCC版本,如果是同样的版本和报错,那么可以参考以下的解决方法:

1
2
[root@localhost ~]# gcc --version | head -n1
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-23)

如果GCC是这个版本,那就安装新版本的的GCC:

1
2
yum install centos-release-scl-rh
yum install devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-binutils

安装好以后,再次配置GCC环境并检查版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost ~]# cat << _EOF_ > /etc/profile.d/devtoolset.sh
> #!/bin/bash
> source scl_source enable devtoolset-7
> _EOF_
[root@localhost ~]#
[root@localhost ~]# gcc --version | head -n1
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-23)
[root@localhost ~]# cat /etc/profile.d/devtoolset.sh
#!/bin/bash
source scl_source enable devtoolset-7
[root@localhost ~]# source /etc/profile.d/devtoolset.sh
[root@localhost ~]# gcc --version | head -n1
gcc (GCC) 7.3.1 20180303 (Red Hat 7.3.1-5)

确认GCC配置好以后,再次执行bundle或者执行gem install sassc来安装即可。

找Rails靠谱的编辑器是不是找了好久了?别找了,就用Simditor吧。
要如何在Rails中完美集成这样一个简洁大气又好用的编辑器呢?我们都知道,在高版本的Rails中使用CoffeeScript已经成为了一种习惯。所以本文将以CoffeeScript作为粘合剂来演示如何集成Simditor编辑器到Rails应用中,同时采用Rails原生的ActiveStorage作为上传支撑。

事先声明下我的环境配置:
Ruby: ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]
Rails: Rails 5.2.2

1. 准备工作

首先我们新建一个Rails项目,在命令行中,运行rails new Demo,待工程创建好以后我们进入Demo目录,新建一个脚手架。这里我们就以Article作为脚手架的名字,运行rails g scaffold Article title:string content:text

创建好之后,运行rails db:migrate,迁移数据库

此时,将simeditor添加到Gemfile中。修改Gemfile,加入以下内容并执行bundle(simditor需要jquery的支持)。

1
2
gem 'jquery-rails'
gem 'simditor'

并在app/assets/javascripts/application.js中加入simditor的引用,此处需要注意,下面两行代码需要添加在//= require_tree这行代码之前,否则会出错。

1
2
3
//= require jquery
//= require simditor
//= require_tree .

同时需要在app/assets/stylesheets/application.css中加入simditor的样式文件:

1
*= require simditor

启动Rails应用,在浏览器中访问/articles/new,此时的界面如下:

到这里,准备工作就完成了。

2. 修改编辑框,集成Simditor

按照Rails的命名惯例,此处编辑框textarea的ID是article_content,我们的CoffeeScript脚本中,则需用选择器来绑定这个text_area。

修改app/assets/javascripts/article.coffee,加入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$(document).on "turbolinks:load", ->
editor = new Simditor(
textarea: $('#article_content')
toolbar: [ 'bold', 'italic', 'underline', 'strikethrough', 'color',
'code', 'blockquote', 'link', 'image', 'ol', 'ul', 'table', 'indent','outdent','alignment']
pasteImage: true,
fileKey: 'file',
upload: {
url: '/images',
params: {"content":"image"},
connectionCount: 3,
leaveConfirm: '有图片正在上传,确定要离开?'
})

再次在浏览器中访问/articles/new,界面变成了这样:

此时Simditor已经集成到了应用中了。

3. 使用ActiveStorage配置上传功能

ActiveStorage是Rails 5.2 以后推出的用于专门管理Web上传内容的组件,和ActiveRecord一样,是一个很方便同时又很强大的Rails Web开发组件。想要集成基于ActiveStorage的上传功能,首先要在我们的应用中安装ActiveStorage:

运行rails active_storage:install然后再运行rails db:migrate:

1
2
3
4
5
6
7
8
9
root@debian:~/Demo# rails active_storage:install
Copied migration 20190327123314_create_active_storage_tables.active_storage.rb from active_storage
root@debian:~/Demo# rails db:migrate
== 20190327123314 CreateActiveStorageTables: migrating ========================
-- create_table(:active_storage_blobs)
-> 0.0050s
-- create_table(:active_storage_attachments)
-> 0.0027s
== 20190327123314 CreateActiveStorageTables: migrated (0.0086s) ===============

迁移完成以后,我们新建一个Upload控制器,使用rails g controller Upload命令生成它。修改Upload控制器代码,位于app/controllers/upload_controller.rb,添加一个upload方法:

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
#app/controllers/upload_controller.rb
class UploadController < ApplicationController

skip_before_action :verify_authenticity_token, :only => [:upload]

def upload

puts "*"*64
puts params

blob = ActiveStorage::Blob.create_after_upload!(
io: params[:image],
filename: params[:image].original_filename,
content_type: params[:image].content_type
)

if blob.save
success = true
msg = 'upload success'
else
success = false
msg = "upload error"
end

render json: { :success=> success, :msg=>msg, :file_path=> rails_blob_path(blob) }

end

end


修改路由配置文件,指定我们的上传路由:

1
2
3
4
5
6
7
#config/routes.rb
Rails.application.routes.draw do
post 'images/upload' => 'upload#upload'
resources :articles
root to: 'articles#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

还要稍微修改一下我们的articles.coffee文件,改一下其中的上传路径和参数,用于Ajax提交图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//app/assets/javascripts/articles.coffee
$(document).on "turbolinks:load", ->
editor = new Simditor(
textarea: $('#article_content')
toolbar: [ 'bold', 'italic', 'underline', 'strikethrough', 'color',
'code', 'blockquote', 'link', 'image', 'ol', 'ul', 'table', 'indent','outdent','alignment']
pasteImage: true,
fileKey: 'file',
upload: {
url: '/images/upload', //<==上传接口
fileKey: 'image', //<=参数名称
params: {"content":"image"},
connectionCount: 3,
leaveConfirm: '有图片正在上传,确定要离开?'
})

至此,已经可以在页面中正常地上传文件了,也可以直接从粘贴板复制文件,Simditor会自动上传文件:

本文涉及到的代码已经上传到Github,仓库链接:https://github.com/Chorder/RailsSimditorActiveStorageDemo

最近睡前把《Ruby元编程》作为枕边书,复习一些元编程技巧。顺手整理记录下这些技巧的同时,也为了这门语言能更广泛地传播,希望有更多的人喜欢Ruby这个神器。

一切都是对象

来到Ruby的世界,请你首先不要被这一切搞晕,实际上这些反而让Ruby的对象模型概念变得更加清晰。一切都是对象,对象是一个类,类也是一个对象。而实际上类是一个带有特殊功能的模块。

1
2
3
4
5
6
7
8
9
10
11
2.5.1 :001 > Class.superclass
=> Module
2.5.1 :002 > Module.superclass
=> Object
2.5.1 :003 > Object.superclass
=> BasicObject
2.5.1 :004 > BasicObject.superclass
=> nil
2.5.1 :005 > Kernel.class
=> Module

模块的超类是BasicObject,BasicObject才是Ruby类系统的根。BasicObject派生了Objce和Kernel,Object又派生出了Module,Module派生出Class。
这像不像道德经里所说的“道生一,一生二,二生三,三生万物”? 再说下去更迷糊了,去Ruby的世界里自己领悟吧。Ruby诞生自亚洲,一直觉得它是一门充满东方哲学的编程语言。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2.5.1 :001 > Class.ancestors
=> [Class, Module, Object, Kernel, BasicObject]
2.5.1 :002 > Class.superclass
=> Module
2.5.1 :003 > Module.ancestors
=> [Module, Object, Kernel, BasicObject]
2.5.1 :004 > Module.superclass
=> Object
2.5.1 :005 > Object.ancestors
=> [Object, Kernel, BasicObject]
2.5.1 :006 > Object.superclass
=> BasicObject
2.5.1 :007 > BasicObject.superclass
=> nil
2.5.1 :008 > Kernel.ancestors
=> [Kernel]
2.5.1 :009 > BasicObject.ancestors
=> [BasicObject]

猴子补丁(Monkey Patch)

在元编程的所有技巧中,首先需要了解的就是Ruby的“猴子补丁”,这是一种打开类的技巧。不同于其他面向对象语言,Ruby的任何类都可以在运行态打“补丁”的,称之为Monkey Patch。

例如,对字符类String,可以在任何地方打开这个类,对它添加一些方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2.5.1 :001 > class String
2.5.1 :002?> def test_method
2.5.1 :003?> return self+"AAA"
2.5.1 :004?> end
2.5.1 :005?> end
=> :test_method
2.5.1 :006 > s="aaa"
=> "aaa"
2.5.1 :007 > s.class
=> String
2.5.1 :008 > s.test_method
=> "aaaAAA"
2.5.1 :009 >

在上面的操作中,我们可以看到,对于Ruby的内置类String,我们为其添加了test_method方法,在下文中,任何的String对象便具有了新添加的这个方法,这就是Ruby打开类的魔术。

你甚至可以对内核模块Kernel打一个Monkey Patch,让你的方法在下文中得到全局支持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2.5.1 :001 > test
Traceback (most recent call last):
3: from /home/chorder/.rvm/rubies/ruby-2.5.1/bin/irb:11:in <main>
2: from (irb):1
1: from (irb):1:in test
ArgumentError (wrong number of arguments (given 0, expected 2..3))
2.5.1 :002 > module Kernel
2.5.1 :003?> def test
2.5.1 :004?> puts "OK"
2.5.1 :005?> end
2.5.1 :006?> end
=> :test
2.5.1 :007 > test
OK
=> nil
2.5.1 :008 >

不同的是,Kernel是一个模块,需要用module关键字来打开。test方法一开始是不存在的,把它作为“补丁”加入到Kernel模块以后,就可以在下文调用了。

Monkey Patch在有些编程语言里是个贬义词,但在Ruby里,它是一个很实用的功能。

细化(refine)

如你所见,Ruby打开类的作用是如此神奇,以至于Monkey Patch一不小心可能会引发一些大的问题。因此有时候需要配合类的细化功能一起使用。

细化的用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2.5.1 :001 > module Refine
2.5.1 :002?> refine String do
2.5.1 :003 > def test
2.5.1 :004?> return self+"TEST"
2.5.1 :005?> end
2.5.1 :006?> end
2.5.1 :007?> end
=> #<refinement:String@Refine>
2.5.1 :008 > "AAA".test
Traceback (most recent call last):
2: from /home/chorder/.rvm/rubies/ruby-2.5.1/bin/irb:11:in <main>
1: from (irb):8
NoMethodError (private method test called for "AAA":String)
2.5.1 :009 > module Test
2.5.1 :010?> using Refine
2.5.1 :011?> "AAA".test
2.5.1 :012?> end
=> "AAATEST"
2.5.1 :013 >

在一个模块中使用refine关键字定义细化之后,在另一个模块中使用using关键字引入这个用于定义细化的模块,细化定义的作用于就只限于调用细化模块的模块内部。

命名空间(Name Space)

在Ruby中,使用::标识引入不同作用域中的类和模块,用于界定类和模块,防止命名冲突,也让源代码的结构更加清晰。

1
2
3
4
5
6
7
8
9
10
11
12
13
2.5.1 :001 > module A
2.5.1 :002?> module B
2.5.1 :003?> class C
2.5.1 :004?> D="Constant"
2.5.1 :005?> end
2.5.1 :006?> end
2.5.1 :007?> end
=> "Constant"
2.5.1 :008 >
2.5.1 :009 > A::B::C::D
=> "Constant"
2.5.1 :010 >

系统环境:

  • Ruby: ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
  • Rails: Rails 5.2.2
  • Gem: 2.7.7
  • SQLite: sqlite3 (1.4.0, 1.3.13)

今天在基于上面的环境使用Rails新建项目的时候,报了一个ActiveRecord::ConnectionNotEstablished错误。
根据排查,是由于SQLite3 gem版本问题导致的。
具体的报错内容如下:

1
Puma caught this error: Error loading the 'sqlite3' Active Record adapter. Missing a gem it depends on? can't activate sqlite3 (~> 1.3.6), already activated sqlite3-1.4.0. Make sure all dependencies are added to Gemfile. (LoadError)

页面报错如下:

1
2
3
ActiveRecord::ConnectionNotEstablished
No connection pool with 'primary' found.

后来找到了解决方法,修改Gemfile,将sqlite3的版本降级到1.4.0以下即可。
修改Gemfile:

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
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.5.1'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.2'
# Use sqlite3 as the database for Active Record
gem 'sqlite3', '< 1.4.0' # <<修改这里,将sqlite3版本设定为小于1.4.0
# Use Puma as the app server
gem 'puma', '~> 3.11'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'mini_racer', platforms: :ruby

# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails', '~> 4.2'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use ActiveStorage variant
# gem 'mini_magick', '~> 4.8'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
gem 'listen', '>= 3.0.5', '< 3.2'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
# Easy installation and use of chromedriver to run system tests with Chrome
gem 'chromedriver-helper'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]


修改完后执行bundle即可。

Ruby有着便捷强大的Gem包管理机制,作为五十年后人类使用最多的编程语言,有必要提前普及一下Ruby Gem的编写制作教程,帮助大家更好的使用Ruby,使用Gem,最好也能让Ruby的Gem库更加丰富,社区能够更加活跃。那么如何从零开始制作和发布自己的Gem呢?一起来动手操作一下。

制作和发布Ruby Gem包一共分如下几个步骤:

  1. 在RubyGems上注册帐号(如果你只想在本地或团队内部使用你的Gem,可以跳过这一步)
  2. 编写和打包Gem(gem build xxx.gemspec)
  3. 测试Gem(mang自mu信者可以跳过这一步)
  4. 发布Gem(gem push xxx)

1. 注册RubyGems帐号

RubyGems.ORG 是一个大型的Gems仓库,你可以现在就加入它,贡献你的Gem。这一步想必无需多说,注册完成之后,你就拥有了属于自己的RubyGems帐号,别人就可以看到你的主页和你发布的Gems。

注册完成之后,你还需要将你的crendentials保存到本地,方便后续直接在命令行使用gem命令。运行

1
curl -u 你的用户名 https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials

2. 编写和打包Gem

编写Gem

我们这次要发布的Gem叫做template_chorder,你也可以改名成任意你想要的名字。首先我们创建一个工程目录,我的目录名叫做GemTemplate,其中包含如下内容:

其中,template.gemspec是项目的描述文件,gem build命令会基于这个文件进行打包。

template.gemspec内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

Gem::Specification.new do |s|
s.name = 'template_chorder'
s.version = '0.0.0'
s.date = '2019-01-11'
s.summary = "Template!"
s.description = "A simple template gem"
s.authors = ["Chorder"]
s.email = '[email protected]'
s.files = ["lib/template.rb"]
s.homepage = 'https://chorder.net/2019/01/11/rubygemtemplate/'
s.license = 'MIT'
end

其中s.name就是将来这个包发布出去的名字。默认的,我们需要将工程的主代码放在lib目录里面,这是ruby的约定。我们的Gem只包含一个很简单的功能,只需要放置全部代码在一个文件当中即可。

lib/template.rb的代码如下:

1
2
3
4
5
class Template
def self.test
puts "Hello, Ruby!"
end
end

就是这么简单。

打包Gem

在工程的根目录,运行

1
gem build template.gemspec 

工程根目录将会产生一个 template_chorder-0.0.0.gem文件。
就是这么简单。

3. 测试Gem

对于刚刚生成的template_chorder-0.0.0.gem,运行

1
gem install template_chorder-0.0.0.gem

来安装它。

安装好以后,就可以在irb解释器中引入和运行它了。完整的过程如下:

疑问
这里其实是有点疑问的,就是为什么gem的名字叫做template_chorder,而在irb中引入的却是template。确实,如果gem包中的文件取名不严谨的话,可能会与其他包产生冲突。至于如何消除冲突,请君自己先思考下。

卸载Gem包,运行 gem uninstall template_chorder

2. 发布Gem

制作好这样一个不负责任的Gem包以后,就可以厚颜无耻地在RubyGems上发布它。但是为了社区的环境卫生清洁,尽量还是不要发布那些粗制滥造的包,就以此为戒吧。
发布Gem包,运行 gem push template_chorder-0.0.0.gem,如果第1步当中的帐号注册和保存
crendentials文件都没有问题,那么包就可以发布出去了。

本文的包发布在:https://rubygems.org/gems/template_chorder

希望能够对你有所帮助。

多进程示例:

multiprocess.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def test_proc(name,second)
Process.setproctitle("PROC-#{Process.pid}" )
sleep second
puts "#{name} - #{Process.pid} |G/U:#{Process.euid}/#{Process.gid}|PPID:#{Process.ppid}"
Process.exit
puts "Never going here"
end

puts "Main process start - #{Process.pid}"

10.times do |t|

Process.fork do
test_proc("PROC-#{t}",5+t)
end

end

#Process.exit
Process.waitall
puts "Main process exit - #{Process.pid}"

运行结果:

多线程示例:

multithread.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14

require "thread"

threads=[]
6.times do |t|
threads <<Thread.new do
puts "Thread-#{t} start: Process: #{Process.pid} "
sleep 5
puts "Thread-#{t} Done: Process: #{Process.pid} "
end
end

threads.each{|t|t.join}

运行结果:

简介

Devise是Rails中一个功能强大、逻辑复杂的用于实现站点用户管理和登录的组件。鉴于Ruby之不重复造轮子的精神思想,Devise是值得去深入研究学习一下的。由于Devise本身的复杂性,这里对搭建的过程做一个记录,也借此分享一下基于Devise实现最基本的站点用户管理和登录的过程。

环境准备

需要在机器上安装Ruby和Rails。
本次编程环境:

  • Debian 9(Kernel 4.9.0-6-amd64)
  • ruby-2.4.1
  • Rails 5.2.0

开始

新建项目

首先新建一个Rails项目TestSite,创建Post scaffold,包含title和body两个字段

1
2
rails new TestSite
rails g scaffold Post title:string body:string

安装Devise

接着在Gemfile中添加devise

1
gem 'devise'

在项目中执行bundle,以及执行devise:install

1
2
bundle
rails generate devise:install

由于我们暂时只在本地测试,因此修改config/environments/development.rb文件,在其中加入mailer配置:

1
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

生成模型

指定Devise将要运行在哪个模型上,这里使用user作为模型,那么运行:

1
rails generate devise User

下一步,就是运行迁移:

1
rails db:migrate

控制器过滤

如果希望在应用运行之前校验用户身份,在ApplicationController中加入以下过滤器:

1
before_action :authenticate_user!

指定路由:

1
root to: 'posts#index'

添加注销功能

在Application的首页,加上用户注销的链接:

1
<%= link_to "Logout", destroy_user_session_path, method: "DELETE"  if current_user %>

配置视图

如果想要自定义登陆界面的视图,则运行以下命令:

1
rails g devise:views users

这里后面带模型名称,就会创建在这个模型范围内的视图(建议这样做,便于以后拓展多重用户身份模型)

此时就可以在相应的视图中自定义登录、注册、重置密码等相应的界面了。

配置控制器

生成控制器就和生成视图的流程是类似的,运行:

1
rails generate devise:controllers users

这样控制器就创建好了。

控制器这一段是值得好好说道说道的。
首先观察下控制器,控制器是可以追加功能的,也可以覆盖原来的方法,不过不推荐这样做,因为从目前经验来看devise自带的控制器已经足够健壮了。但是有时候,我们需要在原有的基础上新增一些功能,例如记录登录日志、增加一点自定义认证方式等等。这里简单介绍下如何在控制器中新增功能。
上一步操作,为我们生成了以下控制器:

1
2
3
4
5
6
app/controllers/users/confirmations_controller.rb
app/controllers/users/passwords_controller.rb
app/controllers/users/registrations_controller.rb
app/controllers/users/sessions_controller.rb
app/controllers/users/unlocks_controller.rb
app/controllers/users/omniauth_callbacks_controller.rb

这里以登录为例,如果我们想要改写登录功能,首先我们要在路由中改写我们要复写的控制器路由:

routes.rb中:

1
2
3
4
5
6
7
8
9
10
11
Rails.application.routes.draw do
#devise_for :users
resources :posts
root to: 'posts#index'

devise_for :users, controllers: {
# 这一行定义了有关sessions的控制选项,交由我们即将复写的sessions控制器处理。
sessions: 'users/sessions'
}

end

接着修改app/controllers/users/sessions_controller.rb,这里我们简单实现一个功能,当用户在前台登陆时,在后台的console中输出用户的登录信息。复写create方法:

1
2
3
4
5
6
7
# POST /resource/sign_in
def create
super
puts "="*64
puts resource.email
puts "="*64
end

尝试在前台登录,控制台输出了我们想要的信息:

待续

1.数组遍历方法总结

1
array = (1..10).to_a 
1
2
3
4
5
length = array.length 
length.times do t
print "#{array[t]} "
end
puts "n"
1
2
3
4
5
length = array.length-1 
for i in 0..length do
print "#{array[i]} "
end
puts "n"
1
2
3
4
for i in array do 
print "#{i} "
end
puts "n"
1
2
array.each{x print x," "} 
puts "n"
1
2
3
4
5
6
7
length = array.length 
i = 0
while i< length do
print "#{array[i]} "
i = i+1
end
puts "n"
1
2
3
4
5
6
7
length = array.length 
i = 0
until i==length do
print "#{array[i]} "
i += 1
end
puts "n"
1
2
array.each_index do i 
print "#{array[i]} "

2.Ruby连接数据库

  • mysql
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    require 'mysql'  
    begin
    db = Mysql.init
    db.options(Mysql::SET_CHARSET_NAME, 'utf8')
    db = Mysql.real_connect("127.0.0.1", "root", "123456", "test", 3306)
    db.query("SET NAMES utf8")
    db.query("drop table if exists tb_test")
    db.query("create table tb_test (id int,
    text LONGTEXT) ENGINE=MyISAM DEFAULT CHARSET=utf8")
    db.query("insert into tb_test (id, text) values (
    1,'first line'),(2,'second line')")
    printf "%d rows were inserted\n",db.affected_rows
    rslt = db.query("select text from tb_test")
    while row = rslt.fetch_row do
    puts row[0]
    end
    rescue Mysql::Error => e
    puts "Error code: #{e.errno}"
    puts "Error message: #{e.error}"
    puts "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate")
    ensure
    db.close if db
    end
  • redis
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #!/bin/ruby

    require 'redis'

    def writeToFile(file,content)
    fp = File.new(file,"a+")
    if fp
    fp.syswrite(content)
    else
    puts "..."
    end
    end

    def connect(host)
    redis = Redis.new(:host => host,:port => 6379)
    redis.info.keys.each do |key|
    puts "#{key}:\t"+redis.info["#{key}"]
    end
    end

    connect("1.1.1.1")
  • sqlite3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    require 'sqlite3'

    db = SQLite3::Database.new('test.db')

    db.execute("create table test(
    ID integet not null,
    Username varchar(20) null,
    Password varchar(64) null)")
    db.execute("insert into test(ID.Username,Password)
    values('0','admin','admin')")
    db.execute("select * from test")
    db.execute("update test set password='12345' where id=0")

3.Ruby socket

  • 服务端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    require 'socket'

    server = TCPServer.open('0.0.0.0', 8080)
    loop do
    Thread.start(server.accept) do |client|
    begin
    while true
    puts "#{client.to_i} online"
    data = client.read()
    throw "empty" if data.empty?
    #puts data.length
    puts data
    end
    rescue Exception => e
    puts "#{client.to_i} offline"
    end
    end
    end
  • 客户端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    require 'socket'

    hostname = '127.0.0.1'
    port = 8080
    buf = 'test'
    s = TCPSocket.open(hostname, port)
    s.write buf
    sleep(1)
    s.close

4.ruby gem 文档服务

rubygems.org上的gem文档访问起来太慢了,其实gem本身就自带doc的功能
安装gem的时候会默认安装相应gem的doc,如果不想占用空间安装doc,则gem install XXX –no-doc 即可。
使用下列命令可以启动gem自带的文档:

1
gem server --port 1234

然后访问http://localhost:1234就可以查看相关的gem文档。

5.ruby改变控制台输出内容的颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
puts "\033[1m前景色\033[0m\n"  
puts " \033[30mBlack (30)\033[0m\n"
puts " \033[31mRed (31)\033[0m\n"
puts " \033[32mGreen (32)\033[0m\n"
puts " \033[33mYellow (33)\033[0m\n"
puts " \033[34mBlue (34)\033[0m\n"
puts " \033[35mMagenta (35)\033[0m\n"
puts " \033[36mCyan (36)\033[0m\n"
puts " \033[37mWhite (37)\033[0m\n"
puts ''
puts "\033[1m背景色\033[0m\n"
puts " \033[40m\033[37mBlack (40), White Text\033[0m\n"
puts " \033[41mRed (41)\033[0m\n"
puts " \033[42mGreen (42)\033[0m\n"
puts " \033[43mYellow (43)\033[0m\n"
puts " \033[44mBlue (44)\033[0m\n"
puts " \033[45mMagenta (45)\033[0m\n"
puts " \033[46mCyan (46)\033[0m\n"
puts " \033[47mWhite (47)\033[0m\n"
puts ''
puts "\033[1m其他\033[0m\n"
puts " Reset (0)"
puts " \033[1mBold (1)\033[0m\n"
puts " \033[4mUnderlined (4)\033[0m\n"

colors

6.一些比较特别的包

Ruby json gem
https://rubygems.global.ssl.fastly.net/gems/json-1.8.3.gem
树莓派wiringpi gpio包
http://pi.gadgetoid.com/article/wiringpi-as-a-ruby-gem

7.安装rvm的正确姿势

参考自:http://rvm.io/rvm/install
首先添加gpg公钥:

1
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3

安装稳定版本的rvm

1
curl -sSL https://get.rvm.io | bash -s stable --ruby

8.解决kali2.0中RVM不能编译ruby-2.3.3

  • 错误详情:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    ruby-2.3.3 - #compiling.......................................................................-
    Error running '__rvm_make -j4',
    showing last 15 lines of /usr/local/rvm/log/1488041042_ruby-2.3.3/make.log
    exts.mk:210: recipe for target 'ext/openssl/all' failed
    make[1]: *** [ext/openssl/all] Error 2
    make[1]: *** Waiting for unfinished jobs....
    installing default nkf libraries
    compiling objspace_dump.c
    linking shared-object json/ext/generator.so
    make[2]: Leaving directory '/usr/local/rvm/src/ruby-2.3.3/ext/json/generator'
    linking shared-object objspace.so
    make[2]: Leaving directory '/usr/local/rvm/src/ruby-2.3.3/ext/objspace'
    linking shared-object nkf.so
    make[2]: Leaving directory '/usr/local/rvm/src/ruby-2.3.3/ext/nkf'
    make[1]: Leaving directory '/usr/local/rvm/src/ruby-2.3.3'
    uncommon.mk:203: recipe for target 'build-ext' failed
    make: *** [build-ext] Error 2
    ++ return 2
    There has been an error while running make. Halting the installation.

查看/usr/local/rvm/log/1488041042_ruby-2.3.3/make.log发现是openssl版本过老导致的。

  • 解决:
    第一步:先安装用于rvm的openssl:
    1
    rvm pkg install openssl

第二步:编译安装ruby,指定openssl目录(我的是/usr/local/rvm/usr/)

1
rvm install ruby-2.3.3 --with-openssl-dir=/usr/local/rvm/usr/

9.设置Gems默认源为ruby-china

现在没有淘宝源了,只有ruby-china源

1
gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/

设置Bundler默认源为ruby-china:

1
bundle config mirror.https://rubygems.org https://gems.ruby-china.org

这样修改以后,即使Gemfile中指定了Source,也会用国内的源。

Windows中搭建Ruby On Rails环境

步骤如下

  1. 安装ruby (我选择的版本是ruby 2.2.3p173)
  2. 安装rails gem
    在这之前建议先把gem的源换成淘宝的源,速度快点。
    1
    2
    3
    4
    5
    6
    7
    8
    gem sources --add https://ruby.taobao.org/ --remove https://rubygems.org/
    gem sources -l
    *** CURRENT SOURCES ***

    https://ruby.taobao.org
    # 请确保只有 ruby.taobao.org

    gem install rails

之后:

1
2
gem install rails
gem install bundler
  1. 安装devkit
    下载地址 http://rubyinstaller.org/downloads

如果是win10,选择 DevKit-mingw64-64-4.7.2-20130224-1432-sfx.exe 这个版本。

然后运行,并选择解压到c:\devkit

进入c:\devkit目录
运行

ruby dk.rb init

之后修改config.yml

添加下面三行

1
2
3
---
- C:/Ruby22-x64
- C:/Ruby22-x64

注意把路径换成当前ruby的安装路径,-前后各有一个空格不可忽略。
然后运行

ruby dk.rb install

最后进入需要建立工程的目录,使用如下命令新建rails项目

rails new testapp

如果在此过程中报错,则进入testapp中
使用

bundler install

来安装所需要的依赖包。

如果还报错,修改testapp目录中的Gemfile

将第一行的sources源内容改为

source 'https://ruby.taobao.org/'

然后再执行bundler install命令

Debian中搭建Ruby On Rails开发环境

假设你已经安装好ruby了

接下来安装rvm

$ curl -L https://get.rvm.io | bash -s stable

某些情况下,可能需要编译一下rvm的初始化脚本

我的位置是在/etc/profile.d/rvm.sh,所以运行这一句:

$source /etc/profile.d/rvm.sh

接着安装bundler

gem install bundler

然后安装rails

gem install rails

如果这地方出现错误,尝试使用rvm切换ruby的版本:

rvm install 2.0.0
rvm 2.0.0 --default

CentOS中搭建ruby on rails开发环境

1
2
3
4
5
6
curl -L https://get.rvm.io | bash -s stable
source /usr/local/rvm/bin/rvm
rvm install 2.0.0
rvm 2.0.0 --default
gem install bundler
gem install rails
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×