如图所示,我试图增加树布局两侧最后节点之间的间隙,因为它们重叠
有没有办法在 D3 中做到这一点?
{
"name": "",
"type": "network",
"children": [{
"name": "",
"type": "lb",
"children": [{
"name": "",
"type": "mm",
"id": "app",
"connServer": "s",
"size": 3938
}]
},
{
"name": "",
"type": "vm",
"children": [{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "appServer",
"id": "app1",
"connServer": "db1",
"size": 3938
}]
},
{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "webServer",
"id": "web1",
"connServer": "app1",
"size": 3534
}]
},
{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "dataServer",
"id": "db1",
"connServer": "app1",
"size": 7074
}]
}]
},
{
"name": "",
"type": "vm",
"children": [{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "webServer",
"id": "web2",
"connServer": "app1",
"size": 721
}]
},
{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "dataServer",
"id": "db2",
"connServer": "db1",
"size": 721
}]
}]
},
{
"name": "",
"type": "vm",
"children": [{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "appServer",
"id": "app2",
"connServer": "db3",
"size": 721
}]
},
{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "webServer",
"id": "web3",
"connServer": "app2",
"size": 721
}]
},
{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "dataServer",
"id": "db3",
"connServer": "app2",
"size": 721
}]
}]
},
{
"name": "",
"type": "vm",
"children": [{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "appServer",
"id": "app3",
"connServer": "db4",
"size": 721
}]
},
{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "webServer",
"id": "web4",
"connServer": "app3",
"size": 721
}]
},
{
"name": "",
"type": "container",
"children": [{
"name": "",
"type": "dataServer",
"id": "db4",
"connServer": "app3",
"size": 721
}]
}]
},{
"name": "",
"type": "sto",
"children": [{
"name": "",
"type": "mm",
"id": "app",
"connServer": "s",
"size": 3938
}]
}]
}
这是我用于构建树布局的代码,当我使用 separate() 时,树布局的子节点在空间中没有对齐,它们坚持自己的位置,没有单独的方法,折叠的子节点将被包裹在一起/在空间中对齐 /** * */ var tooltipMap = d3.map();
// Get JSON data
treeJSON = d3.json("network.json", function(error, treeData) {
// Calculate total nodes, max label length
var totalNodes = 0;
var maxLabelLength = 15;
// variables for drag/drop
var selectedNode = null;
var draggingNode = null;
// panning variables
var panSpeed = 200;
var panBoundary = 20; // Within 20px from edges will pan when
// dragging.
// Misc. variables
var i = 0;
var duration = 750;
var root;
// size of the diagram
var docWidth = $(document).width();
var viewerWidth = docWidth / (1.361);
var docHeight = $(document).height();
var halfHeight = docHeight / 2;
var quarterHeight = docHeight / 4;
viewerHeight = halfHeight + quarterHeight;
var tree = d3.layout.tree().size([ viewerHeight, viewerWidth ]);
/*
* var tree = d3.layout.tree().separation(function(a, b) { return ((a.parent ==
* root) && (b.parent == root)) ? 3 : 1; }).size([ viewerHeight, viewerWidth -
* 160 ]);
*/
// define a d3 diagonal projection for use by the node paths
// later on.
var diagonal = d3.svg.diagonal().projection(function(d) {
return [ d.x, -d.y ];
});
// A recursive helper function for performing some setup by
// walking through all nodes
function visit(parent, visitFn, childrenFn) {
if (!parent)
return;
visitFn(parent);
var children = childrenFn(parent);
if (children) {
var count = children.length;
for ( var i = 0; i < count; i++) {
visit(children[i], visitFn, childrenFn);
}
}
}
// Call visit function to establish maxLabelLength
visit(treeData, function(d) {
totalNodes++;
maxLabelLength = Math.max(d.name.length, maxLabelLength);
}, function(d) {
return d.children && d.children.length > 0 ? d.children : d.children;
});
function getConServers(element, event, status) {
var conServerNode;
var targetElement = event.target;
var targetId = targetElement.id;
if (targetId != null) {
var server = tooltipMap.get(targetElement.id);
if (server != null) {
var connectedServer = server.get("connId");
var outerTarget = d3.select("#" + "outer" + targetId);
var outerCon = d3.select("#" + "outer" + connectedServer);
if (status == "enter") {
outerTarget.style("stroke", "#48C127");
outerTarget.style("stroke-width", 3);
outerCon.style("stroke", "#F07A0B");
outerCon.style("stroke-width", 3);
} else if (status == "exit") {
outerTarget.style("stroke", "#fff");
outerCon.style("stroke", "#fff");
outerTarget.style("stroke-width", 1);
outerCon.style("stroke-width", 1);
}
}
}
}
// sort the tree according to the node names
function sortTree() {
tree.sort(function(a, b) {
return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
});
}
// Sort the tree initially incase the JSON isn't in a sorted
// order.
sortTree();
// TODO: Pan function, can be better implemented.
function pan(domNode, direction) {
var speed = panSpeed;
if (panTimer) {
clearTimeout(panTimer);
translateCoords = d3.transform(svgGroup.attr("transform"));
if (direction == 'left' || direction == 'right') {
translateX = direction == 'left' ? translateCoords.translate[0] + speed : translateCoords.translate[0]
- speed;
translateY = translateCoords.translate[1];
} else if (direction == 'up' || direction == 'down') {
translateX = translateCoords.translate[0];
translateY = direction == 'up' ? translateCoords.translate[1] + speed : translateCoords.translate[1]
- speed;
}
scaleX = translateCoords.scale[0];
scaleY = translateCoords.scale[1];
scale = zoomListener.scale();
svgGroup.transition().attr("transform",
"translate(" + translateX + "," + translateY + ")scale(" + scale + ")");
d3.select(domNode).select('g.node').attr("transform", "translate(" + translateX + "," + translateY + ")");
zoomListener.scale(zoomListener.scale());
zoomListener.translate([ translateX, translateY ]);
panTimer = setTimeout(function() {
pan(domNode, speed, direction);
}, 50);
}
}
// Define the zoom function for the zoomable tree
function zoom() {
svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
// define the zoomListener which calls the zoom function on the
// "zoom" event constrained within the scaleExtents
var zoomListener = d3.behavior.zoom().scaleExtent([ 0.1, 3 ]).on("zoom", zoom);
function initiateDrag(d, domNode) {
draggingNode = d;
d3.select(domNode).select('.ghostCircle').attr('pointer-events', 'none');
d3.selectAll('.ghostCircle').attr('class', 'ghostCircle show');
d3.select(domNode).attr('class', 'node activeDrag');
svgGroup.selectAll("g.node").sort(function(a, b) { // select
// the
// parent
// and
// sort
// the
// path's
if (a.id != draggingNode.id)
return 1; // a is not the hovered element,
// send "a" to the back
else
return -1; // a is the hovered element,
// bring "a" to the front
});
// if nodes has children, remove the links and nodes
if (nodes.length > 1) {
// remove link paths
links = tree.links(nodes);
nodePaths = svgGroup.selectAll("path.link").data(links, function(d) {
return d.target.id;
}).remove();
// remove child nodes
nodesExit = svgGroup.selectAll("g.node").data(nodes, function(d) {
return d.id;
}).filter(function(d, i) {
if (d.id == draggingNode.id) {
return false;
}
return true;
}).remove();
}
// remove parent link
parentLink = tree.links(tree.nodes(draggingNode.parent));
svgGroup.selectAll('path.link').filter(function(d, i) {
if (d.target.id == draggingNode.id) {
return true;
}
return false;
}).remove();
dragStarted = null;
}
// define the baseSvg, attaching a class for styling and the
// zoomListener
var baseSvg = d3.select("#tree-container").append("svg").attr("width", viewerWidth).attr("height", viewerHeight)
.attr("class", "overlay").attr("id", "treesvg").call(zoomListener);
// Define the drag listeners for drag/drop behaviour of nodes.
dragListener = d3.behavior.drag().on("dragstart", function(d) {
if (d == root) {
return;
}
dragStarted = true;
nodes = tree.nodes(d);
d3.event.sourceEvent.stopPropagation();
// it's important that we suppress the mouseover
// event on the node being dragged. Otherwise it
// will absorb the mouseover event and the
// underlying node will not detect it
// d3.select(this).attr('pointer-events',
// 'none');
}).on("drag", function(d) {
if (d == root) {
return;
}
if (dragStarted) {
domNode = this;
initiateDrag(d, domNode);
}
// get coords of mouseEvent relative to svg
// container to allow for panning
relCoords = d3.mouse($('svg').get(0));
if (relCoords[0] < panBoundary) {
panTimer = true;
pan(this, 'left');
} else if (relCoords[0] > ($('svg').width() - panBoundary)) {
panTimer = true;
pan(this, 'right');
} else if (relCoords[1] < panBoundary) {
panTimer = true;
pan(this, 'up');
} else if (relCoords[1] > ($('svg').height() - panBoundary)) {
panTimer = true;
pan(this, 'down');
} else {
try {
clearTimeout(panTimer);
} catch (e) {
}
}
d.x0 += d3.event.dy;
d.y0 += d3.event.dx;
var node = d3.select(this);
node.attr("transform", "translate(" + d.y0 + "," + d.x0 + ")");
updateTempConnector();
}).on("dragend", function(d) {
if (d == root) {
return;
}
domNode = this;
if (selectedNode) {
// now remove the element from the
// parent, and insert it into the new
// elements children
var index = draggingNode.parent.children.indexOf(draggingNode);
if (index > -1) {
draggingNode.parent.children.splice(index, 1);
}
if (typeof selectedNode.children !== 'undefined' || typeof selectedNode._children !== 'undefined') {
if (typeof selectedNode.children !== 'undefined') {
selectedNode.children.push(draggingNode);
} else {
selectedNode._children.push(draggingNode);
}
} else {
selectedNode.children = [];
selectedNode.children.push(draggingNode);
}
// Make sure that the node being added
// to is expanded so user can see added
// node is correctly moved
expand(selectedNode);
sortTree();
endDrag();
} else {
endDrag();
}
});
function endDrag() {
selectedNode = null;
d3.selectAll('.ghostCircle').attr('class', 'ghostCircle');
d3.select(domNode).attr('class', 'node');
// now restore the mouseover event or we won't be able to
// drag a 2nd time
d3.select(domNode).select('.ghostCircle').attr('pointer-events', '');
updateTempConnector();
if (draggingNode !== null) {
update(root);
centerNode(draggingNode);
draggingNode = null;
}
}
// Helper functions for collapsing and expanding nodes.
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
function expand(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expand);
d._children = null;
}
}
var overCircle = function(d) {
selectedNode = d;
updateTempConnector();
};
var outCircle = function(d) {
selectedNode = null;
updateTempConnector();
};
// Function to update the temporary connector indicating
// dragging affiliation
var updateTempConnector = function() {
var data = [];
if (draggingNode !== null && selectedNode !== null) {
// have to flip the source coordinates since we did this
// for the existing connectors on the original tree
data = [ {
source : {
x : selectedNode.y0,
y : selectedNode.x0
},
target : {
x : draggingNode.y0,
y : draggingNode.x0
}
} ];
}
var link = svgGroup.selectAll(".templink").data(data);
link.enter().append("path").attr("class", "templink").attr("d", d3.svg.diagonal()).attr('pointer-events',
'none');
link.attr("d", d3.svg.diagonal());
link.exit().remove();
};
// Function to center node when clicked/dropped so node doesn't
// get lost when collapsing/moving with large amount of
// children.
function centerNode(source) {
scale = zoomListener.scale();
x = -source.y0;
y = -source.x0;
x = x * scale + viewerWidth / 2;
y = y * scale + viewerHeight / 2;
var mySvg = d3.select("#tree-container");
mySvg.select('g').transition().duration(duration).attr("transform",
"translate(" + (x - 230) + "," + (y + 411) + ")scale(" + scale + ")");
zoomListener.scale(scale);
zoomListener.translate([ x, y ]);
var shootX, shootY;
// shootX=x+270;
// shootY=y+350;
shootX = "850.242468772961", shootY = "450.75";
getShootImage(shootX, shootY);
}
/**
* This function is used to add shooting image and text into the frame.
*
* @param shootImgX
* @param shootTextX
* @return
*/
function getShootImage(shootImgX, shootImgY) {
d3.select("#shootCircle").remove();
var imgUrl = "image/target-icon.png";
baseSvg.append("image").attr("id", "shootCircle").attr("xlink:href", imgUrl).attr("x", shootImgX).attr("y",
shootImgY).attr("width", 50).attr("height", 50).style("fill", "red").classed("shoot", true);
baseSvg.append("text").text("Shooting Gun").attr("x", shootImgX).attr("y", shootImgY + 50).attr("width", 50)
.attr("height", 50).style("font-weight", "bold");
var loadButton = document.createElement("input");
loadButton.setAttribute("type", "button");
loadButton.setAttribute("value", "Add Load");
loadButton.setAttribute("class", "btn btn-cust-info");
loadButton.setAttribute("id", "addload");
loadButton.setAttribute("x", shootImgX);
loadButton.setAttribute("y", shootImgY);
loadButton.setAttribute("width", 500);
loadButton.setAttribute("height", 500);
loadButton.style.align = "left";
}
// Toggle children function
function toggleChildren(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else if (d._children) {
d.children = d._children;
d._children = null;
}
return d;
}
// Toggle children on click.
function click(d) {
if (d3.event.defaultPrevented)
return; // click suppressed
d = toggleChildren(d);
update(d);
centerNode(d);
}
function update(source) {
// Compute the new height, function counts total children of
// root node and sets tree height accordingly.
// This prevents the layout looking squashed when new nodes
// are made visible or looking sparse when nodes are removed
// This makes the layout more consistent.
var levelWidth = [ 1 ];
var childCount = function(level, n) {
if (n.children && n.children.length > 0) {
if (levelWidth.length <= level + 1)
levelWidth.push(10);
levelWidth[level + 1] += n.children.length;
n.children.forEach(function(d) {
childCount(level + 1, d);
});
}
};
childCount(0, root);
var newHeight = d3.max(levelWidth) * 25; // 25 pixels per
// line
tree = tree.size([ newHeight, viewerWidth ]);
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(), links = tree.links(nodes);
// Set widths between levels based on maxLabelLength.
nodes.forEach(function(d) {
d.y = (d.depth * (maxLabelLength * 7)); // maxLabelLength
// * 10px
// alternatively to keep a fixed scale one can set a
// fixed depth per level
// Normalize for fixed-depth by commenting out below
// line
// d.y = (d.depth * 500); //500px per level.
/** My code -starts here * */
if (nodes[i] != null && nodes[i] != 'undefined') {
if (nodes[i].type == "appServer") {
var toolMap = d3.map();
toolMap.set("connId", d.connServer);
toolMap.set("id", d.id);
toolMap.set("type", "Web server");
tooltipMap.set(d.id, toolMap);
} else if (nodes[i].type == "webServer") {
var toolMap = d3.map();
toolMap.set("connId", d.connServer);
toolMap.set("id", d.id);
toolMap.set("type", "Web server");
tooltipMap.set(d.id, toolMap);
} else if (nodes[i].type == "dataServer") {
var toolMap = d3.map();
toolMap.set("connId", d.connServer);
toolMap.set("id", d.id);
toolMap.set("type", "Web server");
tooltipMap.set(d.id, toolMap);
}
}
/** My code ends here */
});
// Update the nodes…
node = svgGroup.selectAll("g.node").data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g").call(dragListener).attr("class", "node").attr("transform",
function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
}).on('click', click);
// Adding the outer circle to highlight the connected servers
nodeEnter.append("circle").attr({
r : 15
}).attr("id", function(d, i) {
if (nodes[i].type == "appServer") {
return "outer" + nodes[i].id;
} else if (nodes[i].type == "webServer") {
return "outer" + nodes[i].id;
} else if (nodes[i].type == "dataServer") {
return "outer" + nodes[i].id;
}
}).style("fill", "transparent").style("stroke-width", function(d, i) {
if (nodes[i].type == "appServer") {
return "1";
} else if (nodes[i].type == "webServer") {
return "1";
} else if (nodes[i].type == "dataServer") {
return "1";
} else {
return "0";
}
}).style("stroke", "#fff");
nodeEnter.append("circle").attr("r", 5).attr("id", function(d, i) {
if (nodes[i].type == "appServer") {
return nodes[i].id;
} else if (nodes[i].type == "webServer") {
return nodes[i].id;
} else if (nodes[i].type == "dataServer") {
return nodes[i].id;
}
}).style("filter", function(d, i) {
if (nodes[i].type == "vm") {
return "url(#virtualMac)";
} else if (nodes[i].type == "container") {
return "url(#container)";
} else if (nodes[i].type == "appServer") {
return "url(#appserver)";
} else if (nodes[i].type == "webServer") {
return "url(#webserver)";
} else if (nodes[i].type == "sto") {
return "url(#storage)";
} else if (nodes[i].type == "dataServer") {
return "url(#dbserver)";
} else if (nodes[i].type == "network") {
return "url(#network)";
} else if (nodes[i].type == "lb") {
return "url(#loadbalancer)";
}
}).style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
}).on("mouseover", function(d) {
getConServers(this, d3.event, "enter");
}).on("mouseout", function(d) {
getConServers(this, d3.event, "exit");
});
nodeEnter.append("text").attr("x", function(d) {
return d.children || d._children ? -10 : 10;
}).attr("dy", ".35em").attr('class', 'nodeText').attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
}).text(function(d) {
return d.name;
}).style("fill-opacity", 0);
// phantom node to give us mouseover in a radius around it
nodeEnter.append("circle").attr('class', 'ghostCircle').attr("r", 30).attr("opacity", 0.2) // change
// this
// to zero to
// hide the
// target area
.style("fill", "red").attr('pointer-events', 'mouseover').on("mouseover", function(node) {
overCircle(node);
}).on("mouseout", function(node) {
outCircle(node);
});
// Update the text to reflect whether node has children or
// not.
node.select('text').attr("x", function(d) {
return d.children || d._children ? -10 : 10;
}).attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
}).text(function(d) {
return d.name;
});
// Change the circle fill depending on whether it has
// children and is collapsed
node.select("circle.nodeCircle").attr("r", 4.5).style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
// Transition nodes to their new position.
var nodeUpdate = node.transition().duration(duration).attr("transform", function(d) {
return "translate(" + (d.x - 8) + "," + -d.y + ")";
});
// Fade the text in
nodeUpdate.select("text").style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition().duration(duration).attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
}).remove();
nodeExit.select("circle").attr("r", 0);
nodeExit.select("text").style("fill-opacity", 0);
// Update the links…
var link = svgGroup.selectAll("path.link").data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g").attr("class", "link").attr("stroke-dasharray", function(d) {
return (d.source.parent) ? "6,6" : "1,0";
}).attr("d", function(d) {
var o = {
x : source.x0,
y : source.y0
};
return diagonal({
source : o,
target : o
});
});
// Transition links to their new position.
link.transition().duration(duration).attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition().duration(duration).attr("d", function(d) {
var o = {
x : source.x,
y : source.y
};
return diagonal({
source : o,
target : o
});
}).remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Append a group which holds all nodes and which the zoom
// Listener can act upon.
var svgGroup = baseSvg.append("g");
// Define the root
root = treeData;
root.x0 = viewerHeight / 2;
root.y0 = 0;
// Layout the tree initially and center on the root node.
update(root);
centerNode(root);
addLoadSLAButtons();
});
function addLoadSLAButtons() {
var slaDiv = document.createElement("div");
slaDiv.setAttribute("id", "slaDiv");
slaDiv.style.position = "relative";
slaDiv.style.left = "35px";
slaDiv.style.top = "-250px";
slaDiv.style.width = "100px";
var slaButton = document.createElement("input");
slaButton.setAttribute("type", "button");
slaButton.setAttribute("value", "View/Modify ServiceContract");
slaButton.setAttribute("class", "btn btn-info");
slaButton.setAttribute("id", "addSla");
slaButton.style.position = "absolute";
slaButton.setAttribute("onclick", "");// this will be modified once we
// receive clarification on the
// functionality.
document.getElementById("tree-container").appendChild(slaDiv);
document.getElementById("slaDiv").appendChild(slaButton);
var relDiv = document.createElement("div");
relDiv.setAttribute("id", "relDiv");
relDiv.style.position = "relative";
relDiv.style.left = "35px";
relDiv.style.top = "-190px";
relDiv.style.width = "100px";
var loadButton = document.createElement("input");
loadButton.setAttribute("type", "button");
loadButton.setAttribute("value", "Add Load");
loadButton.setAttribute("class", "btn btn-cust-info");
loadButton.setAttribute("id", "addload");
loadButton.style.position = "absolute";
loadButton.style.align = "left";
loadButton.setAttribute("onclick", ""); // reLoadPage()
document.getElementById("tree-container").appendChild(relDiv);
document.getElementById("relDiv").appendChild(loadButton);
}
最佳答案
更好的方法是使用 separation() 方法。请查看文档了解详细信息,原则上使用该方法定义节点之间的最小距离,但在您的情况下,您需要限制仅对某些节点进行 gapos,这就是为什么在 separation()) 中有条件
var tree = d3.layout.tree()
.separation(function(a, b) { return ((a.parent == root) && (b.parent == root)) ? 3 : 1; })
.size([height, width - 160]);
下图是没有调用 separation() 的代码行的树:(我在前面的示例中添加了节点 AA 和 ZZ,还有 BB 和 CC,但这些节点至少在这种情况下显示为 OK)
...这里是那一行:
现场 jsfiddles:
只是为了好玩,我进一步修改了从“?3:1;})”到“?5:.6;})”的行,我得到了:
关于javascript - 增加我的 D3 树布局的节点之间的差距,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20797521/
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee
我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
是否可以为特定(或所有)项目使用多个布局?例如,我有几个项目,我想对其应用两种不同的布局。一个是绿色的,一个是蓝色的(但是)。我想将它们编译到我的输出目录中的两个不同文件夹中(例如v1和v2)。我一直在玩弄规则和编译block,但我不知道这是怎么回事。因为,每个项目在编译过程中只编译一次,我不能告诉nanoc第一次用layout1编译,第二次用layout2编译。我试过这样的东西,但它导致输出文件损坏。compile'*'doifitem.binary?#don’tfilterbinaryitemselsefilter:erblayout'layout1'layout'layout2'
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年