背景
我一直在使用 C 预处理器来管理和“编译”具有多个文件和构建目标的半大型 javascript 项目。这允许从 javascript 中完全访问 C 预处理器指令,如 #include、#define、#ifdef 等。这是一个示例构建脚本,因此您可以测试示例代码:
#!/bin/bash
export OPTS="-DDEBUG_MODE=1 -Isrc"
for FILE in `find src/ | egrep '\.js?$'`
do
echo "Processing $FILE"
cat $FILE \
| sed 's/^\s*\/\/#/#/' \
| cpp $OPTS \
| sed 's/^[#:<].*// ; /^$/d' \
> build/`basename $FILE`;
done
创建一个src和一个build目录,并将.js文件放在src中。
便捷宏
最初,我只是想要 #include 和一些 #ifdef 的预处理器,但我开始思考,拥有它不是很好吗还有一些方便的宏?实验随之而来。
#define EACH(o,k) for (var k in o) if (o.hasOwnProperty(k))
太棒了,现在我可以写这样的东西了:
EACH (location, prop) {
console.log(prop + " : " location[prop]);
}
它将扩展为:
for (var prop in location) if (location.hasOwnProperty(prop)) {
console.log(prop + " : " location[prop]);
}
foreach 怎么样?
#define FOREACH(o,k,v) var k,v; for(k in o) if (v=o[k], o.hasOwnProperty(k))
// ...
FOREACH (location, prop, val) { console.log(prop + " : " + val) }
请注意我们是如何将 v=o[k] 隐藏在 if 条件中的,这样它就不会干扰应该跟在这个宏调用之后的大括号。
类似类的 OOP
让我们从一个 NAMESPACE 宏和一个晦涩但有用的 js 模式开始...
#define NAMESPACE(ns) var ns = this.ns = new function()
new function(){ ... } 做了一些巧妙的事情。它调用一个匿名函数作为构造函数,所以它不需要在末尾额外的 () 来调用它,并且在它内部 this 指的是被创建的对象由构造函数,换句话说,命名空间本身。这也允许我们在命名空间中嵌套命名空间。
这是我的全套类类 OOP 宏:
#define NAMESPACE(ns) var ns=this.ns=new function()
#define CLASS(c) var c=this;new function()
#define CTOR(c) (c=c.c=this.constructor=$$ctor).prototype=this;\
function $$ctor
#define PUBLIC(fn) this.fn=fn;function fn
#define PRIVATE(fn) function fn
#define STATIC(fn) $$ctor.fn=fn;function fn
如您所见,这些宏在 Variable Object(为方便起见)和 this(出于必要)中定义了许多内容。下面是一些示例代码:
NAMESPACE (Store) {
CLASS (Cashier) {
var nextId = 1000;
this.fullName = "floater";
CTOR (Cashier) (fullName) {
if (fullName) this.fullName = fullName;
this.id = ++nextId;
this.transactions = 0;
}
PUBLIC (sell) (item, customer) {
this.transactions += 1;
customer.inventory.push(item);
}
STATIC (hire) (count) {
var newCashiers = [];
for (var i=count; i--;) {
newCashiers.push(new Cashier());
}
return newCashiers;
}
}
CLASS (Customer) {
CTOR (Customer) (name) {
this.name = name;
this.inventory = [];
this.transactions = 0;
}
PUBLIC (buy) (item, cashier) {
cashier.sell(this, item);
}
}
}
EXTENDS 怎么样?
所以这让我想到了一个问题......我们如何将 EXTENDS 实现为一个宏来包装通常的“克隆原型(prototype),复制构造函数属性”js 原型(prototype)继承?除了要求 EXTENDS 出现在类定义之后 之外,我还没有找到其他方法来做到这一点,这很愚蠢。这个实验需要 EXTENDS 否则就没用了。随意更改其他宏,只要它们提供相同的结果即可。
编辑 - 这些可能对 EXTENDS 派上用场;为了完整起见,在此处列出它们。
#define EACH(o,k) for(var k in o)if(o.hasOwnProperty(k))
#define MERGE(d,s) EACH(s,$$i)d[$$i]=s[$$i]
#define CLONE(o) (function(){$$C.prototype=o;return new $$C;function $$C(){}}())
在此先感谢您提供的任何帮助、建议或热烈讨论。 :)
最佳答案
我想我刚刚完成了自己的挑战。我已经为正在声明的类的父类(super class)的 CLASS 声明宏添加了第二个(可选)参数。
我最初的实现在构造函数周围创建了很多内联垃圾,所以我决定将一些方便的函数包装在宏帮助对象中以避免冗余。
这是我的类 OOP 宏的当前化身:
// class-like oo
#ifndef BASE
#define BASE $$_
#endif
#define COLLAPSE(code) code
#define NAMESPACE(ns) var ns=BASE._ns(this).ns=new function()
#define CLASS(c,__ARGS...) var c=[BASE._class(this),[__ARGS][0]]; \
new function()
#define CTOR(c) BASE._extend($$_##c,c[1],this); \
c=c[0].c=$$_##c; function $$_##c
#define PUBLIC(fn) BASE._public(this).fn=fn;function fn
#define PRIVATE(fn) function fn
#define STATIC(fn) BASE._static(this).fn=fn;function fn
// macro helper object
COLLAPSE(var BASE=new function(){
function Clone(){};
function clone (obj) {
Clone.prototype=obj; return new Clone;
};
function merge (sub, sup) {
for (var p in sup) if (sup.hasOwnProperty(p)) sub[p]=sup[p];
};
this._extend = function (sub, sup, decl) {
if (sup) {
merge(sub, sup);
sub.prototype=clone(sup.prototype);
sub.prototype.constructor=sub;
};
if (decl) {
merge(sub.prototype, decl);
decl._static=sub;
decl._public=sub.prototype;
};
};
this._static=this._ns=this._class=function (obj) {
return (obj._static || obj);
};
this._public=function (obj) {
return (obj._public || obj);
};
})
...这是一个测试命名空间...
//#include "macros.js"
NAMESPACE (Store) {
CLASS (Cashier) {
var nextId = 1000;
this.fullName = "floater";
CTOR (Cashier) (fullName) {
if (fullName) this.fullName = fullName;
this.id = ++nextId;
this.transactions = 0;
}
PUBLIC (sell) (item, customer) {
this.transactions += 1;
customer.inventory.push(item);
}
STATIC (hire) (count) {
var newCashiers = [];
for (var i=count; i--;) {
newCashiers.push(new Cashier());
}
return newCashiers;
}
}
// Customer extends Cashier, just so we can test inheritance
CLASS (Customer, Cashier) {
CTOR (Customer) (name) {
this.name = name;
this.inventory = [];
this.transactions = 0;
}
PUBLIC (buy) (item, cashier) {
cashier.sell(this, item);
}
CLASS (Cart) {
CTOR (Cart) (customer) {
this.customer = customer;
this.items = [];
}
}
}
}
...这是输出...
var $$_=new function(){ function Clone(){}; function clone (obj) { Clone.prototype=obj; return new Clone; }; function merge (sub, sup) { for (var p in sup) if (sup.hasOwnProperty(p)) sub[p]=sup[p]; }; this._extend = function (sub, sup, decl) { if (sup) { merge(sub, sup); sub.prototype=clone(sup.prototype); sub.prototype.constructor=sub; }; if (decl) { merge(sub.prototype, decl); decl._static=sub; decl._public=sub.prototype; }; }; this._static=this._ns=this._class=function (obj) { return (obj._static || obj); }; this._public=function (obj) { return (obj._public || obj); }; }
var Store=$$_._ns(this).Store=new function() {
var Cashier=[$$_._class(this),[][0]]; new function() {
var nextId = 1000;
this.fullName = "floater";
$$_._extend($$_Cashier,Cashier[1],this); Cashier=Cashier[0].Cashier=$$_Cashier; function $$_Cashier (fullName) {
if (fullName) this.fullName = fullName;
this.id = ++nextId;
this.transactions = 0;
}
$$_._public(this).sell=sell;function sell (item, customer) {
this.transactions += 1;
customer.inventory.push(item);
}
$$_._static(this).hire=hire;function hire (count) {
var newCashiers = [];
for (var i=count; i--;) {
newCashiers.push(new Cashier());
}
return newCashiers;
}
}
var Customer=[$$_._class(this),[Cashier][0]]; new function() {
$$_._extend($$_Customer,Customer[1],this); Customer=Customer[0].Customer=$$_Customer; function $$_Customer (name) {
this.name = name;
this.inventory = [];
this.transactions = 0;
}
$$_._public(this).buy=buy;function buy (item, cashier) {
cashier.sell(this, item);
}
var Cart=[$$_._class(this),[][0]]; new function() {
$$_._extend($$_Cart,Cart[1],this); Cart=Cart[0].Cart=$$_Cart; function $$_Cart (customer) {
this.customer = customer;
this.items = [];
}
}
}
}
继承、内部类和嵌套命名空间似乎工作正常。你怎么看,这是一种在 js 中类类 OOP 和代码重用的有用方法吗?如果我遗漏了什么,请告诉我。
关于javascript - 扩展挑战 : preprocessor function macros and class-like oop,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3329094/
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。
我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion
我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的
我想编写一个ruby脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"
这个问题有两个部分。在RubyProgrammingLanguage一书中,有一个使用模块扩展字符串对象和类的示例(第8.1.1节)。第一个问题。为什么如果您使用新方法扩展类,然后创建该类的对象/实例,则无法访问该方法?irb(main):001:0>moduleGreeter;defciao;"Ciao!";end;end=>nilirb(main):002:0>String.extend(Greeter)=>Stringirb(main):003:0>String.ciao=>"Ciao!"irb(main):004:0>x="foobar"=>"foobar"irb(main):
假设我们有A、B、C类。Adefself.inherited(sub)#metaprogramminggoeshere#takeclassthathasjustinheritedclassA#andforfooclassesinjectprepare_foo()as#firstlineofmethodthenrunrestofthecodeenddefprepare_foo#=>prepare_foo()neededhere#somecodeendendBprepare_foo()neededhere#somecodeendend如您所见,我正在尝试将foo_prepare()调用注入
我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan
显然在Test::Unit中没有assert_false。您将如何通过扩展断言并添加文件config/initializers/assertions_helper.rb来添加它?这是最好的方法吗?我不想修改test/unit/assertions.rb。顺便说一句,我不认为这是多余的。我使用的是assert_equalfalse,something_to_evaluate。这种方法的问题是很容易意外使用assertfalse,something_to_evaluate。这将始终失败,不会引发错误或警告,并且会在测试中引入错误。 最佳答案
这个问题在这里已经有了答案:Unabletoinstallgem-Failedtobuildgemnativeextension-cannotloadsuchfile--mkmf(LoadError)(17个答案)关闭9年前。嘿,我正在尝试在一台新的ubuntu机器上安装rails。我安装了ruby和rvm,但出现“无法构建gemnative扩展”错误。这是什么意思?$sudogeminstallrails-v3.2.9(没有sudo表示我没有权限)然后它会输出很多“获取”命令,最终会出现这个错误:Buildingnativeextensions.Thiscouldtakeawhi