中心端(接收端)
1 .创建中心端控制器(CBCentralManager) 2 .扫描设备(Discover) 3 .连接 (Connect) 4 .获取Service和Characteristic
5 . 数据交互(explore and interact)
6 . 断开链接
外设端(发送端)
- 创建Peripheral管理对象
- 创建Service和Characteristic树
- 发送广告
- 处理读写请求和订阅
蓝牙状态
typedef NS_ENUM(NSInteger, CBManagerState) {
CBManagerStateUnknown = 0,
CBManagerStateResetting,
CBManagerStateUnsupported, //不支持
CBManagerStateUnauthorized, //未授权
CBManagerStatePoweredOff, //关闭
CBManagerStatePoweredOn, //蓝牙打开状态
} NS_ENUM_AVAILABLE(NA, 10_0);
连接状态
/*!
* @enum CBPeripheralState
*
* @discussion Represents the current connection state of a CBPeripheral.
*
*/
typedef NS_ENUM(NSInteger, CBPeripheralState) {
CBPeripheralStateDisconnected = 0,
CBPeripheralStateConnecting,
CBPeripheralStateConnected,
CBPeripheralStateDisconnecting NS_AVAILABLE(NA, 9_0),
} NS_AVAILABLE(NA, 7_0);
CBCentralManagerDelegate
@required
// 更新CentralManager状态,参数central就是当前的Manager。
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
@optional
// 蓝牙状态的保存和恢复,进入后台和重新启动有关
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict;
//扫描外部设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;
//与外设完成连接
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
//连接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
//断开连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
CBPeripheralDelegate
@optional
//peripheral更新
- (void)peripheralDidUpdateName:(CBPeripheral *)peripheral NS_AVAILABLE(NA, 6_0);
//服务更新时触发
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices NS_AVAILABLE(NA, 7_0);
//更新RSSI,过时用下一代替
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error NS_DEPRECATED(NA, NA, 5_0, 8_0);
// RSSI值
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error NS_AVAILABLE(NA, 8_0);
//发现服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error;
//发现嵌套服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(nullable NSError *)error;
//发现服务中的Characteristic
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error;
//更新Characteristic
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error;
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error;
CBPeripheralManagerDelegate
@required
//类似CBCentralManager
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
@optional
- (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary<NSString *, id> *)dict;
//开始广播
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(nullable NSError *)error;
//添加服务
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(nullable NSError *)error;
//订阅
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic;
//未订阅
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic;
//接收读取请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request;
//接收写入请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests;
//更新订阅
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral;
//创建CentralManager
- (void)startUpCentralManager {
_centralManager = [[CBCentralManager alloc] initWithDelegate: self queue: nil];
_peripherals = [NSMutableArray array]; //用于存放扫描的外设
}
创建CentralManager会调用Delegate方法:
#pragma mark - CBCentralManagerDelegate
// 实现: 确保支持BL和有效的Central设备
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBManagerStatePoweredOn:
//只有此状态下才可以进行扫描设备
[self scan];
break;
case CBManagerStateUnknown:
break;
case CBManagerStateResetting:
break;
case CBManagerStateUnsupported:
break;
case CBManagerStateUnauthorized:
break;
case CBManagerStatePoweredOff:
break;
default:
break;
}
}
- (void)scan {
[self.centralManager scanForPeripheralsWithServices: nil options: nil];
NSLog(@"Scanning started");
}
serviceUUIDs: 是一个CBUUID类型的数组,每个CBUUID代表一个service的UUID。使用这个参数可以限制扫描内容,如果设置为nil,则表示搜索全部外设。 options: 定义扫描 ,字典。
另外,在bluetooth-central后台模式下,option将被忽略。这个问题后面会详细讲解。
当启动扫描后,每次扫描到一个外设就会调用delegate方法:centralManager: didDiscoverPeripheral: advertisementData: RSSI:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(@"%@", peripheral.name);
//查找到设备并持有它,否则不会保存
[self.peripherals addObject: peripheral];
}
- peripheral : 扫描到的设备
- (void)startUpConnect {
[self.centralManager connectPeripheral: self.peripheral options: nil];
}
self.peripheral : 目标外设 options :字典,用来定制连接行为
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"链接设备名称为:%@", peripheral.name);
// 设置代理
peripheral.delegate = self;
//发现服务
[peripheral discoverServices: nil];
}
连接失败
//链接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"连接名称:%@ 失败,原因:%@", peripheral.name, error.localizedDescription);
}
另外,既然可以建立连接,那么肯定可以断开连接。
** 断开连接**
//断开链接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"与外设断开链接:%@, %@", peripheral.name, error.localizedDescription);
}
如果断开连接不是由cancelPeripheralConnection:发起的,error会给出详细信息。需要注意的是,当断开连接,所有的services, characteristics和Characteristic descriptions都是无效的。
// 设置代理
peripheral.delegate = self;
//发现服务
[peripheral discoverServices: nil];
可以通过CBUUID指定的服务。
CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
if (error) {
NSLog(@"发现服务错误:%@", error.localizedDescription);
return;
}
// 一个外设会发送多个服务
for (CBService *service in peripheral.services) {
//扫描每个service的Characteristic,通过Characteristic的UUID来指定查找那个Characteristic。
[peripheral discoverCharacteristics: nil forService: service];
}
}
注:一个外设包含多个Service,可以通过Service的UUID来区分。一个Service包含多个Characteristic,通过Characteristic的UUID来区分。
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if (error) {
NSLog(@"发现特征错误:%@", error.localizedDescription);
return;
}
for (CBCharacteristic *characteristic in service.characteristics) {
//直接读取Characteristic值,此时调用peripheral: didUpdateValueForCharacteristic: error:
[peripheral readValueForCharacteristic: characteristic];
//另一种情况,订阅特征。此时调用 peripheral: didUpdateNotificationStateForCharacteristic: error:
[peripheral setNotifyValue:YES forCharacteristic: characteristic];
}
}
//给指定的特征设置通知
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state : %@", error.localizedDescription);
}
if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:@""]]) {
return;
}
if (characteristic.isNotifying) {
NSLog(@"Notification began on :%@", characteristic);
} else {
NSLog(@"Notification stoped on : %@ Disconnecting", characteristic);
[self.centralManager cancelPeripheralConnection: peripheral];
}
}
//readValueCharacteristic时调用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
//打印出characteristic的UUID和值
//!注意,value的类型是NSData,具体开发时,会根据外设协议制定的方式去解析数据
NSLog(@"特性ID::%@ 值:%@",characteristic.UUID, characteristic.value);
}
- (void)writeValue {
NSData *data = [@"Test" dataUsingEncoding:NSUTF8StringEncoding];
[self.peripheral writeValue: data forCharacteristic: self.characteristic type: CBCharacteristicWriteWithResponse];
}
发送数据
typedef NS_ENUM(NSInteger, CBCharacteristicWriteType) {
CBCharacteristicWriteWithResponse = 0,
CBCharacteristicWriteWithoutResponse,
};
当type为CBCharacteristicWriteWithResponse时,调用delegate方法:peripheral: didWriteValueForCharacteristic: error:。
//创建PeripheralManager
- (void)startUpPeripheralManager {
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue: nil];
}
//唯一@required方法
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
switch (peripheral.state) {
case CBManagerStatePoweredOn:
// 创建服务和特征
[self buildService];
break;
case CBManagerStateUnknown :
break;
case CBManagerStateResetting:
break;
case CBManagerStateUnsupported:
break;
case CBManagerStateUnauthorized:
break;
case CBManagerStatePoweredOff:
break;
default:
break;
}
}
- (void)buildService {
//创建Characteristic
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID] properties:CBCharacteristicPropertyNotify value: nil permissions:CBAttributePermissionsReadable];
//创建服务
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID] primary:YES];
//将特征添加到服务中
transferService.characteristics = @[self.transferCharacteristic];
//将服务添加到PeripheralManager中,调用delegate方法: peripheralManager: didAddService: error:
[self.peripheralManager addService: transferService];
}
创建Characteristic
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
//特征值可以使用特征描述广播
CBCharacteristicPropertyBroadcast = 0x01,
//特征值可以被读取,通过readValueForCharacteristic: 来读取
CBCharacteristicPropertyRead = 0x02,
// 可以被读写,通过writeValue: forCharacteristic: type: 写入特征值,无应答标识写入成功。
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
// 可以被写入,有应答标识写入成功
CBCharacteristicPropertyWrite = 0x08,
//允许通知特征值,无应答表示接收
CBCharacteristicPropertyNotify = 0x10,
//允许通知特征值,有应答表示接收
CBCharacteristicPropertyIndicate = 0x20,
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
CBCharacteristicPropertyExtendedProperties = 0x80,
//只有信任的设备接收特征值通知
CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200
};
属性特征,可以组合使用。权限
typedef NS_OPTIONS(NSUInteger, CBAttributePermissions) {
// 可读
CBAttributePermissionsReadable = 0x01,
// 可写
CBAttributePermissionsWriteable = 0x02,
// 信任可读
CBAttributePermissionsReadEncryptionRequired = 0x04,
// 信任可写
CBAttributePermissionsWriteEncryptionRequired = 0x08
} NS_ENUM_AVAILABLE(NA, 6_0);
UUID : 唯一标识 isPrimary:YES:主服务;NO:次服务
添加特征到characteristics数据 调用delegate方法: peripheralManager: didAddService: error:
//开始广告
- (void)startAdvertise {
[self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]}];
}
//停止广告
- (void)stopAdvertise {
[self.peripheralManager stopAdvertising];
}
开始广告 advertisementData: 可选字典,包含想要广告的数据。两种类型:
调用delegate方法:peripheralManagerDidStartAdvertising: error:
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"当前Characteristic被订阅");
}
当characteristic配置为notify或indicate,将唤起次方法。
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"Central unsubscribed from characteristic");
}
当central移除特征的notification/indications时调用,取消订阅回调。
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
}
当接收到中心端的读写请求式,会触发这个方法。
在该方法被调用时,可以在方法内通过PeripheralManager调用:respondToRequest: withResult:来响应中心端的读写请求。
plist文件中,设置UIBackgroundModes:
CBCentralManagerScanOptionAllowDuplicatesKey扫描被忽略。
CBAdvertisementDataLocalNameKey被忽略,本地外设名不在广播。
#import "LQPeripheralManager.h"
#import <CoreBluetooth/CoreBluetooth.h>
static NSString *const ServiceUUID1 = @"FFF0";
static NSString *const notifyCharacteristicUUID = @"FFF1";
static NSString *const readwriteCharacteristicUUID = @"FFF2";
static NSString *const ServiceUUID2 = @"FFE0";
static NSString *const readCharacteristicUUID = @"FFE1";
static NSString *const LocalNameKey = @"Owenli";
@interface LQPeripheralManager ()<CBPeripheralManagerDelegate>
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) NSInteger index;
@end
@implementation LQPeripheralManager
+ (instancetype)shareInstance {
static LQPeripheralManager *peripheral;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
peripheral = [[self alloc] init];
});
return peripheral;
}
- (instancetype)init {
if (self = [super init]) {
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
_index = 0;
}
return self;
}
/**
* @Description 初始化化UUID和服务信息
*/
- (void)setup {
//characteristic字段描述
CBUUID *cbuuidCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];
/*
可以通知的Characteristic
properities : CBCharacteristicPropertyNotify
permissions : CBAttributePermissionsReadable
*/
CBMutableCharacteristic *notifyCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:notifyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
/*
可读写的characteristic
prperitise: CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead
permisssions : CBAttributePermissionsReadable | CBAttributePermisssionWriteable
*/
CBMutableCharacteristic * readwriteCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
// 设置descriptor
CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc] initWithType:cbuuidCharacteristicUserDescriptionStringUUID value:@"name"];
[readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];
/*
只读Characteristic
properties: CBCharacteristicPropertyRead
permisssions: CBAttributePermissionsReadable
*/
CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
// 第一个Service
CBMutableService *service1 = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];
[service1 setCharacteristics:@[notifyCharacteristic, readwriteCharacteristic]];
//第二个Service
CBMutableService *service2 = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];
[service2 setCharacteristics:@[readCharacteristic]];
//添加到periperal此时会调用,peripheralManager: didAddService: error:
[self.peripheralManager addService:service2];
[self.peripheralManager addService:service1];
}
#pragma mark - PeripherManagerDelegate
//检测蓝牙状态,
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
switch (peripheral.state) {
case CBManagerStatePoweredOn:
//初始化
[self setup];
break;
case CBManagerStatePoweredOff:
NSLog(@"powered off");
break;
default:
break;
}
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
if (error) {
NSLog(@"%@", error.localizedDescription);
return;
}
//添加服务后,开始广播
//自动回调: peripheralManagerDidStartAdvertising: error:
[self.peripheralManager startAdvertising:@{
CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1], [CBUUID UUIDWithString:ServiceUUID2]],
CBAdvertisementDataLocalNameKey : LocalNameKey
}];
}
//通知发送广播
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
if (error) {
NSLog(@"%@", error.localizedDescription);
}
NSLog(@"start advert");
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"subscribe data of : %@", characteristic.UUID);
//分配定时任务
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendData:) userInfo:characteristic repeats:YES];
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"unsubscrible: %@", characteristic.UUID);
[self.timer invalidate];
}
- (void)sendData:(NSTimer *)timer {
CBMutableCharacteristic *characteristic = timer.userInfo;
if ([self.peripheralManager updateValue:[[NSString stringWithFormat:@"send data : %ld", _index] dataUsingEncoding: NSUTF8StringEncoding] forCharacteristic:characteristic onSubscribedCentrals:nil]) {
NSLog(@"发送成功");
_index ++;
} else {
NSLog(@"发送数据错误");
}
}
//中心设备读取请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
NSLog(@"readRequest");
if (request.characteristic.properties & CBCharacteristicPropertyRead) {
NSData *data = [[NSString stringWithFormat:@"by characteristic request"] dataUsingEncoding:NSUTF8StringEncoding];
[request setValue:data];
//对请求作出成功响应
[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
} else {
[self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}
//写入请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
NSLog(@"writeRequest");
CBATTRequest *request = requests.firstObject;
if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
CBMutableCharacteristic *charateristic = (CBMutableCharacteristic *)request.characteristic;
charateristic.value = request.value;
NSLog(@"receive data :%@", [[NSString alloc] initWithData:charateristic.value encoding:NSUTF8StringEncoding]);
[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
} else {
[self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral {
NSLog(@"peripheralManagerIsReadyToUpdateSubscribers");
}
@end
使用LighBlue测试外设端
UUID方法:uuidgen。
Mac测试软件:LighBlue,可以用来测试iOS端外设。
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我