用EXCEL编写俄罗斯方块小游戏(基于VBA)
工作属性原因,工作中使用excel办公是常态。前一阵子因工作业务需求,需要用到VBA。研究了一阵子VBA,解决了当时的需求。
后来想想,VBA可以如此彻底的控制excel,那么可不可以编个小游戏呢。
说干就干,先拿与表格最像的俄罗斯方块试试手。

首先,俄罗斯方块游戏需要完成哪些工作。
设置游戏窗口大小及外观,对于有着多年做表经验的我来说,简直是信手拈来。(原来编游戏如此简单,这么快就完成了第一步。休息一天O(∩_∩)O哈哈~)
设计思路:标准的俄罗斯方块共有7个方块,分别是“一”、“J”、“L”、“T”、“S”、“Z”、“田”。
我们注意到每个不同形状的俄罗斯方块均有4个方格,我们选取其中一个作为形状的旋转中心,并通过相对中心的偏移坐标储存不同方块。

shape_0 = Array(Array(0, 0), Array(0, 1), Array(0, -1), Array(0, 2)) '初始化长方块
shape_1 = Array(Array(0, 0), Array(0, 1), Array(0, -1), Array(-1, 1)) '初始化L1方块
shape_2 = Array(Array(0, 0), Array(0, 1), Array(0, -1), Array(-1, -1)) '初始化L2方块
shape_3 = Array(Array(0, 0), Array(0, 1), Array(0, -1), Array(-1, 0)) '初始化T方块
shape_4 = Array(Array(0, 0), Array(0, 1), Array(-1, -1), Array(-1, 0)) '初始化Z方块
shape_5 = Array(Array(0, 0), Array(0, -1), Array(-1, 1), Array(-1, 0)) '初始化S方块
shape_6 = Array(Array(0, 0), Array(-1, 0), Array(-1, -1), Array(0, -1)) '初始化田方块
shape_base = Array(shape_0, shape_1, shape_2, shape_3, shape_4, shape_5, shape_6) '所有方块数据存入数组
通过数组嵌套(非三维数组)完成方块坐标数据的存储。
随机产生0–6的随机数,根据随机数选取方块坐标数据。以焦点坐标为中心利用Offset函数偏移出四个range单元格,使用Union函数连接四个range单元格生成。然后对方块着色并加边框。 并对当前方块的下壁碰撞值赋值(用于碰撞检测,判定方块是否到底或者已落至某一方块上方)。
Sub draw_shape(s_n_can, s_s_x, s_s_y)'画出随机方块过程
Set drop_rng_focus = Cells(s_s_x, s_s_y) '传入焦点方块X,Y
Set b_rng_can = Cells(s_s_x, s_s_y)
Set p_rng_can = b_rng_can
For dr_i_2 = 0 To UBound(shape_base(s_n_can))
off_x_can = shape_base(s_n_can)(dr_i_2)(0)
off_y_can = shape_base(s_n_can)(dr_i_2)(1)
Set p_rng_can = Union(p_rng_can, b_rng_can.Offset(off_x_can, off_y_can))'偏移并连接单元格
Next
p_rng_can.Interior.ColorIndex = shape_color(s_n_can)'对当前方块加底色(底色数据存在一维数组shape_color中)
p_rng_can.Borders.LineStyle = 1'对当前方块加边框
Set drop_rng = p_rng_can '下落方块赋地址
For Each cel In drop_rng
If s_arr_top(cel.Column) < cel.Row Then s_arr_top(cel.Column) = cel.Row '下落图块碰撞下壁刷新赋值
Next
End Sub
根据游戏机制,在出生点Cells(s_s_x, s_s_y)生成并画出方块后,方块需要按照一定速度下降。
通过for循环+系统休眠方式实现方块缓慢地不停下落。方块每下落一层,休眠seep_speed时间。可以通过初始化或赋值seep_speed值来控制方块下落速度。
注:这里作者放弃使用EXCEL中VBA自带的OnTime方法,而是通过【for循环+系统休眠】方式实现方块缓慢地不停下落。
Application对象的OnTime方法能够安排一个过程在将来的特定时间运行,作用是安排某个过程的自动运行。
但是OnTime方法有个致命的缺点就是最小运行时间间隔为1秒钟,对于俄罗斯方块游戏来说每1秒钟下落一层,太过缓慢,且无法调节下落速度,不灵活。
方块下落至底部或落至底部累积方块上之后,使用Union函数将当前下落方块与底部累积块合并,生成新的底部累积方块range。
Sub draw_shape_down(s_n_can, s_s_x, s_s_y)
draw_shape s_n_can, s_s_x, s_s_y '生成下落方块第一帧range,并保存下落方块range至drop_rng及下落方块焦点range至drop_rng_focus。
drop_rng_col = s_n_can '保存下落块索引
sleep (seep_speed) '延时
tt_down = 18 '设置最大下降步数
For ii = 1 To tt_down
If pz_check() = 1 Then '若上下碰撞壁有重叠,即下降方块已经叠放在某个方块之上
Exit For
End If
drop_rng.Interior.ColorIndex = 0 '将上一步方块颜色释放
drop_rng.Borders.LineStyle = 0 '将上一步方块边框释放
Set drop_rng = drop_rng.Offset(1, 0) '将方块range下移一步
Set drop_rng_focus = drop_rng_focus.Offset(1, 0) '将方块焦点range下移一步
drop_rng.Interior.ColorIndex = shape_color(s_n_can) '对当前方块着色
drop_rng.Borders.LineStyle = 1 '对当前方块加边框
For Each cel In drop_rng
If s_arr_top(cel.Column) < cel.Row Then s_arr_top(cel.Column) = cel.Row '下落图块碰撞下壁刷新赋值
Next
sleep (seep_speed) '延时
Next
If foot_shape_rng Is Nothing Then '检查此时是否有底部累积图块,若没有则等于当前下落方块
Set foot_shape_rng = drop_rng
End If
Set foot_shape_rng = Union(foot_shape_rng, drop_rng) '底部累积图块更新range
foot_shape_rng.Borders.LineStyle = 1 '底部累积图块加边框
For Each cel_foot In foot_shape_rng
If s_arr_foot(cel_foot.Column) > cel_foot.Row Then s_arr_foot(cel_foot.Column) = cel_foot.Row '底部累积图块碰上撞壁数组刷新赋值
Next
Set drop_rng = Nothing
Call goal_disshape '检测是否得分
End Sub
设计思路:通过调用windows的API(GetKeyboardState)来监听键盘操作以完成交互。
注:这里作者放弃使用EXCEL中VBA自带的Onkey事件,而是调用windows的API(GetKeyboardState)来监听键盘操作以完成交互。
Onkey方法能够监听到我们按下的是计算机上的那个按键,并能够根据特定的按键执行特定的代码的能力。
但是Onkey方法有个致命的缺点就是Onkey方法在程序执行sleep (seep_speed) 休眠时,无响应。也就是说方块下落时按键无响应。
调用windows的API需要在游戏执行页表(Sheet)下书写代码。
Private Declare Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If kong_ou Mod 2 = 1 Then
Dim keycode(0 To 255) As Byte
GetKeyboardState keycode(0)
If keycode(38) > 127 Then '上
Call turn_shape '调用方块变形过程(函数)
ElseIf keycode(39) > 127 Then '右
Call move_right '调用方块右移过程(函数)
ElseIf keycode(40) > 127 Then '下
Call move_down '调用方块快速下降过程(函数)。通过赋值减小seep_speed值来实现。
ElseIf keycode(37) > 127 Then '左
Call move_left '调用方块左移过程(函数)
End If
End If
kong_ou = kong_ou + 1
[l25].Select '更改页面样式时请将本行代码注释掉,否则无法修改。修改页面完成后请取消注释。
End Sub
同时为了防止按方向键时选中单元格变化影响游戏体验,加入判断机制,固定选中单元格为[l25]。以免出现下图现象,影响体验。

If dw_ou = 0 Then
p_rng_can.Interior.ColorIndex = shape_color(s_n_can)
p_rng_can.Borders.LineStyle = 1
Set drop_rng = p_rng_can '下落方块赋地址
For Each cel In drop_rng
If s_arr_top(cel.Column) < cel.Row Then s_arr_top(cel.Column) = cel.Row '下落图块碰撞下壁刷新赋值
Next
End If
Function pz_check() '通过下落方块底部边界数组与底部累积方块顶部边界数组同位比对,判断下降方块是否已经置于底部方块以上,若位于某个方块上方了,则返回检测结果1
ck_zan = 0
For ck_i = sp_y_bsc - 4 To sp_y_bsc + 5
If s_arr_top(ck_i) + 1 >= s_arr_foot(ck_i) Then ck_zan = 1
If s_arr_foot(ck_i) = sp_x_bsc + 1 Then game_over_yn = 1
Next
pz_check = ck_zan
End Function
Function move_lim(lim_rng As Range) '方块是否能够移动检测,返回值为0则可以移动,返回值为1则不可以移动
move_lim = 0
For Each lim_cell In lim_rng
If lim_cell.Column < sp_y_bsc - 4 Or lim_cell.Column > sp_y_bsc + 5 Then
move_lim = 1
End If
Next
Dim mix_rng As Range
If Not foot_shape_rng Is Nothing Then
Set mix_rng = Intersect(lim_rng, foot_shape_rng) '截取重叠部分,检验是否有重叠
If Not mix_rng Is Nothing Then
move_lim = 1
End If
End If
End Function
变形检测:检测变形后方块range是否超出游戏窗口左右边界,若超出则平移回游戏界面内。
If change_ou = 1 Then '进入判断是否超过左右边界,若超过左右边界,则平移至界内
'变形后出界纠正操作开始
bound_fin = sp_y_bsc
For Each bound_cell In p_rng_can
If bound_cell.Column < sp_y_bsc - 4 Then
If bound_cell.Column < bound_fin Then
bound_fin = bound_cell.Column
End If
End If
If bound_cell.Column > sp_y_bsc + 5 Then
If bound_cell.Column > bound_fin Then
bound_fin = bound_cell.Column
End If
End If
Next
If bound_fin < sp_y_bsc - 4 Then
Set p_rng_can = p_rng_can.Offset(0, sp_y_bsc - 4 - bound_fin)
End If
If bound_fin > sp_y_bsc + 5 Then
Set p_rng_can = p_rng_can.Offset(0, sp_y_bsc + 5 - bound_fin)
End If
'变形后出界纠正操作结束
End If
Sub goal_disshape() '得分检测、消除满行、上部方块下移一行过程
game_s_x = sp_x_bsc - 1 '定位游戏界面左上角焦点X
game_s_y = sp_y_bsc - 4 '定位游戏界面左上角焦点Y
For goal_i = 0 To 19 '游戏区域内循环遍历
dis_all_line = 0
Set dis_range = Range(Cells(game_s_x + goal_i, game_s_y), Cells(game_s_x + goal_i, game_s_y + 9)) '游戏界面内第N行range行内遍历
For Each ran_dis In dis_range '第N行range行内遍历
dis_all_line = dis_all_line + 1 '单行左起累积个
If ran_dis.Interior.ColorIndex < 0 Then '一旦遇到非着色块,立即跳出本行循环
dis_all_line = -1
Exit For
End If
Next
If dis_all_line = 10 Then '判断行内有底色单元格个数,若为10怎说明已经集齐一行积木
'''将游戏界面以集齐一行的单元格行为界,分为上下两部分(不含满单元格行)。分别与底部累积块foot_shape_rng通过Intersect函数截取重叠部分,形成不含满行块的上半部分累积块range为dis_foot_rng_up,下半部分累积块range为dis_foot_rng_down。
Set dis_up_ran = Range(Cells(game_s_x, game_s_y), Cells(game_s_x + goal_i - 1, game_s_y + 9))
Set dis_foot_rng_up = Intersect(dis_up_ran, foot_shape_rng).Offset(1, 0) '截取重叠部分并整体下移1行形成上半部分累积块
If goal_i = 19 Then
Set dis_foot_rng_down = Nothing '满行单元格处于最后一行,则下半部分累积块为空
Else
Set dis_down_ran = Range(Cells(game_s_x + goal_i + 1, game_s_y), Cells(game_s_x + 19, game_s_y + 9))
Set dis_foot_rng_down = Intersect(dis_down_ran, foot_shape_rng) '截取重叠部分,形成下半部分累积块
End If
''''''''''''''''分情况重组底部累积块
If dis_foot_rng_down Is Nothing Then
Set foot_shape_rng = dis_foot_rng_up '若下半部分累积块为空,则新底部累积块等于上半部分累积块。
Else
Set foot_shape_rng = Union(dis_foot_rng_up, dis_foot_rng_down) '正常情况下,新底部累积块等于上半部分累积块合并下半部分累积块。
End If
''''''''''''重新赋值底部累积块碰撞壁上沿
For s_arr_foot_i = sp_y_bsc - 4 To sp_y_bsc + 5
s_arr_foot(s_arr_foot_i) = sp_x_bsc + 19 '先统一设置上沿为界面底沿
Next
For Each cel_foot In foot_shape_rng
If s_arr_foot(cel_foot.Column) > cel_foot.Row Then s_arr_foot(cel_foot.Column) = cel_foot.Row '底部累积图块碰上撞壁数组刷新赋值
Next
''''''''''''消除满行特效并分数加100
Set texiao_rng = Range(Cells(game_s_x + goal_i, game_s_y), Cells(game_s_x + goal_i, game_s_y + 9))
shan_ii = 3
For texiao_i = 3 To 8
sleep (0.1)
If shan_ii = 3 Then
shan_ii = 0
Else
shan_ii = 3
End If
texiao_rng.Interior.ColorIndex = shan_ii
Next
For Each tx_rng In texiao_rng
sleep (0.05) '延时
tx_rng.Interior.ColorIndex = 0
Next
sleep (seep_speed) '延时
score = score + 100 '分数+100
score_win.Value = score '外显分数
''''''''''''消除行以上的单元格下移
dis_up_ran.Copy '复制消除行上部分方块集合
dis_up_ran.Offset(1, 0).PasteSpecial '下移一行粘贴
Range(Cells(game_s_x, game_s_y + 9), Cells(game_s_x + 1, game_s_y)).Clear '擦除最顶行
End If
Next
End Sub
Private game_over_yn As Variant '定义 游戏是否可以执行
Private game_win As Range '定义 游戏显示窗口位置
Private next_shape_win As Range '定义 下一方块显示窗口位置
Private score_win As Range '定义 成绩显示窗口位置
Private shape_base As Variant '定义 方块形状数组
Private shape_color As Variant '定义 方块色彩数组
Private foot_shape_rng As Range '定义 底部累计方块的range
Private drop_whic_focus_next As Variant '定义 下一次掉落方块的随机种类
Private drop_whic_focus As Variant '定义 正在移动方块的随机种类
Private sp_x_bsc As Variant '定义 正在移动方块出生点X
Private sp_y_bsc As Variant '定义 正在移动方块出生点Y
Private seep_speed As Variant '定义速度
Private drop_rng As Range '定义 正在移动方块
Private drop_rng_focus As Range '定义 正在移动方块旋转焦点
Private drop_rng_temp As Range '定义 正在被操作方块
Private drop_rng_col As Variant '定义 正在被操作方块颜色指针
Private s_arr_top(8 To 17) '定义 移动方块各列最低端
Private s_arr_foot(8 To 17) '定义底部累积方块各列最高端
Private score As Variant '定义得分
Public kong_ou As Variant '定义控制奇偶数
Public change_ou As Variant '定义变形校验奇偶数
Sub overall_situ_config() '初始化全局配置
Set next_shape_win = Range("t3:w6") '初始化下一方块显示窗口位置
Set game_win = Range("h3:q22") '初始化游戏显示窗口位置
Set score_win = [t9] '初始化 成绩显示窗口位置
score = 0 '初始化分数为0分
kong_ou = 1 '初始化隔次执行间隔器
change_ou = 0 '初始化改变形状校验值
score_win.Value = score '外显分数0
shape_color = Array(3, 4, 5, 10, 7, 28, 45) '初始化方块底色
sp_x_bsc = 4 '初始掉落焦点坐标X
sp_y_bsc = 12 '初始掉落焦点坐标Y
End Sub
Sub init_config() '初始化单次掉落数据
shape_base = Array(shape_0, shape_1, shape_2, shape_3, shape_4, shape_5, shape_6) '所有方块数据存入数组
'''''''初始化本次掉落方块
If IsEmpty(drop_whic_focus) Then
Randomize '重置随机数种子
drop_whic_focus = Int(0 + (6 - 0 + 1) * Rnd()) '产生随机数0-6
Else
drop_whic_focus = drop_whic_focus_next
End If
'''''''生成下次掉落方块
Randomize '重置随机数种子
drop_whic_focus_next = Int(0 + (6 - 0 + 1) * Rnd()) '产生随机数0-6
s_next_x = sp_x_bsc
s_next_y = sp_y_bsc + 9
draw_next drop_whic_focus_next, s_next_x, s_next_y '调用画下一掉落方块过程
seep_speed = 0.5 '初始化速度
For s_arr_top_i = sp_y_bsc - 4 To sp_y_bsc + 5
s_arr_top(s_arr_top_i) = sp_x_bsc - 2 '初始化当次移动块碰撞壁下沿
Next
If IsEmpty(s_arr_foot(sp_y_bsc)) Then
For s_arr_foot_i = sp_y_bsc - 4 To sp_y_bsc + 5
s_arr_foot(s_arr_foot_i) = sp_x_bsc + 19 '如果尚未有方块掉落并累积底部,则初始化底部累积块碰撞壁上沿
Next
End If
End Sub
② 控制方块变化函数
Public Sub move_left() '方块左移调用过程
If drop_rng Is Nothing Then
MsgBox ("尚无积木掉落")
Else
If move_lim(drop_rng.Offset(0, -1)) = 0 Then
Set drop_rng_temp = drop_rng
drop_rng_temp.Interior.ColorIndex = 0 '释放底色
drop_rng_temp.Borders.LineStyle = 0 '释放边框
Set drop_rng = drop_rng.Offset(0, -1) '将方块range左移一步
Set drop_rng_focus = drop_rng_focus.Offset(0, -1) '将方块焦点range左移一步
drop_rng.Interior.ColorIndex = shape_color(drop_whic_focus) '对当前方块着色
drop_rng.Borders.LineStyle = 1 '对当前方块加边框
End If
End If
End Sub
Public Sub move_right() '方块右移调用过程
If drop_rng Is Nothing Then
MsgBox ("尚无积木掉落")
Else
If move_lim(drop_rng.Offset(0, 1)) = 0 Then
Set drop_rng_temp = drop_rng
drop_rng_temp.Interior.ColorIndex = 0 '释放底色
drop_rng_temp.Borders.LineStyle = 0 '释放边框
Set drop_rng = drop_rng.Offset(0, 1) '将方块range右移一步
Set drop_rng_focus = drop_rng_focus.Offset(0, 1) '将方块焦点range右移一步
drop_rng.Interior.ColorIndex = shape_color(drop_whic_focus) '对当前方块着色
drop_rng.Borders.LineStyle = 1 '对当前方块加边框
End If
End If
End Sub
Public Sub move_down() '方块加速下降
If drop_rng Is Nothing Then
MsgBox ("尚无积木掉落")
Else
seep_speed = seep_speed / 100
End If
End Sub
Public Sub turn_shape() '方块改变形状
If drop_rng Is Nothing Then
MsgBox ("尚无积木掉落")
Else
Set drop_rng_temp = drop_rng
drop_rng_temp.Interior.ColorIndex = 0 '释放底色
drop_rng_temp.Borders.LineStyle = 0 '释放边框
For change_i = 0 To UBound(shape_base(drop_whic_focus))
zan_x_to_y = shape_base(drop_whic_focus)(change_i)(0)
shape_base(drop_whic_focus)(change_i)(0) = 0 - shape_base(drop_whic_focus)(change_i)(1)
shape_base(drop_whic_focus)(change_i)(1) = zan_x_to_y
Next
change_ou = 1
draw_shape drop_whic_focus, drop_rng_focus.Row, drop_rng_focus.Column
End If
End Sub
游戏中方块的碰撞检测是基于底部累积方块上沿及下落运动方块下沿是否相交进行判定。
若方块累积出现空洞,下落方块从侧面进入底部累积块内部,则方块碰撞检测将失效。出现意想不到的bug。就如下图一样。

下一步,将采用所有方块均参与碰撞检测方式改进游戏代码。(如果有时间的话┗( ▔, ▔ )┛)
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc
我使用rails3.1+rspec和factorygirl。我对必填字段(validates_presence_of)的验证工作正常。我如何让测试将该事实用作“成功”而不是“失败”规范是:describe"Addanindustrywithnoname"docontext"Unabletocreatearecordwhenthenameisblank"dosubjectdoind=Factory.create(:industry_name_blank)endit{shouldbe_invalid}endend但是我失败了:Failures:1)Addanindustrywithnona
修改(澄清问题)我已经花了几天时间试图弄清楚如何从Facebook游戏中抓取特定信息;但是,我遇到了一堵又一堵砖墙。据我所知,主要问题如下。我可以使用Chrome的检查元素工具手动查找我需要的html-它似乎位于iframe中。但是,当我尝试抓取该iframe时,它是空的(属性除外):如果我使用浏览器的“查看页面源代码”工具,这与我看到的输出相同。我不明白为什么我看不到iframe中的数据。答案不是它是由AJAX之后添加的。(我知道这既是因为“查看页面源代码”可以读取Ajax添加的数据,也是因为我有b/c我一直等到我可以看到数据页面之后才抓取它,但它仍然不存在)。发生这种情况是因为
我正在尝试用Ruby(Rails)编写一个正则表达式,以便用户名的字符仅包含数字和字母(也没有空格)。我有这个正则表达式,/^[a-zA-Z0-9]+$/,但它似乎没有用,我在Rails中收到一个错误,说“The如果正则表达式使用多行anchor(^或$),这可能会带来安全风险。您是要使用\A和\z,还是忘记添加:multiline=>true选项?"我的user.rb模型中此实现的完整代码是:classUser我做错了什么以及如何修复此正则表达式,使其仅对数字和字母有效而不对空格有效?谢谢。 最佳答案 简短回答:使用/\A[a-z
我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源
为了减少我的小Rails应用程序中的代码重复,我一直致力于将我的模型之间的通用代码放入它自己的单独模块中,到目前为止一切顺利。模型的东西相当简单,我只需要在开头包含模块,例如:classIso这工作正常,但是现在,我将有一些Controller和View代码,这些代码也将在这些模型之间通用,到目前为止,我有这个用于我的可发送内容:#Thisisamodulethatisusedforpages/formsthatarecanbe"sent"#eitherviafax,email,orprinted.moduleSendablemoduleModeldefself.included(kl