参考:https://www.jianshu.com/p/59e08e48ac20
抓包商品详情页,要模拟的是sign参数

先搭建基础框架代码:
package com.kdd.test;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.*;
public class jd_main extends AbstractJni {
private static final Log log = LogFactory.getLog(AbstractJni.class);
public static void main (String[] args) throws IOException {
jd_main RunLDQ =new jd_main();
RunLDQ.runJni();
RunLDQ.destroy();
}
private void destroy() throws IOException{
emulator.close();
System.out.println("destroy");
}
private static LibraryResolver createLibraryResolver() {
return new AndroidResolver(23);
}
private static AndroidEmulator createARMEmulator() {
return AndroidEmulatorBuilder
.for32Bit()
.build();
}
private final AndroidEmulator emulator;
private final VM vm;
private Module module;
private DvmClass aBitmapkitUtils;
//初始化
public jd_main(){
emulator = createARMEmulator();
final Memory memory = emulator.getMemory();
// 设置 sdk版本 23
memory.setLibraryResolver(createLibraryResolver());
//使用apk文件加载so的话,会自动处理签名方面的jni,具体可看AbstractJni,利用apk加载的好处,
vm = emulator.createDalvikVM(new File("F:\\frida_learn_app\\jd\\jd-9.2.2.apk"));
vm.setJni(this);
// 是否打印日志
vm.setVerbose(true);
}
public String runJni(){
//加载apk的so
DalvikModule dm = vm.loadLibrary("jdbitmapkit", false);
//调用jni
dm.callJNI_OnLoad(emulator);
module = dm.getModule();
return null;
}
运行有报错补代码

@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature) {
case "com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;": {
return vm.resolveClass("android/app/Activity", vm.resolveClass("android/content/ContextWrapper", vm.resolveClass("android/content/Context"))).newObject(null);
}
}
return super.getStaticObjectField(vm, dvmClass, signature);
}
报错补代码

@Override
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "android/app/Application->getPackageName()Ljava/lang/String;": {
String packageName = vm.getPackageName();
if (packageName != null) {
return new StringObject(vm, packageName);
}
}
}
throw new UnsupportedOperationException(signature);
}
报错补代码

@Override
public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
switch (signature) {
case "sun/security/pkcs/PKCS7-><init>([B)V": {
ByteArray array = varArg.getObjectArg(0);
return new StringObject(vm, new String(array.getValue()));
}
}
return super.newObject(vm, dvmClass, signature, varArg);
}
基础环境没报错后,调用签名函数
//加载so的哪个类
aBitmapkitUtils = vm.resolveClass("com/jingdong/common/utils/BitmapkitUtils");
//调用方法
DvmObject<?> strRc = aBitmapkitUtils.callStaticJniMethodObject(emulator,"getSignFromJni()(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
vm.addLocalObject(null),
vm.addLocalObject(new StringObject(vm,"wareBusiness")),
vm.addLocalObject(new StringObject(vm,"{\"abTest800\":true,\"avoidLive\":false,\"brand\":\"360\",\"cityId\":2144,\"darkModelEnum\":3,\"districtId\":24463,\"eventId\":\"Searchlist_Productid\",\"fromType\":0,\"isDesCbc\":true,\"latitude\":\"26.618816\",\"lego\":true,\"longitude\":\"106.644705\",\"model\":\"1605-A01\",\"ocrFlag\":false,\"pluginVersion\":90220,\"plusClickCount\":0,\"plusLandedFatigue\":0,\"provinceId\":\"24\",\"skuId\":\"10024083045618\",\"source_type\":\"search\",\"source_value\":\"鼠标垫小号\",\"townId\":51707,\"uAddrId\":\"0\"}")),
vm.addLocalObject(new StringObject(vm,"uuid")),
vm.addLocalObject(new StringObject(vm,"android")),
vm.addLocalObject(new StringObject(vm,"9.2.2")));
System.out.println(strRc.getValue());
//获取返回值
return (String) strRc.getValue();
后面有报错也是跟着报错补环境 最后成功运行出结果:

全部代码如下:
package com.kdd.test;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.Enumeration;
import com.github.unidbg.linux.android.dvm.api.*;
import com.github.unidbg.linux.android.dvm.api.ClassLoader;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.*;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.*;
public class jd_main extends AbstractJni {
private static final Log log = LogFactory.getLog(AbstractJni.class);
public static void main (String[] args) throws IOException {
jd_main RunLDQ =new jd_main();
RunLDQ.runJni(args);
RunLDQ.destroy();
}
private void destroy() throws IOException{
emulator.close();
System.out.println("destroy");
}
private static LibraryResolver createLibraryResolver() {
return new AndroidResolver(23);
}
private static AndroidEmulator createARMEmulator() {
return AndroidEmulatorBuilder
.for32Bit()
.build();
}
private final AndroidEmulator emulator;
private final VM vm;
private Module module;
private DvmClass aBitmapkitUtils;
//初始化
public jd_main(){
emulator = createARMEmulator();
final Memory memory = emulator.getMemory();
// 设置 sdk版本 23
memory.setLibraryResolver(createLibraryResolver());
//使用apk文件加载so的话,会自动处理签名方面的jni,具体可看AbstractJni,利用apk加载的好处,
vm = emulator.createDalvikVM(new File("F:\\frida_learn_app\\jd\\jd-9.2.2.apk"));
vm.setJni(this);
// 是否打印日志
// vm.setVerbose(true);
}
public String runJni(String[] args){
//加载apk的so
DalvikModule dm = vm.loadLibrary("jdbitmapkit", false);
//调用jni
dm.callJNI_OnLoad(emulator);
module = dm.getModule();
//加载so的哪个类
aBitmapkitUtils = vm.resolveClass("com/jingdong/common/utils/BitmapkitUtils");
//调用方法
DvmObject<?> strRc = aBitmapkitUtils.callStaticJniMethodObject(emulator,"getSignFromJni()(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
vm.addLocalObject(null),
vm.addLocalObject(new StringObject(vm,"wareBusiness")),
vm.addLocalObject(new StringObject(vm,"{\"abTest800\":true,\"avoidLive\":false,\"brand\":\"360\",\"cityId\":2144,\"darkModelEnum\":3,\"districtId\":24463,\"eventId\":\"Searchlist_Productid\",\"fromType\":0,\"isDesCbc\":true,\"latitude\":\"26.618816\",\"lego\":true,\"longitude\":\"106.644705\",\"model\":\"1605-A01\",\"ocrFlag\":false,\"pluginVersion\":90220,\"plusClickCount\":0,\"plusLandedFatigue\":0,\"provinceId\":\"24\",\"skuId\":\"10024083045618\",\"source_type\":\"search\",\"source_value\":\"鼠标垫小号\",\"townId\":51707,\"uAddrId\":\"0\"}")),
vm.addLocalObject(new StringObject(vm,"uuid")),
vm.addLocalObject(new StringObject(vm,"android")),
vm.addLocalObject(new StringObject(vm,"9.2.2")));
System.out.println(strRc.getValue());
//获取返回值
return (String) strRc.getValue();
}
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature) {
case "com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;": {
return vm.resolveClass("android/app/Activity", vm.resolveClass("android/content/ContextWrapper", vm.resolveClass("android/content/Context"))).newObject(null);
}
}
return super.getStaticObjectField(vm, dvmClass, signature);
}
@Override
public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
switch (signature) {
case "sun/security/pkcs/PKCS7-><init>([B)V": {
ByteArray array = varArg.getObjectArg(0);
return new StringObject(vm, new String(array.getValue()));
}
}
return super.newObject(vm, dvmClass, signature, varArg);
}
@Override
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "android/app/Application->getPackageName()Ljava/lang/String;": {
String packageName = vm.getPackageName();
if (packageName != null) {
return new StringObject(vm, packageName);
}
}
}
throw new UnsupportedOperationException(signature);
}
@Override
public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "java/lang/StringBuffer-><init>()V":{
return vm.resolveClass("java/lang/StringBuffer").newObject(new StringBuffer());
}
case "java/lang/Integer-><init>(I)V" :{
return vm.resolveClass("java/lang/Integer").newObject(new Integer(vaList.getIntArg(0)));
}
}
throw new UnsupportedOperationException(signature);
}
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature) {
case "android/app/Application->getAssets()Landroid/content/res/AssetManager;":
return new AssetManager(vm, signature);
case "android/app/Application->getClassLoader()Ljava/lang/ClassLoader;":
return new ClassLoader(vm, signature);
case "android/app/Application->getContentResolver()Landroid/content/ContentResolver;":
return vm.resolveClass("android/content/ContentResolver").newObject(signature);
case "java/util/ArrayList->get(I)Ljava/lang/Object;": {
int index = vaList.getIntArg(0);
ArrayListObject arrayList = (ArrayListObject) dvmObject;
return arrayList.getValue().get(index);
}
case "android/app/Application->getSystemService(Ljava/lang/String;)Ljava/lang/Object;": {
StringObject serviceName = vaList.getObjectArg(0);
assert serviceName != null;
return new SystemService(vm, serviceName.getValue());
}
case "java/lang/String->toString()Ljava/lang/String;":
return dvmObject;
case "java/lang/Class->getName()Ljava/lang/String;":
return new StringObject(vm, ((DvmClass) dvmObject).getName());
case "android/view/accessibility/AccessibilityManager->getEnabledAccessibilityServiceList(I)Ljava/util/List;":
return new ArrayListObject(vm, Collections.<DvmObject<?>>emptyList());
case "java/util/Enumeration->nextElement()Ljava/lang/Object;":
return ((Enumeration) dvmObject).nextElement();
case "java/util/Locale->getLanguage()Ljava/lang/String;":
Locale locale = (Locale) dvmObject.getValue();
return new StringObject(vm, locale.getLanguage());
case "java/util/Locale->getCountry()Ljava/lang/String;":
locale = (Locale) dvmObject.getValue();
return new StringObject(vm, locale.getCountry());
case "android/os/IServiceManager->getService(Ljava/lang/String;)Landroid/os/IBinder;": {
ServiceManager serviceManager = (ServiceManager) dvmObject;
StringObject serviceName = vaList.getObjectArg(0);
assert serviceName != null;
return serviceManager.getService(vm, serviceName.getValue());
}
case "java/io/File->getAbsolutePath()Ljava/lang/String;":
File file = (File) dvmObject.getValue();
return new StringObject(vm, file.getAbsolutePath());
case "android/app/Application->getPackageManager()Landroid/content/pm/PackageManager;":
case "android/content/ContextWrapper->getPackageManager()Landroid/content/pm/PackageManager;":
case "android/content/Context->getPackageManager()Landroid/content/pm/PackageManager;":
DvmClass clazz = vm.resolveClass("android/content/pm/PackageManager");
return clazz.newObject(signature);
case "android/content/pm/PackageManager->getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;": {
StringObject packageName = vaList.getObjectArg(0);
assert packageName != null;
int flags = vaList.getIntArg(1);
if (log.isDebugEnabled()) {
log.debug("getPackageInfo packageName=" + packageName.getValue() + ", flags=0x" + Integer.toHexString(flags));
}
return new PackageInfo(vm, packageName.getValue(), flags);
}
case "android/app/Application->getPackageName()Ljava/lang/String;":
case "android/content/ContextWrapper->getPackageName()Ljava/lang/String;":
case "android/content/Context->getPackageName()Ljava/lang/String;": {
String packageName = vm.getPackageName();
if (packageName != null) {
return new StringObject(vm, packageName);
}
break;
}
case "android/content/pm/Signature->toByteArray()[B":
if (dvmObject instanceof Signature) {
Signature sig = (Signature) dvmObject;
return new ByteArray(vm, sig.toByteArray());
}
break;
case "android/content/pm/Signature->toCharsString()Ljava/lang/String;":
if (dvmObject instanceof Signature) {
Signature sig = (Signature) dvmObject;
return new StringObject(vm, sig.toCharsString());
}
break;
case "java/lang/String->getBytes()[B": {
String str = (String) dvmObject.getValue();
return new ByteArray(vm, str.getBytes());
}
case "java/lang/String->getBytes(Ljava/lang/String;)[B":
String str = (String) dvmObject.getValue();
StringObject charsetName = vaList.getObjectArg(0);
assert charsetName != null;
try {
return new ByteArray(vm, str.getBytes(charsetName.getValue()));
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
case "java/lang/Integer->toString()Ljava/lang/String;":{
Integer iUse = (Integer)dvmObject.getValue();
return new StringObject(vm, Integer.toString(iUse));
}
case "java/lang/StringBuffer->toString()Ljava/lang/String;":{
StringBuffer str1 = (StringBuffer) dvmObject.getValue();
return new StringObject(vm,str1.toString());
}
case "java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;": {
StringBuffer str1 = (StringBuffer) dvmObject.getValue();
StringObject serviceName = vaList.getObjectArg(0);
assert serviceName != null;
return vm.resolveClass("java/lang/StringBuffer").newObject(str1.append(serviceName.getValue()));
}
case "java/security/cert/CertificateFactory->generateCertificate(Ljava/io/InputStream;)Ljava/security/cert/Certificate;":
CertificateFactory factory = (CertificateFactory) dvmObject.getValue();
DvmObject<?> stream = vaList.getObjectArg(0);
assert stream != null;
InputStream inputStream = (InputStream) stream.getValue();
try {
return vm.resolveClass("java/security/cert/Certificate").newObject(factory.generateCertificate(inputStream));
} catch (CertificateException e) {
throw new IllegalStateException(e);
}
case "java/security/cert/Certificate->getEncoded()[B": {
Certificate certificate = (Certificate) dvmObject.getValue();
try {
return new ByteArray(vm, certificate.getEncoded());
} catch (CertificateEncodingException e) {
throw new IllegalStateException(e);
}
}
case "java/security/MessageDigest->digest([B)[B": {
MessageDigest messageDigest = (MessageDigest) dvmObject.getValue();
ByteArray array = vaList.getObjectArg(0);
assert array != null;
return new ByteArray(vm, messageDigest.digest(array.getValue()));
}
case "java/util/ArrayList->remove(I)Ljava/lang/Object;": {
int index = vaList.getIntArg(0);
ArrayListObject list = (ArrayListObject) dvmObject;
return list.getValue().remove(index);
}
case "java/util/List->get(I)Ljava/lang/Object;":
List<?> list = (List<?>) dvmObject.getValue();
return (DvmObject<?>) list.get(vaList.getIntArg(0));
case "java/util/Map->entrySet()Ljava/util/Set;":
Map<?, ?> map = (Map<?, ?>) dvmObject.getValue();
return vm.resolveClass("java/util/Set").newObject(map.entrySet());
case "java/util/Set->iterator()Ljava/util/Iterator;":
Set<?> set = (Set<?>) dvmObject.getValue();
return vm.resolveClass("java/util/Iterator").newObject(set.iterator());
}
throw new UnsupportedOperationException(signature);
}
}
IDEA 找到 File → Project Structure … 然后选择 Artifacts, 点加号 Add 如图配置,勾上 Include tests

点击ok后 Build → Build Artifacts进行编译 编译成功后会生成很多jar文件

在控制台测试运行下 java -jar unidbg-master.jar

运行出了结果,证明打包的没问题

# coding:utf-8
import requests, urllib, subprocess
import chardet, jpype,os
headers = {
"Host": "api.m.jd.com",
"charset": "UTF-8",
"cache-control": "no-cache",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"user-agent": "okhttp/3.12.1"
}
cookies = {
}
url = "https://api.m.jd.com/client.action"
params = {
"functionId": "wareBusiness",
"clientVersion": "9.2.2",
"build": "85371",
"client": "android",
"d_brand": "360",
"d_model": "1605-A01",
"osVersion": "6.0.1",
"screen": "1920*1080",
"partner": "ks012",
"aid": "xxx",
"oaid": "",
"eid": "xxx",
"sdkVersion": "23",
"lang": "zh_CN",
"uuid": "xxx",
"area": "24_2144_2149_21104",
"networkType": "wifi",
"wifiBssid": "xxx",
# "st": "1665562015795",
# "sign": "45a7dc3f547be113a6a4dfa942e190c6",
# "sv": "111"
}
body = '''{"abTest800":true,"avoidLive":false,"brand":"360","cityId":2144,"darkModelEnum":3,"districtId":24463,"eventId":"Searchlist_Productid","fromType":0,"isDesCbc":true,"latitude":"","lego":true,"longitude":"","model":"1605-A01","ocrFlag":false,"pluginVersion":90220,"plusClickCount":0,"plusLandedFatigue":0,"provinceId":"24","skuId":"10024083045618","source_type":"search","source_value":"鼠标垫小号","townId":51707,"uAddrId":"0"}'''
data = {
"lmt": "0",
"body": body,
"": ""
}
jvmPath=jpype.getDefaultJVMPath()
d='unidbg_master_jar2/unidbg-master.jar'#对应jar地址
jpype.startJVM(jvmPath,"-ea","-Djava.class.path="+d+"")
JDClass=jpype.JClass("com.kdd.test.runliudq") //类目
jd=JDClass()
signature=jd.runJni(["wareBusiness", body, "uuid", "android", "9.2.2"])
url = url + "?" + urllib.parse.urlencode(params) + "&" + str(signature)
print(url)
response = requests.post(url, headers=headers, cookies=cookies, data=data)
print(response.text)
print(response)
jpype.shutdownJVM()
成功跑出结果
这个案例网上有很多,适合入门哈哈
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).
我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame
我有一个应用需要发送用户事件邀请。当用户邀请friend(用户)参加事件时,如果尚不存在将用户连接到该事件的新记录,则会创建该记录。我的模型由用户、事件和events_user组成。classEventdefinvite(user_id,*args)user_id.eachdo|u|e=EventsUser.find_or_create_by_event_id_and_user_id(self.id,u)e.save!endendend用法Event.first.invite([1,2,3])我不认为以上是完成我的任务的最有效方法。我设想了一种方法,例如Model.find_or_cr
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
在许多ruby类之间共享记录器实例的最佳(正确)方法是什么?现在我只是将记录器创建为全局$logger=Logger.new变量,但我觉得有更好的方法可以在不使用全局变量的情况下执行此操作。如果我有以下内容:moduleFooclassAclassBclassC...classZend在所有类之间共享记录器实例的最佳方式是什么?我是以某种方式在Foo模块中声明/创建记录器还是只是使用全局$logger没问题? 最佳答案 在模块中添加常量:moduleFooLogger=Logger.newclassAclassBclassC..