十年河东,十年河西,莫欺少年穷
学无止境,精益求精
netcore3.1控制台应用程序,引入MQTTnet 2.8版本
订阅端:
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using MQTTnet;
using MQTTnet.Server;
using MQTTnet.Client;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using MQTTnet.Protocol;
namespace swapConsole
{
class Program
{
private static MqttClient mqttClient = null;
private static string topic = "test123ABC";
private static IMqttClientOptions Options
{
get
{
MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder();
builder.WithCleanSession(false);
//用户名 密码
builder.WithCredentials("", "");
var id = Guid.NewGuid().ToString();
builder.WithClientId(id);
builder.WithTcpServer("1270.0.0.0", 1883);
return builder.Build();
}
}
static async Task Main(string[] args)
{
MqttFactory factory = new MqttFactory();
if (mqttClient == null)
{
mqttClient = (MqttClient)factory.CreateMqttClient();
mqttClient.ApplicationMessageReceived += MqttClient_ApplicationMessageReceived;
mqttClient.Connected += MqttClient_Connected;
mqttClient.Disconnected += async (s, e) =>
{
Console.WriteLine("尝试重连!" + Environment.NewLine);
await ConnectToServer();
};
}
await ConnectToServer();
Console.ReadLine();
}
/// <summary>
/// 连接MQTT服务器
/// </summary>
private static async Task ConnectToServer()
{
try
{
var res =await mqttClient.ConnectAsync(Options);
}
catch (Exception ex)
{
Console.WriteLine($"连接到MQTT服务器失败!" + Environment.NewLine + ex.Message + Environment.NewLine);
}
}
/// <summary>
/// 连接MQTT服务器触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MqttClient_Connected(object sender, EventArgs e)
{
Console.WriteLine("已连接到MQTT服务器!" + Environment.NewLine);
SubscribeInfo();
}
/// <summary>
/// 接收消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MqttClient_ApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)
{
Console.WriteLine($">> {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}{Environment.NewLine}");
}
/// <summary>
/// 订阅消息
/// </summary>
public static void SubscribeInfo()
{
if (string.IsNullOrEmpty(topic))
{
Console.WriteLine("订阅主题不能为空!");
return;
}
if (!mqttClient.IsConnected)
{
Console.WriteLine("MQTT客户端尚未连接!");
return;
}
mqttClient.SubscribeAsync(new List<TopicFilter> {
new TopicFilter(topic, MqttQualityOfServiceLevel.ExactlyOnce)
});
Console.WriteLine($"已订阅[{topic}]主题" + Environment.NewLine);
}
/// <summary>
/// 退订消息
/// </summary>
public static void UnSubscribeInfo()
{
if (string.IsNullOrEmpty(topic))
{
Console.WriteLine("退订主题不能为空!");
return;
}
if (!mqttClient.IsConnected)
{
Console.WriteLine("MQTT客户端尚未连接!");
return;
}
mqttClient.UnsubscribeAsync(topic);
Console.WriteLine($"已退订[{topic}]主题" + Environment.NewLine);
}
}
}
View Code发布端:
using MQTTnet;
using MQTTnet.Client;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace swapPublish
{
class Program
{
private static MqttClient mqttClient = null;
private static string topic = "test123ABC";
private static IMqttClientOptions Options
{
get
{
MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder();
builder.WithCleanSession(false);
//用户名 密码
builder.WithCredentials("", "");
var id = Guid.NewGuid().ToString();
builder.WithClientId(id);
builder.WithTcpServer("127.0.0.1", 1883);
return builder.Build();
}
}
static async Task Main(string[] args)
{
MqttFactory factory = new MqttFactory();
if (mqttClient == null)
{
mqttClient = (MqttClient)factory.CreateMqttClient();
mqttClient.Connected += MqttClient_Connected;
mqttClient.Disconnected += async(s, e) =>
{
Console.WriteLine("尝试重连!" + Environment.NewLine);
await ConnectToServer();
};
}
await ConnectToServer();
Console.WriteLine("已断开MQTT连接!" + Environment.NewLine);
Console.ReadLine();
}
/// <summary>
/// 连接MQTT服务器
/// </summary>
private static async Task ConnectToServer()
{
try
{
var res = await mqttClient.ConnectAsync(Options);
}
catch (Exception ex)
{
Console.WriteLine($"连接到MQTT服务器失败!" + Environment.NewLine + ex.Message + Environment.NewLine);
}
}
/// <summary>
/// 连接MQTT服务器触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MqttClient_Connected(object sender, EventArgs e)
{
Console.WriteLine("已连接到MQTT服务器!" + Environment.NewLine);
for(int i = 0; i < 10; i++)
{
var tak = PublishInfo();
Thread.Sleep(2000);
}
}
private static async Task PublishInfo( )
{
if (string.IsNullOrEmpty(topic))
{
Console.WriteLine("发布主题不能为空!");
return;
}
string inputString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
MqttApplicationMessageBuilder builder = new MqttApplicationMessageBuilder();
builder.WithPayload(Encoding.UTF8.GetBytes(inputString));
builder.WithTopic(topic);
builder.WithRetainFlag(false);
builder.WithExactlyOnceQoS();
await mqttClient.PublishAsync(builder.Build());
}
}
}
View Code如何只允许一个客户端消费同一个消息,暂时未解决!
大家有解决方法,请贴出评论。谢谢
MQTTnet 3.0.16 版本的使用
客户端:
using MQTTnet;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Receiving;
using MQTTnet.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace mqttsub
{
class Program
{
static async Task Main(string[] args)
{
MqttClient mqtt = new MqttClient();
await mqtt.StartAsync();
Console.ReadKey();
}
}
public class MqttClient
{
private IMqttClient client;
private IMqttClientOptions options;
MqttClientDto model =null;
public MqttClient()
{
model = new MqttClientDto
{
Account = "",
PassWord = "",
ClientId = Guid.NewGuid().ToString(),
IP = "",
Port = 1883,
Topic="test/+/ABC" //通配符模式 该模式匹配 test/123/ABC testABC test/DDDDD/ABC 等
};
}
public async Task StartAsync()
{
try
{
client = new MqttFactory().CreateMqttClient();
var build = new MqttClientOptionsBuilder()
//配置客户端Id
.WithClientId(Guid.NewGuid().ToString())
//配置登录账号
.WithCredentials(model.Account,model.PassWord)
//配置服务器IP端口 这里得端口号是可空的
.WithTcpServer(model.IP, 1883)
.WithCleanSession();
options = build.Build();
//收到服务器发来消息
client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(MessageReceivedHandler);
//client.UseApplicationMessageReceivedHandler(args=> {
// Console.WriteLine("===================================================");
// Console.WriteLine("收到消息:");
// Console.WriteLine($"主题:{args.ApplicationMessage.Topic}");
// Console.WriteLine($"消息:{Encoding.UTF8.GetString(args.ApplicationMessage.Payload)}");
// Console.WriteLine("+++++++++++++++++++++++++++++++++++++++++++++++++++");
// Console.WriteLine();
//});
//连接成功
client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(ConnectedHandler);
//client.UseConnectedHandler(args=> {
// Console.WriteLine("本客户端已连接成功");
// Console.WriteLine($"地址:{model.IP}");
// Console.WriteLine($"端口:{model.Port}");
// Console.WriteLine($"客户端:{model.ClientId}");
// Console.WriteLine($"账号:{model.Account}");
// Console.WriteLine();
// //第1种订阅方式
// client.SubscribeAsync("主题名称").GetAwaiter().GetResult();
// //第2种订阅方式
// List<MqttTopicFilter> Topics = new List<MqttTopicFilter>();
// Topics.Add(new MqttTopicFilter() { Topic = "主题名称A", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
// Topics.Add(new MqttTopicFilter() { Topic = "主题名称B" });
// Topics.Add(new MqttTopicFilter() { Topic = "主题名称C" });
// client.SubscribeAsync(Topics.ToArray()).GetAwaiter().GetResult();
// //第3种订阅方式
// MqttClientSubscribeOptionsBuilder builder = new MqttClientSubscribeOptionsBuilder();
// builder.WithTopicFilter("AAA");
// client.SubscribeAsync(builder.Build()).GetAwaiter().GetResult();
//});
//断开连接 重连就写在此处
client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(DisconnectedHandler);
//client.UseDisconnectedHandler(args =>
//{
// Console.WriteLine("本客户端已经断开连接");
// Console.WriteLine();
// try
// {
// client.ConnectAsync(options).GetAwaiter().GetResult();
// }
// catch (Exception ex)
// {
// Console.WriteLine("重连失败");
// }
//});
//客户端发送消息
//await client.PublishAsync("你想要的主题", "你需要发送的东西");
//await client.PublishAsync("你想要的主题", Encoding.UTF8.GetBytes("你需要发送的东西").ToList());
//连接
await client.ConnectAsync(options);
}
catch (MqttConnectingFailedException)
{
Console.WriteLine("身份校验失败");
}
catch (Exception ex)
{
Console.WriteLine("出现异常");
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// 客户端断开连接后,如果需要重连在此处实现
/// </summary>
/// <param name="obj"></param>
private async void DisconnectedHandler(MqttClientDisconnectedEventArgs obj)
{
Console.WriteLine("本客户端已经断开连接");
Console.WriteLine();
try
{
await client.ConnectAsync(options);
}
catch (Exception)
{
Console.WriteLine("重连失败");
}
}
/// <summary>
/// 连接成功 在此处做订阅主题(Topic)操作
/// </summary>
/// <param name="obj"></param>
private async void ConnectedHandler(MqttClientConnectedEventArgs obj)
{
Console.WriteLine("本客户端已连接成功");
Console.WriteLine($"地址:{model.IP}");
Console.WriteLine($"端口:{model.Port}");
Console.WriteLine($"客户端:{model.ClientId}");
Console.WriteLine($"账号:{model.Account}");
Console.WriteLine();
//第1种订阅方式
// client.SubscribeAsync("主题名称").GetAwaiter().GetResult();
//第2种订阅方式
List<MqttTopicFilter> Topics = new List<MqttTopicFilter>();
Topics.Add(new MqttTopicFilter() { Topic = model.Topic, QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce});
//Topics.Add(new MqttTopicFilter() { Topic = "主题名称B" });
//Topics.Add(new MqttTopicFilter() { Topic = "主题名称C" });
await client.SubscribeAsync(Topics.ToArray());
//第3种订阅方式
//MqttClientSubscribeOptionsBuilder builder = new MqttClientSubscribeOptionsBuilder();
//builder.WithTopicFilter("AAA");
//client.SubscribeAsync(builder.Build()).GetAwaiter().GetResult();
}
/// <summary>
/// 收到消息
/// </summary>
/// <param name="obj"></param>
private void MessageReceivedHandler(MqttApplicationMessageReceivedEventArgs obj)
{
Console.WriteLine("===================================================");
Console.WriteLine("收到消息:");
Console.WriteLine($"主题:{obj.ApplicationMessage.Topic}");
Console.WriteLine($"消息:{Encoding.UTF8.GetString(obj.ApplicationMessage.Payload)}");
Console.WriteLine("+++++++++++++++++++++++++++++++++++++++++++++++++++");
Console.WriteLine();
}
}
public class MqttClientDto
{
/// <summary>
/// 连接地址
/// </summary>
public string IP { get; set; }
/// <summary>
/// 账号
/// </summary>
public string Account { get; set; }
/// <summary>
/// 密码
/// </summary>
public string PassWord { get; set; }
/// <summary>
/// 客户端Id
/// </summary>
public string ClientId { get; set; }
public int Port { get; set; }
public string Topic { get; set; }
}
}
View Code服务端:
using MQTTnet;
using MQTTnet.Client.Receiving;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace MqttPub
{
class Program
{
static async Task Main(string[] args)
{
await new ServerDome(). StartAsync();
Console.Read();
}
}
public class ServerDome
{
private IMqttServer server;
MqttClientDto model = null;
public ServerDome()
{
model = new MqttClientDto
{
Account = "",
PassWord = "",
ClientId = Guid.NewGuid().ToString(),
IP = "",
Port = 1883,
Topic = "test"
};
}
public async Task StartAsync()
{
if (server == null || !server.IsStarted)
{
server = new MqttFactory().CreateMqttServer();
MqttServerOptionsBuilder serverOptions = new MqttServerOptionsBuilder();
//、默认监听端口
serverOptions.WithDefaultEndpointPort(model.Port);
//校验客户端信息
serverOptions.WithConnectionValidator(client => {
string Account = client.Username;
string PassWord = client.Password;
string clientid = client.ClientId;
if (Account == "" && PassWord == "")
{
client.ReasonCode = MqttConnectReasonCode.Success;
Console.WriteLine("校验成功");
}
else
{
client.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
Console.WriteLine("校验失败");
}
});
//客户端发送消息监听
server.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(MessageReceivedHandler);
//server.UseApplicationMessageReceivedHandler(args=>{
// Console.WriteLine("===================================================");
// Console.WriteLine("收到消息:");
// Console.WriteLine($"客户端:{args.ClientId}");
// Console.WriteLine($"主题:{args.ApplicationMessage.Topic}");
// Console.WriteLine($"消息:{Encoding.UTF8.GetString(args.ApplicationMessage.Payload)}");
// Console.WriteLine("+++++++++++++++++++++++++++++++++++++++++++++++++++");
// Console.WriteLine();
//});
//客户端连接事件
server.ClientConnectedHandler = new MqttServerClientConnectedHandlerDelegate(ClientConnectedHandler);
//server.UseClientConnectedHandler(args =>
//{
// Console.WriteLine($"{args.ClientId}此客户端已经连接到服务器");
//});
//客户端断开连接事件
server.ClientDisconnectedHandler = new MqttServerClientDisconnectedHandlerDelegate(ClientDisconnectedHandler);
//server.UseClientDisconnectedHandler(args => {
// Console.WriteLine($"断开连接的客户端:{args.ClientId}");
// Console.WriteLine($"断开连接类型:{args.DisconnectType.ToString()}");
//});
//客户端订阅主题事件
server.ClientSubscribedTopicHandler = new MqttServerClientSubscribedHandlerDelegate(ClientSubscribedTopicHandler);
//客户端取消订阅主题事件
server.ClientUnsubscribedTopicHandler = new MqttServerClientUnsubscribedTopicHandlerDelegate(ClientUnsubscribedTopicHandler);
//服务器启动事件
server.StartedHandler = new MqttServerStartedHandlerDelegate(StartedHandler);
//服务器停止事件
server.StoppedHandler = new MqttServerStoppedHandlerDelegate(StoppedHandler);
//服务端发送数据
//await server.PublishAsync("你想要的主题","你需要发送的东西");
//var mqttApplicationMessage = new MqttApplicationMessage();
//mqttApplicationMessage.Topic = "你想要的主题";
//mqttApplicationMessage.Payload = Encoding.ASCII.GetBytes("你需要发送的东西");
//await server.PublishAsync(mqttApplicationMessage);
//启动服务器
await server.StartAsync(serverOptions.Build());
}
}
public async Task StopAsync()
{
if (server != null)
{
if (server.IsStarted)
{
await server.StopAsync();
server.Dispose();
}
}
}
/// <summary>
/// 客户端取消订阅主题
/// </summary>
/// <param name="obj"></param>
private void ClientUnsubscribedTopicHandler(MqttServerClientUnsubscribedTopicEventArgs obj)
{
Console.WriteLine($"客户端:{obj.ClientId}");
Console.WriteLine($"取消订阅主题:{obj.TopicFilter}");
}
/// <summary>
/// 客户端订阅的主题
/// </summary>
/// <param name="obj"></param>
private void ClientSubscribedTopicHandler(MqttServerClientSubscribedTopicEventArgs obj)
{
Console.WriteLine($"客户端:{obj.ClientId}");
Console.WriteLine($"订阅主题:{obj.TopicFilter.Topic}");
}
/// <summary>
/// 客户端断开连接
/// </summary>
/// <param name="obj"></param>
private void ClientDisconnectedHandler(MqttServerClientDisconnectedEventArgs obj)
{
Console.WriteLine($"断开连接的客户端:{obj.ClientId}");
Console.WriteLine($"断开连接类型:{obj.DisconnectType.ToString()}");
}
/// <summary>
/// 客户端连接到服务器事件
/// </summary>
/// <param name="obj"></param>
private void ClientConnectedHandler(MqttServerClientConnectedEventArgs obj)
{
throw new NotImplementedException();
}
/// <summary>
/// 收到各个客户端发送的消息
/// </summary>
/// <param name="obj"></param>
private void MessageReceivedHandler(MqttApplicationMessageReceivedEventArgs obj)
{
Console.WriteLine("===================================================");
Console.WriteLine("收到消息:");
Console.WriteLine($"客户端:{obj.ClientId}");
Console.WriteLine($"主题:{obj.ApplicationMessage.Topic}");
Console.WriteLine($"消息:{Encoding.UTF8.GetString(obj.ApplicationMessage.Payload)}");
Console.WriteLine("+++++++++++++++++++++++++++++++++++++++++++++++++++");
Console.WriteLine();
}
/// <summary>
/// MQTT启动服务器事件
/// </summary>
/// <param name="obj"></param>
private void StartedHandler(EventArgs obj)
{
Console.WriteLine($"程序已经启动!监听端口为:{model.Port}");
}
/// <summary>
/// MQTT服务器停止事件
/// </summary>
/// <param name="obj"></param>
private void StoppedHandler(EventArgs obj)
{
Console.WriteLine("程序已经关闭");
}
}
public class MqttClientDto
{
/// <summary>
/// 连接地址
/// </summary>
public string IP { get; set; }
/// <summary>
/// 账号
/// </summary>
public string Account { get; set; }
/// <summary>
/// 密码
/// </summary>
public string PassWord { get; set; }
/// <summary>
/// 客户端Id
/// </summary>
public string ClientId { get; set; }
public int Port { get; set; }
public string Topic { get; set; }
}
}
View Code
这里说明下如何使用通配符
例如,发送 topic 主题为:test/123/ABC 或者 test/234/ABC ,消费者在订阅时,可以使用:test/+/ABC 来订阅该类消息。
发布者发布内容为: test//status ,订阅者订阅的为:test/+/status

当然,发布者也可以在 / / 之间增加内容,例如设备号:

主题名不能使用通配符, 但是主题过滤器中可以使用通配符.因此,订阅者可以通过过滤器接合通配符订阅一类消息
以MQTTnet 3.0.16 为例,开启自动确认,开启不保留最后一跳消息。

我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po