笔者使用Arduino编写ESP32-CAM的驱动程序,版本为1.8.19。在较新的版本中,Arduino的UI风格发生了变化,不过下面配置的功能基本保留,读者注意辨别其中的异同之处。
1.首先,我们需要在Arduino中配置ESP32开发板的开发环境。打开Arduino,按如下路径依次点击:“文件”
→
\rightarrow
→ “首选项”,找到“附加开发板管理器网址”,如图1.1所示。


3.按照界面上“一行一个”的指示,将下面两个网址输入进去:
https://dl.espressif.com/dl/package_esp32_index.json
https://github.com/espressif/arduino-esp32/releases/download/2.0.2/package_esp32_dev_index.json
然后点击“好”即可。
1.接下来我们需要配置开发板。按如下路径依次点击:“工具”
→
\rightarrow
→ “开发板”
→
\rightarrow
→ “开发板管理器”,弹出界面如图1.3:

2.在显示有“对搜索进行过滤…”字样的搜索框内输入“ESP32”,显示界面如图1.4:

3.选择版本2.0.2,然后点击“安装”,等待其安装好即可。
4.安装好后,按如下路径点击:“工具” → \rightarrow → “开发板” → \rightarrow → “ESP32 Arduino” → \rightarrow → “AI Thinker ESP32 CAM”。至此,我们就配置好了ESP32的开发环境和开发板,可以进行下一步的开发了。
1.由于官方的库并不能驱动ESP32-CAM,因此在此处我参考了CSDN用户“ShemuelHe”的博客。博客链接为本节最后的参考资料当中的第二个链接。在此处,我们需要使用GitHub上大神yoursunny用户所提供的库。下载链接为本节参考资料的第一个链接。点进他的主页后,如图1.5所示:

2.点击“Download ZIP”,将代码压缩包下载下来。然后回到Arduino,按如下路径点击:“项目”
→
\rightarrow
→ “加载库”
→
\rightarrow
→ “添加.ZIP库”,弹出界面如图1.6所示:

3.找到刚才下载的库的路径,找到.ZIP文件(该压缩包不需要解压),选中后点击“打开”。这样,这个库就添加好了。其他项目中,如果要添加非官方库,也可以通过这样的方式。
完整Arduino代码(经过测试,直接复制可用)
#include <SPI.h>
#include <sd_defines.h>
#include <SD_MMC.h>
#include <SD.h>
#include <sd_defines.h>
#include <sd_diskio.h>
#include <esp32cam.h>
#include <WebServer.h>
#include <WiFi.h>
#include "cJSON.h"
#include "FS.h"
#include "esp_camera.h"
//以下改成要连接的WiFi名称和密码
const char* WIFI_SSID = "******";
const char* WIFI_PASS = "******";
WebServer server(80);
static auto loRes = esp32cam::Resolution::find(320, 240);
static auto hiRes = esp32cam::Resolution::find(1280, 1024);
//UXGA:分辨率为1600*1200的输出格式,SXGA(1280*1024)、XVGA(1280*960)、WXGA(1280*800)、XGA(1024*768)、SVGA(800*600)、VGA(640*480)、CIF(352*288)和QQVGA(160*120)等。
char path[] = "/1.jpg";
int order = 1;
void handleBmp()
{
if (!esp32cam::Camera.changeResolution(loRes)) {
Serial.println("SET-LO-RES FAIL");
}
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast<int>(frame->size()));
if (!frame->toBmp()) {
Serial.println("CONVERT FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CONVERT OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast<int>(frame->size()));
server.setContentLength(frame->size());
server.send(200, "image/bmp");
WiFiClient client = server.client();
frame->writeTo(client);
}
void serveJpg()
{
auto frame = esp32cam::capture();
if (frame == nullptr) {
Serial.println("CAPTURE FAIL");
server.send(503, "", "");
return;
}
Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
static_cast<int>(frame->size()));
server.setContentLength(frame->size());
server.send(200, "image/jpeg");
WiFiClient client = server.client();
frame->writeTo(client);
}
void handleJpgLo()
{
if (!esp32cam::Camera.changeResolution(loRes)) {
Serial.println("SET-LO-RES FAIL");
}
serveJpg();
}
void handleJpgHi()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
serveJpg();
}
void handleJpg()
{
server.sendHeader("Location", "/cam-hi.jpg");
server.send(302, "", "");
}
void handleMjpeg()
{
if (!esp32cam::Camera.changeResolution(hiRes)) {
Serial.println("SET-HI-RES FAIL");
}
Serial.println("STREAM BEGIN");
WiFiClient client = server.client();
auto startTime = millis();
int res = esp32cam::Camera.streamMjpeg(client);
if (res <= 0) {
Serial.printf("STREAM ERROR %d\n", res);
return;
}
auto duration = millis() - startTime;
Serial.printf("STREAM END %dfrm %0.2ffps\n", res, 1000.0 * res / duration);
}
// Init SD Card
void sd_init()
{
//The argument ("/sdcard",true) means closing LED light on the board
if (!SD_MMC.begin("/sdcard",true)) {
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
}
else if (cardType == CARD_SD) {
Serial.println("SDSC");
}
else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
}
else {
Serial.println("UNKNOWN");
}
//Get the size of SD card, unit: MB
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf("SD 卡容量大小: %lluMB\n", cardSize);
}
void setup()
{
Serial.begin(115200);
Serial.println();
{
using namespace esp32cam;
Config cfg;
cfg.setPins(pins::AiThinker);
cfg.setResolution(hiRes);
cfg.setBufferCount(2);
cfg.setJpeg(80);
bool ok = Camera.begin(cfg);
Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
}
sd_init();
delay(5000);
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.print("http://");
Serial.println(WiFi.localIP());
Serial.println(" /cam.bmp");
Serial.println(" /cam-lo.jpg");
Serial.println(" /cam-hi.jpg");
Serial.println(" /cam.mjpeg");
server.on("/cam.bmp", handleBmp);
server.on("/cam-lo.jpg", handleJpgLo);
server.on("/cam-hi.jpg", handleJpgHi);
server.on("/cam.jpg", handleJpg);
server.on("/cam.mjpeg", handleMjpeg);
server.begin();
}
void loop()
{
server.handleClient();
camera_fb_t * fb = esp_camera_fb_get();
sprintf(path,"/%d.jpg",order);
if (fb == NULL)
{
Serial.println( "Get picture failed");
}
else
{
fs::FS &fs = SD_MMC;
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if (!file)
{
Serial.println("Failed to Create a File!");
}
else
{
file.write(fb->buf , fb->len);
}
esp_camera_fb_return(fb);
order += 1;
}
}
最终跑出来的效果如图1.7、1.8所示:
1.Python读取URL获取视频流

图中“图像显示”的程序是通过Python编写,这一部分涉及PyQt5的使用,将在1.2节详细介绍。
2.保存拍摄的照片到TF卡

注意: ESP32-CAM的TF卡槽最多支持4G容量的TF卡,超过此容量的TF卡均不能被成功识别。
此部分参考资料如下:
1.GitHub - yoursunny/esp32cam: OV2640 camera on ESP32-CAM, Arduino library / https://github.com/yoursunny/esp32cam
获取视频流的ESP32代码包
2.【ESP32-CAM】使用opencv获取ESP32-CAM视频流(一)_ShemuelHe的博客-CSDN博客_esp32移植opencv / https://blog.csdn.net/ShemuelHe/article/details/121365730?utm_source=app&app_version=5.0.1&code=app_1562916241&uLinkId=usr1mkqgl919blen
WiFi获取视频流,python openCV实现视频流获取
3.(ESP32学习16)ESP32_CAM获取图片并且保存文件名为当前时间_bird1999625的博客-CSDN博客 / https://blog.csdn.net/ailta/article/details/106866261
CAM摄像头拍摄图像并保存至TF卡
在1.1.4节中介绍了电脑端读取URL图像,获取视频流的效果。该节中的参考链接2中提供了OpenCV的方式来读取视频流。而由于我们需要将程序移植到不同的电脑上使用,因此需要将Python脚本打包成.exe执行文件。这一节将介绍如何使用Python读取URL图像并将整个程序打包成.exe可执行文件,使其在没有安装Python开发环境的电脑上也能运行。
此部分内容在网上有很多详细的资料,此处不再赘述,可参考本节末尾的参考资料当中的第一个链接,相当详细,将每一步都列举了出来,按操作即可成功安装。
1.打开PyCharm,新建工程和py脚本,然后按照如下路径依次点击:“文件”
→
\rightarrow
→ “设置”
→
\rightarrow
→ “项目”
→
\rightarrow
→ “Python解释器”,界面如图1.9所示:

2.点击红圈当中的“+”按钮,进入下面的界面:

3.在左上角画红色线的搜索栏中输入“PyQt5”;然后选中右下角橙色线处“指定版本”,选择最新的版本;最后点击左下角红圈圈住的“安装软件包”,等待安装这个包即可。
4.同样,在此工程中,需要安装“PyQt5-tools”包。操作方法同上。
5.安装好了之后,找到这两个包的安装位置。笔者的位置如下:

这两个包安装在路径“\UITest\venv\Lib\site-packages”中。
1.按如下路径依次点击:“文件”
→
\rightarrow
→ “设置”
→
\rightarrow
→ “项目”
→
\rightarrow
→ “工具”
→
\rightarrow
→ “外部工具” ,进入如下界面:

2.点击上图中左上角的“+”,弹出如下窗口:

3.在“名称”栏中,输入外部工具的名字,在这里我们将其命名为“QtDesigner”;在“程序栏”中,输入“designer.exe”的路径;在“工作目录”栏中,输入“$FileDir$”。其中,“designer.exe”的路径如下:

输入好后,界面如下:

4.同样,我们需要添加外部工具“pyuic5.exe”程序。该程序将QtDesigner中设计好的UI界面转化成Python脚本,供我们编程开发使用。在“外部工具”中,再点一次“+”,将该工具添加进来:

我们将该工具命名为PyUIC,“程序”一栏添加pyuic5.exe文件的路径;“实参”一栏添加如下信息:$FileName$ -o $FileNameWithoutExtension$.py;“工作目录”一栏添加:$ProjectFileDir$。然后点击“确定”。这样,我们就添加好了我们所需要的外部工具。
在1.2.3节中,我们下载好了开发.exe文件所需要的软件包,并添加好了外部工具。至此,准备工作已经全部完成,我们可以开始使用QtDesigner来开发我们的软件了。
1.打开Qt:在PyCharm顶端的菜单栏中,按照如下顺序点击:“工具”
→
\rightarrow
→ “External tools”
→
\rightarrow
→ “QtDesigner”:

打开后,界面如图1.20所示:

左侧为常用的一些控件树;中间的部分为设计工具提供给我们的一些模板;右侧为控件树和当前选中的控件的一些属性。在这里,我们选择“Main Window”,点击“Create”,界面如图1.21所示:

这个时候,我们就可以添加各种控件并给它们配置属性,以达到我们的目的。
2.关于如何布局,读者可参考白月黑羽的教程,相当详细Python Qt 图形界面编程 - PySide2 PyQt5 PyQt PySide_哔哩哔哩_bilibili / https://www.bilibili.com/video/BV1cJ411R7bP?spm_id_from=333.999.0.0,此文档中不再赘述。读者需要尤其注意Layout的使用。本项目中需要使用PyQt5开发的程序较为简单,就是读取URL获取视频流,因此,笔者所设计的UI布局如图1.22所示:

在这个界面中,我使用了一个Label控件用来显示图像,三个按钮控件来触发事件,一个文本框用来输入URL地址。控件添加完成后,使用Layout进行布局。
在QtDesigner中,我们可以通过QSS的方式来美化控件。此处我以按钮控件为例,简要介绍QSS的使用。
1.选中按钮控件,在属性栏中,找到“Qwidget”
→
\rightarrow
→ “Stylesheet”,点击“StyleSheet”右侧的三点按钮:

弹出界面如图1.24所示:

2.在这个编辑框里,我们可以输入如下格式的代码:
QPushButton {// 按钮一般属性
background-color: white ;
font-size:16px;
color:black;
border-radius: 15px;//圆角半径
font-family:微软雅黑;//字体
background:rgb(255, 255, 255);//背景颜色
border:2px solid black;//边框宽度
}
QPushButton:hover{ //鼠标悬浮在按钮上时按钮的属性
background:rgb(237, 108, 0, 150);//鼠标悬浮时背景颜色为rgb(237,108,0,150)
}
QPushButton:pressed{ //鼠标按下时按钮的属性
background:white;//按下鼠标时背景颜色为白色
}
3.然后点击“OK”即可。这个时候,控件的外观就会按照我们代码所设定的样子显示出来。
设计好UI界面后,我们就需要将UI文件转化成.py文件,在PyCharm编辑器中编写程序了。
1.在左侧的文件预览器中,找到我们的.ui文件,右键单击,然后左键依次点击:“External tools”
→
\rightarrow
→ “PyUIC”:

2.点击后,会生成一个和.ui文件同名的.py文件。控制台和文件树如下所示:

3.双击打开VideoShow.py文件,这时候编辑器就会显示我们所创建的UI对应的.py文件代码了。
当我们有了.ui文件对应的.py文件后,我们就需要将这个ui使用代码运行起来,最终实现我们想要的功能。在这一节,我将以项目中的图像显示为例,简要介绍如何将我们创建的ui在PyCharm中运行出来。
1.在.ui文件和刚才生成的.py文件同一个文件夹下新建一个.py文件,在这里,我将其命名为“Test.py”。
2.添加如下代码:
import sys
import requests
import VideoShow
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QImage, QPixmap, QIcon
class MainWindows(QMainWindow, VideoShow.Ui_ShowVideo):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setupUi(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainWindows()
# 设置软件窗口的名字
main.setWindowTitle('Submarine 图像显示')
# 设置软件的图标。注意,"icon_16.png"文件需要和工程在同一文件夹下。
main.setWindowIcon(QIcon("icon_16.png"))
# 显示UI
main.show()
sys.exit(app.exec_())
3.运行上述代码,我们就可以看到我们刚才设计的UI界面了:

在1.2.7节,我们成功地将我们的UI界面运行了起来。但是,这个时候这个界面是没有什么用的——我们还没有给相关控件添加功能代码,使它们发挥各自的作用。在这里,我们就需要给控件编写信号和槽函数的相关代码了。关于信号和槽的基本概念,在1.2.4节的链接中也有较为详细的介绍。简单来说,当我们点击按钮时,发送一个信号,这个信号被连接到一个槽函数当中,该函数就会执行相应的功能代码。在这个工程中,我们需要给三个按钮编写槽函数,并且开启一个定时器,每隔一段时间读取一次URL。在这一节中,我将简单介绍如何编写功能代码。
1.控件命名
在QtDesigner中的控件树中,我们可以给我们的控件命名:

例如此处,我将按钮控件分别命名为“CloseAppButton”、“CloseVideoButton”、“OpenVideoButton”。在PyUIC生成的代码中,我们如果想要调用这几个控件,就需要调用这些变量名。
2.槽函数的编写
首先,我们编写一下“打开视频”这个按钮的槽函数。代码如下:
def onOpenVideoButtonClicked(self):
self.timer.start(20) # 设置计时间隔并启动,间隔20ms
self.VideoShowLabel.setScaledContents(True)
在这个函数中,我们将定时器启动,并设置读取时间间隔为20ms;设置显示图像的Label控件为自适应图片大小。那么,这个槽函数名就叫做“onOpenVideoButtonClicked”,“打开视频”按钮被按下后,程序就会执行该函数当中的代码。
3.将信号连接到槽函数上
编写好了槽函数后,我们需要将信号连接到槽函数上。比如,我们需要将“打开视频”按钮被点击的信号连接到刚才我们编写的槽函数上,以使槽函数执行功能。连接信号的代码如下:
self.OpenVideoButton.clicked.connect(self.onOpenVideoButtonClicked)
这样,当我们运行UI后,点击这些控件,程序就会执行相应的功能了。
4.整个App的代码如下:
import sys
import requests
import VideoShow
from PyQt5.QtGui import QImage, QPixmap, QIcon
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import QTimer
class MainWindows(QMainWindow, VideoShow.Ui_ShowVideo):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setupUi(self)
# 初始化一个定时器
self.timer = QTimer(self)
# 计时结束调用operate()方法
self.timer.timeout.connect(self.operate)
# 将开启信号与槽函数关联
self.OpenVideoButton.clicked.connect(self.onOpenVideoButtonClicked)
self.CloseVideoButton.clicked.connect(self.onCloseVideoButtonClicked)
self.CloseAppButton.clicked.connect(self.onCloseAppButtonClicked)
def onOpenVideoButtonClicked(self):
self.timer.start(20) # 设置计时间隔并启动,间隔20ms
self.VideoShowLabel.setScaledContents(True)
def onCloseVideoButtonClicked(self):
self.timer.stop() # 关闭定时器
def onCloseAppButtonClicked(self):
self.timer.stop() # 关闭定时器
self.close() # 关闭应用程序
# 定时器的处理函数
def operate(self):
url = self.URLEdit.text() # 获取URL
# print(url)
res = requests.get(url)
img = QImage.fromData(res.content)
self.VideoShowLabel.setPixmap(QPixmap.fromImage(img))
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainWindows()
main.setWindowTitle('Submarine 图像显示')
main.setWindowIcon(QIcon("icon_16.png"))
main.show()
sys.exit(app.exec_())
这样,我们就编写好了一个简单的UI界面。
1.和1.2.2节安装PyQt5包一样,我们需要将“pyinstaller”这个包下载下来:

点击“安装软件包”,等待这个包安装好即可。
2.在控制台下端找到“终端”并点击,出现如下界面:

3.然后在上面的窗口中输入如下指令:
pyinstaller -F -w -i icon.ico Test.py
其中,“-F”表示打包后只生成一个.exe文件(也可以理解为覆盖掉之前产生的同名.exe文件);“-w”表示不使用控制台;“-i”表示改变生成的.exe文件的图标,后面要跟上图标文件的文件名和格式。一般这里支持.ico格式,读者可以在PhotoShop中制作好自己的图标并将其放在工程文件夹下。最后,添加上我们要打包的.py文件名。
常用选项及说明如下:
4.按下回车,等待打包完毕:

5.打开我们的工程文件夹下的“dist”文件夹,如下图所示:

可以看到我们刚才生成的.exe文件了。将其复制到我们存放.ico文件的文件夹中(否则图标将不会显示)并双击打开,程序就可以正常运行了:

至此,我们就制作完成了图像传输的简单.exe程序。该程序可以在没有安装python环境的计算机中运行。
此部分参考资料如下:
1.PyCharm2021安装教程_学习H的博客-CSDN博客_pycharm2021安装教程
2. 将python程序打包成exe_蹦跶的小羊羔的博客-CSDN博客_python打包成exe
3.Python Qt 图形界面编程 - PySide2 PyQt5 PyQt PySide_哔哩哔哩_bilibili
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
我想用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中编写命令行实用程序
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我有一个在Linux服务器上运行的ruby脚本。它不使用rails或任何东西。它基本上是一个命令行ruby脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b