环境: 基于jstree 3.0.0,rails: 4.1.4
// 32px.png 40px.png 和 throbber.gif 放到images目录
// src/themes/style.css 重命名为jstree-style.css.scss放到stylesheets目录
// src目录下的js文件: jstree.js放入javascripts文件夹
// 其余的都是jstree的插件,有需要就放,没有就可以不放进来:jstree.checkbox.js jstree.contextmenu.js jstree.dnd.js jstree.search.js jstree.sort.js jstree.state.js jstree.types.js jstree.unique.js jstree.wholerow.js
// 在application.js中设置js文件的加载顺序,不然会出现js错误。jstree需要在插件之前,实例如下:
// //=require jquery
// //=require jquery_ujs
// //=require jquery-ui
// //=require jstree.vakata
// //=require jstree
// //=require jstree.contextmenu
// .
// .
// .
// //= require_tree
ps: 对于jstree.vakata.js文件,我这边的实践是,只有引入了这个文件,jstree的有些功能才能正常进行,但是作者也在这里有过说明,说是不需要引入: https://github.com/vakata/jstree/issues/123
<!--views/home/simple_tree.erb-->
<div id="mytree-html">
<ul>
<li data-jstree='{"type": "fold"}'>文件夹1</li>
<li data-jstree='{"type": "fold"}'>文件夹2
<ul>
<li data-jstree='{"icon": "/assets/file.png"}'>文件1</li>
</ul>
</li>
</ul>
</div>
<script>
$('#mytree-html').bind('loaded.jstree', function(e, data) {
// 当jstree被load完成后,打开所有的节点
data.instance.open_all();
});
$('#mytree-html').jstree({
"types": {
"fold": {
"icon": ".<%= asset_path('fold.png') %>"
},
"file": {
"icon": ".<%= asset_path('file.png') %>"
}
},
'plugins': ['html_data', 'types', 'themes']
});
</script>
$('#mytree').jstree({
'core': {
// check_callbackc参数用于Contextmenu 和Drag & drop 插件,设置true后才能起作用
'check_callback': true,
'data': {
// 后台JSON数据的地址,当jstree载入时,会自动从这个地址获取json数据,然后生成树状结构
'url': '/home/tree_data',
'data': function(node) {
return {node_id: node.id}
}
}
},
// types插件设置,但是在我的实践中,这个怎么也没法起效,有待进一步研究
'types': {
'#': {
'valid_children': ['fold', 'file']
},
'fold': {
'icon': "<%= asset_path('fold.png') %>",
'valid_children': ['fold', 'file']
},
'file': {
'icon': "<%= asset_path('file.png') %>",
'valid_children': []
}
},
'plugins': ['types', 'dnd', 'contextmenu', 'wholerow'],
// 右键自定义菜单设置,customMenu是一个函数,下面马上就会讲到
'contextmenu': {
'items': customMenu
}
});
// 自定义右键菜单
function customMenu(node) {
var items = {
'createSubFold': {
'label': '新建子目录',
'action': function(obj) {
var inst = $.jstree.reference(obj.reference);
var node = inst.get_node(obj.reference);
inst.create_node(node, {}, 'last', function(new_node) {
setTimeout(function() {inst.edit(new_node);}, 0);
});
}
},
'createfile': {
'label': '新建文件',
'action': function(obj) {
var inst = $.jstree.reference(obj.reference);
var node = inst.get_node(obj.reference);
$.ajax({
url: '/home/new_file',
data: {fold_id: node.id},
success: function(data, textStatus, jqXHR) {
console.log("new file success!");
}
});
}
},
'rename': {
'label': '重命名',
'action': function(obj) {
var inst = $.jstree.reference(obj.reference);
var node = inst.get_node(obj.reference);
inst.edit(node);
}
},
'edit': {
'label': '编辑',
'submenu': {
'cut': {
'label': '剪切',
'action': function(obj) {}
},
'copy': {
'label': '复制',
'action': function(obj) {}
},
'paste': {
'label': '粘帖',
'action': function(obj) {}
},
}
},
'destroy': {
'label': '删除',
'action': function(obj) {
var inst = $.jstree.reference(obj.reference);
var node = inst.get_node(obj.reference);
$.ajax({
url: '/home/destroy_node',
type: 'DELETE',
data: {id: node.id}
});
}
}
};
// 当树的节点是文件时,右键菜单没有新建子文件夹
// 通过json格式传过来的li_attr或a_attr我们可以轻松的实现不同的节点,拥有不同的右键菜单
if (node.li_attr.class == 'file') {
delete items.createSubFold;
}
return items;
}
$('#mytree').bind('move_node.jstree', function(e, data) {
// 当页面树的节点位置发生变化后,同时需要更新后端存储的树的结构
$.ajax({
url: '/home/move_node',
type: 'POST',
data: {id: data.node.id, old_parent: data.old_parent, old_position: data.old_position, parent: data.parent, position: data.position},
success: function(data, textStatus, jqXHR) {
console.log('move node successfully!');
}
});
});
// 实现的功能,通过左键点击节点,可以显示节点的详细信息
$('#mytree').bind('select_node.jstree', function(e, data) {
// 只有左键选中node才触发ajax操作
if (data.event.which == 1) {
if (data.node.li_attr.class == 'file') {
$.ajax({
url: '/home/show_file',
data: {id: data.node.id}
});
}
else {
$.ajax({
url: '/home/show_fold',
data: {id: data.node.id}
});
}
}
});
// 前端重命名后,需要同时处理后端的重命名,同时,创建一个子目录时,也是调用这个callback
$('#mytree').bind('rename_node.jstree', function(e, data) {
$.ajax({
url: '/home/node_rename',
type: 'POST',
data: {id: data.node.id, new_name: data.node.text},
success: function(data, textStatus, jqXHR) {
console.log("rename successfully!");
}
});
});
$('#mytree').delegate('a', 'dbclick', function(e) {
$('#mytree').jstree('toggle_node', this);
e.preventDefault();
return false;
};
$('#jstree-demo').jstree(true).refresh_node('#' + node_id);
$('#jstree-demo').jstree(true).refresh();
使用效果参见:https://github.com/beyondalbert/jstree-demo
参考:
http://www.jstree.com/
// 选中所有元素
$("*");
// 根据类名选择元素
$('.class-demo');
// 根据元素名字来选择元素
$('div');
// 根据元素id来选择元素
$('#id-demo');
// 组合选择多种类型的元素
$('#id-demo, div, .class-demo');
// 根据元素的属性来选择元素,更多属性选择器用法见:
// http://api.jquery.com/category/selectors/attribute-selectors/
$("a[href='/test_demo']");
$(window).resize(function() {
console.log("浏览器宽度为:" + $(window).width());
});
$('#target-element-id').hover(function() {
// 悬浮到目标元素上时,触发这个函数中的事件
console.log("mouse enter target element");
}, function() {
// 鼠标离开目标元素时,触发这个函数中的事件
console.log("mouse leave target element");
});
$("#target-element-id").click(function() {
console.log("target element is clicked!");
});
$("#target-element-id").dblclick(function() {
console.log("target element is double clicked!");
});
$('#target-select-id').change(function() {
var selectedItemValue = $(this).children('option:selected').val();
console.log("change the value to: " + selectedItemValue);
});
$('#target-element-id').addClass("foo");
$('#target-element-id').removeClass("foo");
// 一般用于ajax中,设置需要提交的data
// 同时,一般会阻止默认的表单form提交: event.preventDefault();
var submitData = $("#target-form-id").serialize();
ps: 表单form中的表单都需要有name属性,不然这个函数不会把表单的值编码到最终的字符串
html内容:
<div id="target-id">
<div>test1</div>
test2
<input id="input-id" value="test!">
</div>
js实例:
// 输出 test1 test2
$('#target-id').text();
// 得到
// <div>test1</div>
// test2
$('#target-id').html();
// 设置目标元素的text
$('#target-id').text('test3');
// 设置目标元素的html内容
$('#target-id').html(htmlContent);
// 获取input框的值
$('#input-id').val();
// 设置input框的值
$('#input-id').val();
<div id="demo-id" data-msg="test msg" data-demo="test demo">
// 输出test msg
$('#demo-id').data('msg');
// 设置data-msg的值
$('#demo-id').data('msg', 'test change msg');
var cloneTargetElement = $('#target-element-id').clone();
// 通常用于复制后,在append到其他的元素
cloneTargetElement.appendTo("#another-element");
// 如需复制子元素上的事件,需要添加true选项
var cloneTargetElement = $('#target-element-id').clone(true);
实例:
$('#target-id').after(cloneTargetElement);
$('#target-id').append(cloneTargetElement);
cloneTargetElement.appendTo("#target-id");
实例:
<input id="check-demo" type="checkbox" checked="checked">
// 返回true
$('#check-demo').prop('checked');
// 返回“checked”
$('#check-demo').attr('checked');
// 设置checkbox为未选中状态
$('#check-demo').prop('checked', false);
<ul>
<li>foo</li>
<li>bar</li>
</ul>
$('li').each(function(index) {
console.log(index + ":" + $(this).text());
});
ps: 一般如果是jquery获取的一个dom数组,建议用这个方式来遍历,如果是纯js的数组,建议用forEach方式来遍历
var arrayDemo = [1, 2, 3];
arrayDemo.forEach(function(element, index, array) {
console.log("a[" + index + "] = " + element);
});
$('li').filter(function(index) {
return index % 3 === 2
});
实例:
$('#target-id').find(".demo-class");
$('#target-id').children(".demo-class");
<div class="container">
<div class="hello">Hello</div>
<div class="goodbye">Goodbye</div>
</div>
// 删除选中的元素
$('.hello').remove();
// 删除选中元素中,符合选择器的子元素
$('.container').remove('.goodbye');
$('#target-id').show();
$('#target-id').hide();
// 比较复杂的用法:
// 控制显示的快慢:用“slow”(600)和“fast”(200),或者是数值,以毫秒为单位
$('#target-id').show("slow", function() {
// 显示结束后,需要处理的逻辑写在这里
});
$('#target-id').prev();
// 目标节点之后的兄弟节点选择,以下选择id为target的元素之后的div兄弟节点
$('#target ~ div');
$('#target-id').before("<p>test</p>");
$("<p>test</p>").insertBefore('#target-id');
$(document).on('click', '#target-element', function() {
console.log('test');
});
环境版本: webmock: 1.20.4 rspec-rails: 3.0.2
group :test do
gem 'webmock'
end
require 'webmock/rspec'
ps: 默认情况下,只要添加了webmock,当前应用所有的外部依赖都被阻断了,除非你在spec_helper.rb中打开
# 打开所有的外部链接
WebMock.allow_net_connect!
# 或者只允许部分外部链接,并且允许正则匹配
WebMock.disable_net_connect!(:allow => [/example.org/, /google/])
直接在 it 语句中使用:
it "should return project hash" do
stub_request(:get, "http://www.example.com/api/projects/1.json").to_return(:body => {id: 1, name: "test project"}.to_json)实力
res = JSON.parse(Net::HTTP.get(URI('http://www.example.com/api/projects/1.json')))
expect(res).to eq({id: 1, name: "test project"})
end
更多的mock实例可以参加github:
https://github.com/bblimke/webmock
应用场景:你的应用非常多的依赖于一个外部的api服务,通过上面的简单mock,会重复写多个mock,并且测试代码也会变得分散无法很好的维护。
group :development, :test do
gem 'sinatra'
end
在spec_helper.rb中添加如下的代码:
config.before(:each) do
stub_request(:any, /example.com/).to_rack(FakeServer)
end
# spec/support/fake_server.rb
require 'sinatra/base'
class FakeApiServer < Sinatra::Base
get '/api/projects/1.json' do
content_type :json
status 200
{id: 1, name: "test project"}.to_json
end
end
it "should return project hash" do
# 下面的url会自动的发送的我们的fake server上,然后返回fake server上设置的返回值
res = JSON.parse(Net::HTTP.get(URI('http://www.example.com/api/projects/1.json')))
expect(res).to eq({id: 1, name: "test project"})
end
参考:
http://robots.thoughtbot.com/how-to-stub-external-services-in-tests
https://github.com/bblimke/webmock
基于版本: rspec_rails: 3.0.4 factory_girl_rails: 4.5.0
group :development, :test do
gem 'rspec-rails'
gem 'factory_girl_rails'
end
# config/database.yml
test:
adapter: mysql2
encoding: utf8
reconnect: false
pool: 5
socket: /var/run/mysqld/mysqld.sock
database: demo_test
username: demo
password: "****"
在rails demo的根目录下运行如下的命令:
rails g rspec:install
该命令会生成如下的结果:
create .rspec
create spec
create spec/spec_helper.rb
在.rspec文件中加如下代码:
--format documentation
在config/application.rb中添加一下配置
config.generators do |g|
g.test_framework :rspec,
:fixtures => true,
:view_specs => false,
:helper_specs => true,
:routing_specs => false,
:controller_specs => true,
:request_specs => true
g.fixture_replacement :factory_girl, :dir => "spec/factories"
end
rails g rspec:model project
# 将生成一下文件:
# spec/models/project_spec.rb
# spec/factories/projects.rb
#还能用于一下内容的自动生成:
rails g rspec:controller project
rails g rspec:helper project
# 等等,具体参见:https://www.relishapp.com/rspec/rspec-rails/docs/generators
require "rails_helper"
Rspec.describe Project, :type => :model do
# model的实例方法测试
describe "#issue_count" do
it "返回项目中的任务个数" do
project = FactoryGirl.create(:project, :with_issues)
expect(project.issue_count).to eq(3)
end
end
# model的类方法测试
describe ".total_count" do
it "返回所有项目的数量" do
expect(Project.total_count).to eq(1)
end
end
end
git clone git链接 #克隆remote代码
git clone -b branch名字 git链接 #指定remote的分支克隆
git branch #查看本地分支
git branch -d [name] #删除分支, -d选项只能删除已经参与了合并的分支,对于未有合并的分支是无法删除的。如果想强制删除一个分支,可以使用-D选项
git push origin --delete branch_name #删除remote分支
git branch -r #查看remote分支
git branch b-name #创建分支
git checkout b-name #切换到另外的分支
git checkout -b b-name #切换到remote的分支
git push origin b-name #本地branch push到remote
git config -l #查看本地git配置信息
git tag -a v1.0.0 -m "后台第一个版本!" #创建tag
git tag #列出本地的tag
git push origin --tags #本地所有tag一起push到remote
git push origin v1.0.0 #push本地特定的tag到remote
git log --pretty=format:'%h : %s' --topo-order --graph #log显示
#合并多个commit
git rebase -i HEAD~4 #对最近的4次进行合并
#rebase操作参考: http://blog.chinaunix.net/uid-27714502-id-3436706.html
#stash用法:
git stash #备份当前的工作区的内容,从最近的一次提交中读取相关内容,让工作区保证和上次提交的内容一致。同时,将当前的工作区内容保存到Git栈中。
git stash pop #从Git栈中读取最近一次保存的内容,恢复工作区的相关内容。由于可能存在多个Stash的内容,所以用栈来管理,pop会从最近的一个stash中读取内容并恢复。
git stash list #显示Git栈内的所有备份,可以利用这个列表来决定从那个地方恢复。
git stash clear #清空Git栈。此时使用gitg等图形化工具会发现,原来stash的哪些节点都消失了。
git stash apply stash@{1} #就可以将你指定版本号为stash@{1}的工作取出来
git reset --hard commit_id #回到某个版本
git reset --hard HEAD~3 #会将最新的3次提交全部重置,就像没有提交过一样
git remote set-url origin url #切换git url
git config --global user.email "your_email@example.com" #设置全局的用户邮件地址
git config user.email "your_email@example.com" #设置当前git库的用户邮件地址
#分支1 merge 到 分支2
git checkout branch2
git fetch origin
git merge origin/branch1 #merge完后需要可能需要手动处理conflict
git push origin branch2
sudo apt-get install git
ssh-keygen -C "github帐号" -f ~/.ssh/github
ssh -T git@github.com
Hi XXX! You’ve successfully authenticated, but GitHub does not provide shell access. #显示这个表示设置成功
# 先进入需要push的目录
git init
git commit -m "first commit"
git remote add origin git@github.com:xxx/xxx.git
git push -u origin master
基于版本: fancory_girl_rails: 4.5.0
一般一个model对象对应一个factories目录下的文件,比如有一个项目model,则建立以下factories文件:
Rails.root/spec/factories/projects.rb
# Rails.root/spec/factories/projects.rb
FactoryGirl.define do
factory :project do
name "test project"
description "test descriprion"
end
end
@project = FactoryGirl.create(:project)
# 或者:
@project = FactoryGirl.build(:project)
FactoryGirl.define do
sequence :name do |n|
"test#{n} project"
end
factory :project do
name
description "test descriprion"
end
end
@project = FactoryGirl.create_list(:project, 5)
# => test1_project
# => test2_project
# => .
# => .
# => .
FactoryGirl.define do
factory :project do
name "test project"
description "test descriprion"
end
trait :with_archived do
is_archived true
end
end
# 调用
project = FactoryGirl.create(:project, :with_archived)
project.is_archived # => true
FactoryGirl.define do
factory :project do
name "test project"
description "test description"
factory :archived_project do
is_archived true
end
end
end
# 或者:
FactoryGirl.define do
factory :project do
name "test project"
description "test description"
end
factory :archived_project, parent: :project do
is_archived true
end
end
# 调用:
archived_project = FactoryGirl.create(:archived_project)
archived_project.is_archived # => true
# Rails.root/spec/factories/versions.rb
FactoryGirl.define do
factory :version do
project
end
end
# 在创建version时,会自动创建version关联的project,前提是已经定义好了project的FactoryGirl
version = FactoryGirl.create(:version)
FactoryGirl.define do
factory :project do
name "test project"
end
trait :with_version do
after :create do |project|
FactoryGirl.create :version, project: project
end
end
end
# 调用
project = FactoryGirl.create(:project, :with_version)
# Rails.root/spec/factories/projects.rb
FactoryGirl.define do
factory :project do
transient do
name_flag false
end
name { name_flag ? "test project with flag" : "test project" }
end
trait :with_versions do
transient do
number_of_versions 3
end
after :create do |project, evaluator|
FactoryGirl.create_list :version, evaluator.number_of_versions, project: project
end
end
end
# 调用如下:
project = FactoryGirl.create(:project, name_flag: true)
project.name # => "test project with flag"
FactoryGirl.create(:project, :with_versions, number_of_versions: 5, name_flag: true)
http://www.slideshare.net/gabevanslv/factory-girl-15924188
http://arjanvandergaag.nl/blog/factory_girl_tips.html
http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md