invfs 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +24 -0
- data/README.md +191 -0
- data/Rakefile +212 -0
- data/gemstub.rb +11 -0
- data/lib/invfs.rb +366 -0
- data/lib/invfs/zip.rb +75 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2909cffa456fdd3f5b4fc6efdbc4cfaa07f1faf30db565da868af67233bf9635
|
4
|
+
data.tar.gz: f5db8163c2fb5533c7fb72deab583d66c3b54d8bb29058c426d68733d2020821
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e4126c3039e12664d9564ff9877dafc8694777f91d286f3fe800667ed7df2ce8379d2d5488bee2c966bf7b7e8ff600368e6bab47a75ec98ee73faa13a3e1cfba
|
7
|
+
data.tar.gz: 8b05d884e52a39537978f395e886f975f87a946e9ed38dcd04a145e9528c6408c2edf4582aab535d8a9f39a00a59ba1832c0462aab86bdcaa2671bddca9c0e56
|
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2017, dearblue. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or
|
4
|
+
without modification, are permitted provided that the following
|
5
|
+
conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in
|
11
|
+
the documentation and/or other materials provided with the
|
12
|
+
distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
15
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
16
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
17
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
18
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
19
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
20
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
22
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
# invfs - customization "require" in VFS support
|
2
|
+
|
3
|
+
ruby の ``require`` に仮想ファイルシステム (VFS; Virtual Filesystem) 対応機能を追加します。
|
4
|
+
|
5
|
+
* package name: invfs <https://github.com/dearblue/ruby-invfs>
|
6
|
+
* version: 0.2
|
7
|
+
* production quality: CONCEPT, EXPERIMENTAL, UNSTABLE
|
8
|
+
* license: BSD-2-clause License
|
9
|
+
* author: dearblue <mailto:dearblue@users.noreply.github.com>
|
10
|
+
* report issue to: <https://github.com/dearblue/ruby-invfs/issues>
|
11
|
+
* dependency ruby: ruby-2.2+
|
12
|
+
* dependency ruby gems:
|
13
|
+
* rubyzip-1.2.0 (BSD-2-Clause)
|
14
|
+
* https://github.com/rubyzip/rubyzip
|
15
|
+
* https://rubygems.org/gems/rubyzip
|
16
|
+
* dependency library: (none)
|
17
|
+
* bundled external C library: (none)
|
18
|
+
|
19
|
+
|
20
|
+
## How to install (インストールの仕方)
|
21
|
+
|
22
|
+
```shell
|
23
|
+
# gem install invfs
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
## How to use (使い方)
|
28
|
+
|
29
|
+
この例では ``mybox.zip`` という Zip 書庫ファイルの中に ``mybox/core.rb``
|
30
|
+
という Ruby スクリプトファイルが存在しているものとして解説を進めていきます。
|
31
|
+
|
32
|
+
mybox.zip の中身:
|
33
|
+
|
34
|
+
```text
|
35
|
+
$ unzip -v mybox.zip
|
36
|
+
Archive: mybox.zip
|
37
|
+
Length Method Size Cmpr Date Time CRC-32 Name
|
38
|
+
-------- ------ ------- ---- ---------- ----- -------- ----
|
39
|
+
69 Defl:X 60 13% 01-26-2017 00:24 8d7bd341 mybox/core.rb
|
40
|
+
-------- ------- --- -------
|
41
|
+
69 60 13% 1 file
|
42
|
+
```
|
43
|
+
|
44
|
+
mybox/core.rb の中身:
|
45
|
+
|
46
|
+
```text
|
47
|
+
$ unzip -p mybox.zip mybox/core.rb
|
48
|
+
module MyBox
|
49
|
+
def MyBox.sayhello!
|
50
|
+
puts "Hello, Ruby!"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
Ruby スクリプトで実際に利用する場合は次のようにします:
|
56
|
+
|
57
|
+
```ruby:ruby
|
58
|
+
require "invfs/zip" # (1)
|
59
|
+
|
60
|
+
$: << InVFS.zip("mybox.zip") # (2)
|
61
|
+
|
62
|
+
require "mybox/core" # (3)
|
63
|
+
|
64
|
+
MyBox.sayhello!
|
65
|
+
# => Hello, Ruby!
|
66
|
+
```
|
67
|
+
|
68
|
+
1. ``require "invfs"`` すると、それ以降で VFS を探す機能が利用できるようになります。
|
69
|
+
|
70
|
+
``require "invfs/zip"`` すると、rubyzip を用いて zip 書庫ファイルから読み込めるようになります。
|
71
|
+
|
72
|
+
2. ``$:`` に VFS としての機能を持った任意のオブジェクトを追加します。
|
73
|
+
|
74
|
+
``$: << InVFS.zip("mybox.zip")`` の部分です。
|
75
|
+
|
76
|
+
3. ``require`` で任意のライブラリを指定します。
|
77
|
+
|
78
|
+
VFS 内から同じ VFS のファイルを指定したい場合、``require_relative`` も利用できます。
|
79
|
+
|
80
|
+
|
81
|
+
## VFS オブジェクトについて
|
82
|
+
|
83
|
+
VFS オブジェクトは ``$:`` に追加する、利用者定義のロードパスと見せかけるオブジェクトです。
|
84
|
+
|
85
|
+
このオブジェクトは、以下のメソッドが要求されます。
|
86
|
+
|
87
|
+
* ``.to_path() -> string``
|
88
|
+
* ``.file?(path) -> true or false``
|
89
|
+
* ``.size(path) -> integer``
|
90
|
+
* ``.read(path) -> string as binary``
|
91
|
+
|
92
|
+
実際にどのように定義すればいいのかについては、[lib/invfs/zip.rb](lib/invfs/zip.rb) あるいは [lib/invfs.rb](lib.invfs.rb) で定義されている ``InVFS::UnionFS``、``InVFS::StringMapFS`` を参考にして下さい。
|
93
|
+
|
94
|
+
### ``.to_path() -> string``
|
95
|
+
|
96
|
+
ロードパスに変換するためのメソッドです。
|
97
|
+
|
98
|
+
***文字列を返してください。***
|
99
|
+
|
100
|
+
ruby が提供する本来の require の内部や File.join が ``to_path`` して、
|
101
|
+
文字列以外を文字列に変換するために呼びます
|
102
|
+
(``file.c:rb_get_path_check_to_string``)。
|
103
|
+
|
104
|
+
### ``.file?(path) -> true or false``
|
105
|
+
|
106
|
+
VFS 内部にファイルが存在するかを確認するためのメソッドです。
|
107
|
+
|
108
|
+
***真偽値を返して下さい。***
|
109
|
+
|
110
|
+
path に関して発生した例外は出来る限り捕捉して、false を返すべきです。
|
111
|
+
|
112
|
+
### ``.size(path) -> integer``
|
113
|
+
|
114
|
+
ファイルサイズを取得するためのメソッドです。
|
115
|
+
|
116
|
+
***0 以上の整数値を返して下さい。***
|
117
|
+
|
118
|
+
### ``.read(path) -> string as binary``
|
119
|
+
|
120
|
+
VFS からファイルを読み込むためのメソッドです。
|
121
|
+
|
122
|
+
***文字列、または nil を返して下さい。***
|
123
|
+
|
124
|
+
|
125
|
+
## Environment Variables (環境変数について)
|
126
|
+
|
127
|
+
* ``RUBY_REQUIRE_INVFS_MAX_LOADSIZE`` :: 読み込みファイルの最大ファイルサイズの指定
|
128
|
+
|
129
|
+
VFS 内における読み込み対象ファイルの最大ファイルサイズを指定することが出来ます。
|
130
|
+
|
131
|
+
数値に続けて接頭辞を付けることが出来ます。以下は 64 MiB とした時の指定です。
|
132
|
+
|
133
|
+
```shell
|
134
|
+
$ export RUBY_REQUIRE_INVFS_MAX_LOADSIZE=64mib
|
135
|
+
```
|
136
|
+
|
137
|
+
最小値は 256 KiB、最大値は 64 MiB、既定値は 2 MiB となっています。
|
138
|
+
|
139
|
+
|
140
|
+
## 課題
|
141
|
+
|
142
|
+
* セキュリティレベル? なにそれおいしいの?
|
143
|
+
|
144
|
+
* マルチスレッド? なにそれおいしいの?
|
145
|
+
|
146
|
+
複数のスレッドで VFS 内のファイルを指定した場合は、``$LOADED_FEATURES``
|
147
|
+
に不整合が起きる可能性がある。
|
148
|
+
|
149
|
+
* ``require`` / ``require_relative`` の探索速度がとても遅い。
|
150
|
+
|
151
|
+
``require "invfs"`` しただけで、3倍以上遅くなる。
|
152
|
+
|
153
|
+
VFS 内の ``.so`` ファイルの読み込みにいたっては、10倍以上遅くなる。
|
154
|
+
|
155
|
+
* VFS オブジェクトのシグネチャが変動すると不整合が起きる。
|
156
|
+
|
157
|
+
``$LOAD_PATH`` に追加された VFS オブジェクトのシグネチャ (``.to_path``)
|
158
|
+
が変動すると、同じライブラリを ``require`` した時に ``$LOADED_FEATURES``
|
159
|
+
と一致しなくなるため、再読み込みしてしまう。
|
160
|
+
|
161
|
+
シグネチャの解決に ``.object_id`` などを用いるべき?
|
162
|
+
|
163
|
+
=> ``require_relative`` したファイルの VFS が決定しやすくなるが、ファイルパス名
|
164
|
+
(VFS 名) が暗号みたくなって特定しづらそう。
|
165
|
+
|
166
|
+
* ``require`` した後に ``$LOAD_PATH`` から VFS オブジェクトを除去すると ``require_relative`` で不具合が起きる。
|
167
|
+
|
168
|
+
* パス解決の正確性を向上させるか?
|
169
|
+
|
170
|
+
ライブラリへの指定が相対パスで、シンボリックリンクが絡んでくると不正確になる。
|
171
|
+
|
172
|
+
* ``require`` されたファイルが VFS から取り出されたのかを確認する処理は文字列の比較であるため不正確。
|
173
|
+
|
174
|
+
これは直接 ``require_relative`` する時の VFS を確認する処理に関係してくる。
|
175
|
+
|
176
|
+
``caller_locations`` で VFS の直接確認が出来るようにするためには ruby の
|
177
|
+
C コードに手を入れなきゃなんないし諦める。
|
178
|
+
|
179
|
+
* ``$:`` にファイルを指定した場合、InVFS の VFS mapper が自動で VFS に置き換えるようにするか?
|
180
|
+
|
181
|
+
実現させると、以下のことが可能となる:
|
182
|
+
|
183
|
+
```shell
|
184
|
+
$ ruby -I mybox.zip -r invfs/zip -r mybox/core -e '......'
|
185
|
+
```
|
186
|
+
|
187
|
+
これは、以下と同等である:
|
188
|
+
|
189
|
+
```shell
|
190
|
+
$ ruby -r invfs/zip -e '$: << InVFS.zip("mybox.zip"); require "mybox/core"; ......'
|
191
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
|
2
|
+
require "pathname"
|
3
|
+
require "rake/clean"
|
4
|
+
|
5
|
+
docnames = "{README,LICENSE,CHANGELOG,Changelog,HISTORY}"
|
6
|
+
doctypes = "{,.txt,.rd,.rdoc,.md,.markdown}"
|
7
|
+
cexttypes = "{c,C,cc,cxx,cpp,h,H,hh}"
|
8
|
+
|
9
|
+
DOC = FileList["#{docnames}{,.ja}#{doctypes}"] +
|
10
|
+
FileList["{contrib,ext}/**/#{docnames}{,.ja}#{doctypes}"] +
|
11
|
+
FileList["ext/**/*.#{cexttypes}"]
|
12
|
+
EXT = FileList["ext/**/*"]
|
13
|
+
BIN = FileList["bin/*"]
|
14
|
+
LIB = FileList["lib/**/*.rb"]
|
15
|
+
SPEC = FileList["spec/**/*"]
|
16
|
+
TEST = FileList["test/**/*"]
|
17
|
+
EXAMPLE = FileList["examples/**/*"]
|
18
|
+
GEMSTUB_SRC = "gemstub.rb"
|
19
|
+
RAKEFILE = [File.basename(__FILE__), GEMSTUB_SRC]
|
20
|
+
EXTRA = []
|
21
|
+
EXTCONF = FileList["ext/**/extconf.rb"]
|
22
|
+
EXTCONF.reject! { |n| !File.file?(n) }
|
23
|
+
EXTMAP = {}
|
24
|
+
|
25
|
+
load GEMSTUB_SRC
|
26
|
+
|
27
|
+
EXTMAP.dup.each_pair do |dir, name|
|
28
|
+
EXTMAP[Pathname.new(dir).cleanpath.to_s] = Pathname.new(name).cleanpath.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
GEMSTUB.extensions += EXTCONF
|
32
|
+
GEMSTUB.executables += FileList["bin/*"].map { |n| File.basename n }
|
33
|
+
GEMSTUB.executables.sort!
|
34
|
+
|
35
|
+
PACKAGENAME = "#{GEMSTUB.name}-#{GEMSTUB.version}"
|
36
|
+
GEMFILE = "#{PACKAGENAME}.gem"
|
37
|
+
GEMSPEC = "#{PACKAGENAME}.gemspec"
|
38
|
+
|
39
|
+
GEMSTUB.files += DOC + EXT + EXTCONF + BIN + LIB + SPEC + TEST + EXAMPLE + RAKEFILE + EXTRA
|
40
|
+
GEMSTUB.files.sort!
|
41
|
+
if GEMSTUB.rdoc_options.nil? || GEMSTUB.rdoc_options.empty?
|
42
|
+
readme = %W(.md .markdown .rd .rdoc .txt #{""}).map { |ext| "README#{ext}" }.find { |m| DOC.find { |n| n == m } }
|
43
|
+
GEMSTUB.rdoc_options = %w(--charset UTF-8) + (readme ? %W(-m #{readme}) : [])
|
44
|
+
end
|
45
|
+
GEMSTUB.extra_rdoc_files += DOC + LIB + EXT.reject { |n| n.include?("/externals/") || !%w(.h .hh .c .cc .cpp .cxx).include?(File.extname(n)) }
|
46
|
+
GEMSTUB.extra_rdoc_files.sort!
|
47
|
+
|
48
|
+
GEMSTUB_TRYOUT = GEMSTUB.dup
|
49
|
+
GEMSTUB_TRYOUT.version = "#{GEMSTUB.version}#{Time.now.strftime(".TRYOUT.%Y%m%d.%H%M%S")}"
|
50
|
+
PACKAGENAME_TRYOUT = "#{GEMSTUB.name}-#{GEMSTUB_TRYOUT.version}"
|
51
|
+
GEMFILE_TRYOUT = "#{PACKAGENAME_TRYOUT}.gem"
|
52
|
+
GEMSPEC_TRYOUT = "#{PACKAGENAME_TRYOUT}.gemspec"
|
53
|
+
|
54
|
+
CLEAN << GEMSPEC << GEMSPEC_TRYOUT
|
55
|
+
CLOBBER << GEMFILE
|
56
|
+
|
57
|
+
task :default => :tryout do
|
58
|
+
$stderr.puts <<-EOS
|
59
|
+
#{__FILE__}:#{__LINE__}:
|
60
|
+
\ttype ``rake release'' to build release package.
|
61
|
+
EOS
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "build tryout package"
|
65
|
+
task :tryout
|
66
|
+
|
67
|
+
desc "build release package"
|
68
|
+
task :release => :all
|
69
|
+
|
70
|
+
unless EXTCONF.empty?
|
71
|
+
RUBYSET ||= (ENV["RUBYSET"] || "").split(",")
|
72
|
+
|
73
|
+
if RUBYSET.nil? || RUBYSET.empty?
|
74
|
+
$stderr.puts <<-EOS
|
75
|
+
#{__FILE__}:
|
76
|
+
|
|
77
|
+
| If you want binary gem package, launch rake with ``RUBYSET`` enviroment
|
78
|
+
| variable for set ruby interpreters by comma separated.
|
79
|
+
|
|
80
|
+
| e.g.) $ rake RUBYSET=ruby
|
81
|
+
| or) $ rake RUBYSET=ruby21,ruby22,ruby23
|
82
|
+
|
|
83
|
+
EOS
|
84
|
+
else
|
85
|
+
platforms = RUBYSET.map { |ruby| `#{ruby} --disable-gems -e "puts RUBY_PLATFORM"`.chomp }
|
86
|
+
platforms1 = platforms.uniq
|
87
|
+
unless platforms1.size == 1 && !platforms1[0].empty?
|
88
|
+
abort <<-EOS
|
89
|
+
#{__FILE__}:#{__LINE__}: different platforms:
|
90
|
+
#{RUBYSET.zip(platforms).map { |ruby, platform| "%24s => %s" % [ruby, platform] }.join("\n")}
|
91
|
+
ABORTED.
|
92
|
+
EOS
|
93
|
+
end
|
94
|
+
PLATFORM = platforms1[0]
|
95
|
+
|
96
|
+
RUBY_VERSIONS = RUBYSET.map do |ruby|
|
97
|
+
ver = `#{ruby} --disable-gems -e "puts RUBY_VERSION"`.slice(/\d+\.\d+/)
|
98
|
+
raise "failed ruby checking - ``#{ruby}''" unless $?.success?
|
99
|
+
[ver, ruby]
|
100
|
+
end
|
101
|
+
|
102
|
+
SOFILES_SET = RUBY_VERSIONS.map { |(ver, ruby)|
|
103
|
+
EXTCONF.map { |extconf|
|
104
|
+
extdir = Pathname.new(extconf).cleanpath.dirname.to_s
|
105
|
+
case
|
106
|
+
when soname = EXTMAP[extdir.sub(/^ext\//i, "")]
|
107
|
+
soname = soname.sub(/\.so$/i, "")
|
108
|
+
when extdir == "ext" || extdir == "."
|
109
|
+
soname = GEMSTUB.name
|
110
|
+
else
|
111
|
+
soname = File.basename(extdir)
|
112
|
+
end
|
113
|
+
|
114
|
+
[ruby, File.join("lib", "#{soname.sub(/(?<=\/)|^(?!.*\/)/, "#{ver}/")}.so"), extconf]
|
115
|
+
}
|
116
|
+
}.flatten(1)
|
117
|
+
SOFILES = SOFILES_SET.map { |(ruby, sopath, extconf)| sopath }
|
118
|
+
|
119
|
+
GEMSTUB_NATIVE = GEMSTUB.dup
|
120
|
+
GEMSTUB_NATIVE.files += SOFILES
|
121
|
+
GEMSTUB_NATIVE.platform = Gem::Platform.new(PLATFORM).to_s
|
122
|
+
GEMSTUB_NATIVE.extensions.clear
|
123
|
+
GEMFILE_NATIVE = "#{GEMSTUB_NATIVE.name}-#{GEMSTUB_NATIVE.version}-#{GEMSTUB_NATIVE.platform}.gem"
|
124
|
+
GEMSPEC_NATIVE = "#{GEMSTUB_NATIVE.name}-#{GEMSTUB_NATIVE.platform}.gemspec"
|
125
|
+
|
126
|
+
task :all => ["native-gem", GEMFILE]
|
127
|
+
|
128
|
+
desc "build binary gem package"
|
129
|
+
task "native-gem" => GEMFILE_NATIVE
|
130
|
+
|
131
|
+
desc "generate binary gemspec"
|
132
|
+
task "native-gemspec" => GEMSPEC_NATIVE
|
133
|
+
|
134
|
+
file GEMFILE_NATIVE => DOC + EXT + EXTCONF + BIN + LIB + SPEC + TEST + EXAMPLE + SOFILES + RAKEFILE + [GEMSPEC_NATIVE] do
|
135
|
+
sh "gem build #{GEMSPEC_NATIVE}"
|
136
|
+
end
|
137
|
+
|
138
|
+
file GEMSPEC_NATIVE => RAKEFILE do
|
139
|
+
File.write(GEMSPEC_NATIVE, GEMSTUB_NATIVE.to_ruby, mode: "wb")
|
140
|
+
end
|
141
|
+
|
142
|
+
desc "build c-extension libraries"
|
143
|
+
task "sofiles" => SOFILES
|
144
|
+
|
145
|
+
SOFILES_SET.each do |(ruby, soname, extconf)|
|
146
|
+
sodir = File.dirname(soname)
|
147
|
+
makefile = File.join(sodir, "Makefile")
|
148
|
+
|
149
|
+
CLEAN << GEMSPEC_NATIVE << sodir
|
150
|
+
CLOBBER << GEMFILE_NATIVE
|
151
|
+
|
152
|
+
directory sodir
|
153
|
+
|
154
|
+
desc "generate Makefile for binary extension library"
|
155
|
+
file makefile => [sodir, extconf] do
|
156
|
+
rel_extconf = Pathname.new(extconf).relative_path_from(Pathname.new(sodir)).to_s
|
157
|
+
cd sodir do
|
158
|
+
sh *%W"#{ruby} #{rel_extconf} --ruby=#{ruby} #{ENV["EXTCONF"]}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
desc "build binary extension library"
|
163
|
+
file soname => [makefile] + EXT do
|
164
|
+
cd sodir do
|
165
|
+
sh "make"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
task :all => GEMFILE
|
174
|
+
task :tryout => GEMFILE_TRYOUT
|
175
|
+
|
176
|
+
desc "generate local rdoc"
|
177
|
+
task :rdoc => DOC + LIB do
|
178
|
+
sh *(%w(rdoc) + GEMSTUB.rdoc_options + DOC + LIB)
|
179
|
+
end
|
180
|
+
|
181
|
+
desc "launch rspec"
|
182
|
+
task rspec: :all do
|
183
|
+
sh "rspec"
|
184
|
+
end
|
185
|
+
|
186
|
+
desc "build gem package"
|
187
|
+
task gem: GEMFILE
|
188
|
+
|
189
|
+
desc "generate gemspec"
|
190
|
+
task gemspec: GEMSPEC
|
191
|
+
|
192
|
+
desc "print package name"
|
193
|
+
task "package-name" do
|
194
|
+
puts PACKAGENAME
|
195
|
+
end
|
196
|
+
|
197
|
+
file GEMFILE => DOC + EXT + EXTCONF + BIN + LIB + SPEC + TEST + EXAMPLE + RAKEFILE + [GEMSPEC] do
|
198
|
+
sh "gem build #{GEMSPEC}"
|
199
|
+
end
|
200
|
+
|
201
|
+
file GEMFILE_TRYOUT => DOC + EXT + EXTCONF + BIN + LIB + SPEC + TEST + EXAMPLE + RAKEFILE + [GEMSPEC_TRYOUT] do
|
202
|
+
#file GEMFILE_TRYOUT do
|
203
|
+
sh "gem build #{GEMSPEC_TRYOUT}"
|
204
|
+
end
|
205
|
+
|
206
|
+
file GEMSPEC => RAKEFILE do
|
207
|
+
File.write(GEMSPEC, GEMSTUB.to_ruby, mode: "wb")
|
208
|
+
end
|
209
|
+
|
210
|
+
file GEMSPEC_TRYOUT => RAKEFILE do
|
211
|
+
File.write(GEMSPEC_TRYOUT, GEMSTUB_TRYOUT.to_ruby, mode: "wb")
|
212
|
+
end
|
data/gemstub.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
GEMSTUB = Gem::Specification.new do |s|
|
2
|
+
s.name = "invfs"
|
3
|
+
s.version = File.read("README.md").slice(/^\s*\*\s*version\s*:+.+/).partition(/version\s*:+\s*/i)[2]
|
4
|
+
s.author = "dearblue"
|
5
|
+
s.license = "BSD-2-Clause"
|
6
|
+
s.email = "dearblue@users.noreply.github.com"
|
7
|
+
s.homepage = "https://github.com/dearblue/ruby-invfs"
|
8
|
+
s.summary = %(customization for "require" in VFS support)
|
9
|
+
s.description = %(Customization for "require" in Virtual Filesystem (VFS) support)
|
10
|
+
s.add_runtime_dependency "rubyzip", "~> 1.2"
|
11
|
+
end
|
data/lib/invfs.rb
ADDED
@@ -0,0 +1,366 @@
|
|
1
|
+
#!ruby
|
2
|
+
|
3
|
+
module InVFS
|
4
|
+
end
|
5
|
+
|
6
|
+
InVFS::TOPLEVEL_BINDING = binding.freeze
|
7
|
+
|
8
|
+
require "pathname"
|
9
|
+
require "tempfile"
|
10
|
+
|
11
|
+
module InVFS
|
12
|
+
module Extensions
|
13
|
+
unless Numeric.method_defined?(:clamp)
|
14
|
+
refine Numeric do
|
15
|
+
def clamp(min, max)
|
16
|
+
case
|
17
|
+
when self < min
|
18
|
+
min
|
19
|
+
when self > max
|
20
|
+
max
|
21
|
+
else
|
22
|
+
self
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
unless String.method_defined?(:to_path)
|
29
|
+
refine String do
|
30
|
+
alias to_path to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
refine String do
|
35
|
+
def to_i_with_unit
|
36
|
+
case strip
|
37
|
+
when /^(\d+(\.\d+)?)(?:([kmg])i?)?b?/i
|
38
|
+
unit = 1 << (10 * " kmgtp".index(($3 || " ").downcase))
|
39
|
+
($1.to_f * unit).round
|
40
|
+
else
|
41
|
+
to_i
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
refine Integer do
|
47
|
+
alias to_i_with_unit to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
refine Numeric do
|
51
|
+
def KiB
|
52
|
+
self * (1 << 10)
|
53
|
+
end
|
54
|
+
|
55
|
+
def MiB
|
56
|
+
self * (1 << 20)
|
57
|
+
end
|
58
|
+
|
59
|
+
def GiB
|
60
|
+
self * (1 << 30)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
refine BasicObject do
|
65
|
+
def __native_file_path?
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
[::String, ::File, ::Dir, ::Pathname].each do |klass|
|
71
|
+
refine klass do
|
72
|
+
def __native_file_path?
|
73
|
+
true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
[::String, ::File, ::Dir].each do |klass|
|
79
|
+
refine klass do
|
80
|
+
def file?(path)
|
81
|
+
File.file?(File.join(self, path))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
refine Object do
|
87
|
+
if Object.const_defined?(:DEBUGGER__)
|
88
|
+
BREAKPOINT_SET = {}
|
89
|
+
|
90
|
+
def __BREAKHERE__
|
91
|
+
locate = caller_locations(1, 1)[0]
|
92
|
+
__BREAKPOINT__(locate.path, locate.lineno + 1)
|
93
|
+
end
|
94
|
+
|
95
|
+
def __BREAKPOINT__(base, pos)
|
96
|
+
case base
|
97
|
+
when Module
|
98
|
+
pos = String(pos.to_sym)
|
99
|
+
when String
|
100
|
+
base = "#{base}".freeze
|
101
|
+
pos = pos.to_i
|
102
|
+
else
|
103
|
+
raise ArgumentError
|
104
|
+
end
|
105
|
+
|
106
|
+
key = [base, pos]
|
107
|
+
unless BREAKPOINT_SET[key]
|
108
|
+
BREAKPOINT_SET[key] = true
|
109
|
+
DEBUGGER__.break_points.push [true, 0, base, pos]
|
110
|
+
end
|
111
|
+
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
else
|
115
|
+
def __BREAKHERE__
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
|
119
|
+
def __BREAKPOINT__(base, pos)
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
using Extensions
|
127
|
+
|
128
|
+
DEFAULT_MAX_LOADSIZE = 2.MiB
|
129
|
+
MINIMAL_MAX_LOADSIZE = 256.KiB
|
130
|
+
MAXIMAM_MAX_LOADSIZE = 64.MiB
|
131
|
+
maxloadsize = (ENV["RUBY_REQUIRE_INVFS_MAX_LOADSIZE"] || DEFAULT_MAX_LOADSIZE).to_i_with_unit
|
132
|
+
MAX_LOADSIZE = maxloadsize.clamp(MINIMAL_MAX_LOADSIZE, MAXIMAM_MAX_LOADSIZE)
|
133
|
+
|
134
|
+
#
|
135
|
+
# +$LOADED_FEATURES+ に追加される接頭辞
|
136
|
+
#
|
137
|
+
LOADED_PREFIX = "<inVFS>:".freeze
|
138
|
+
|
139
|
+
def InVFS.findlib(vfs, lib, relative)
|
140
|
+
if relative
|
141
|
+
if vfs.file?(lib)
|
142
|
+
lib
|
143
|
+
else
|
144
|
+
nil
|
145
|
+
end
|
146
|
+
else
|
147
|
+
case
|
148
|
+
when vfs.file?(lib)
|
149
|
+
lib
|
150
|
+
when vfs.file?(librb = lib + ".rb")
|
151
|
+
librb
|
152
|
+
when vfs.file?(libso = lib + ".so")
|
153
|
+
libso
|
154
|
+
else
|
155
|
+
nil
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def InVFS.findvfs(lib, relative)
|
161
|
+
findpath(lib, relative) do |vfs, sub|
|
162
|
+
next unless sub = findlib(vfs, sub, relative)
|
163
|
+
return nil if vfs.__native_file_path?
|
164
|
+
next if vfs.size(sub) > MAX_LOADSIZE
|
165
|
+
return [vfs, sub]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# call-seq:
|
171
|
+
# findpath(absolute_lib_path, relative) { |vfs, subpath| ... }
|
172
|
+
#
|
173
|
+
def InVFS.findpath(lib, relative)
|
174
|
+
if relative || Pathname(lib).absolute?
|
175
|
+
# 絶対パス表記でのライブラリ指定、または require_relative の呼び出し元
|
176
|
+
|
177
|
+
# NOTE: LOAD_PATH ($:) の順序を優先するか?
|
178
|
+
# NOTE: 一致するティレクトリ階層の深さを優先するか?
|
179
|
+
# NOTE: => とりあえずは順序を優先しておく
|
180
|
+
|
181
|
+
$:.each do |vfs|
|
182
|
+
#__BREAKHERE__
|
183
|
+
dir = String(vfs.to_path)
|
184
|
+
dir += "/" unless dir.empty? || dir[-1] == "/"
|
185
|
+
(a, b, c) = lib.partition(dir)
|
186
|
+
next unless a.empty? && !b.empty? && !c.empty?
|
187
|
+
yield(vfs, c)
|
188
|
+
end
|
189
|
+
else
|
190
|
+
$:.each { |vfs| yield(vfs, lib) }
|
191
|
+
end
|
192
|
+
|
193
|
+
nil
|
194
|
+
end
|
195
|
+
|
196
|
+
def InVFS.loaded?(vfs, lib)
|
197
|
+
!!$".include?(LOADED_PREFIX + File.join(vfs, lib))
|
198
|
+
end
|
199
|
+
|
200
|
+
def InVFS.require_in(vfs, path)
|
201
|
+
code = String(vfs.read(path))
|
202
|
+
loadpath = File.join(vfs, path)
|
203
|
+
unless File.extname(path) == ".so"
|
204
|
+
unless code.encoding == Encoding::UTF_8
|
205
|
+
code = code.dup if code.frozen?
|
206
|
+
code.force_encoding(Encoding::UTF_8)
|
207
|
+
end
|
208
|
+
|
209
|
+
eval code, InVFS::TOPLEVEL_BINDING.dup, loadpath, 1
|
210
|
+
else
|
211
|
+
Dir.mktmpdir do |dir|
|
212
|
+
tempname = File.join(dir, File.basename(path))
|
213
|
+
mode = File::CREAT | File::WRONLY | File::EXCL | File::BINARY
|
214
|
+
File.open(tempname, mode, 0700) { |fd| fd << code }
|
215
|
+
require! tempname
|
216
|
+
$".pop # 偽装したライブラリパスであるため、削除
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
$" << (LOADED_PREFIX + loadpath)
|
221
|
+
|
222
|
+
true
|
223
|
+
end
|
224
|
+
|
225
|
+
module Kernel
|
226
|
+
private
|
227
|
+
def require(lib)
|
228
|
+
__BREAKHERE__
|
229
|
+
(vfs, sub) = InVFS.findvfs(Pathname(lib).cleanpath.to_path, false)
|
230
|
+
if vfs
|
231
|
+
return false if InVFS.loaded?(vfs, sub)
|
232
|
+
InVFS.require_in(vfs, sub)
|
233
|
+
else
|
234
|
+
super lib
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
private
|
239
|
+
def require_relative(lib)
|
240
|
+
__BREAKHERE__
|
241
|
+
base = caller_locations(1, 1)[0]
|
242
|
+
(vfs, sub) = InVFS.findvfs(base.path, true)
|
243
|
+
if vfs
|
244
|
+
sub = (Pathname(sub) + ".." + lib).cleanpath.to_path
|
245
|
+
sub = InVFS.findlib(vfs, sub, false)
|
246
|
+
raise LoadError, "cannot load such file - #{lib}" unless sub
|
247
|
+
return false if InVFS.loaded?(vfs, sub)
|
248
|
+
InVFS.require_in(vfs, sub)
|
249
|
+
else
|
250
|
+
eval <<-"REQUIRE_RELATIVE", binding, base.path, base.lineno
|
251
|
+
super lib
|
252
|
+
REQUIRE_RELATIVE
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class UnionFS
|
258
|
+
attr_reader :dirs
|
259
|
+
|
260
|
+
def initialize(*dirs)
|
261
|
+
@dirs = dirs
|
262
|
+
end
|
263
|
+
|
264
|
+
def file?(lib)
|
265
|
+
dirs.each do |dir|
|
266
|
+
path = File.join(dir, lib)
|
267
|
+
return true if File.file?(path)
|
268
|
+
end
|
269
|
+
|
270
|
+
false
|
271
|
+
end
|
272
|
+
|
273
|
+
def size(lib)
|
274
|
+
dirs.each do |dir|
|
275
|
+
path = File.join(dir, lib)
|
276
|
+
return File.size(path) if File.file?(path)
|
277
|
+
end
|
278
|
+
|
279
|
+
raise Errno::ENOENT, lib
|
280
|
+
end
|
281
|
+
|
282
|
+
def read(lib)
|
283
|
+
dirs.each do |dir|
|
284
|
+
path = File.join(dir, lib)
|
285
|
+
return File.binread(path) if File.file?(path)
|
286
|
+
end
|
287
|
+
|
288
|
+
raise Errno::ENOENT, lib
|
289
|
+
end
|
290
|
+
|
291
|
+
def to_path
|
292
|
+
%(#<#{self.class} #{dirs.map { |d| "<#{d}>" }.join(", ")}>)
|
293
|
+
end
|
294
|
+
|
295
|
+
def to_s
|
296
|
+
to_path
|
297
|
+
end
|
298
|
+
|
299
|
+
def inspect
|
300
|
+
to_s
|
301
|
+
end
|
302
|
+
|
303
|
+
def pretty_print(q)
|
304
|
+
q.group(2, "#<#{self.class}", ">") do
|
305
|
+
dirs.each_with_index do |d, i|
|
306
|
+
q.text "," if i > 0
|
307
|
+
q.breakable " "
|
308
|
+
d.pretty_print q
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
MultipleDirectory = UnionFS
|
315
|
+
|
316
|
+
class StringMapFS
|
317
|
+
attr_reader :map
|
318
|
+
|
319
|
+
def initialize(*map)
|
320
|
+
@map = Hash[*map]
|
321
|
+
end
|
322
|
+
|
323
|
+
def to_path
|
324
|
+
sprintf %(#<%s 0x%08x>) % [self.class, object_id]
|
325
|
+
end
|
326
|
+
|
327
|
+
def file?(path)
|
328
|
+
!!map.has_key?(path)
|
329
|
+
end
|
330
|
+
|
331
|
+
def size(path)
|
332
|
+
(map[path] or return nil).bytesize
|
333
|
+
end
|
334
|
+
|
335
|
+
def read(path)
|
336
|
+
(map[path] or return nil).to_s
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
module InVFS
|
342
|
+
origin = Object.method(:require)
|
343
|
+
define_singleton_method(:require!, ->(lib) {
|
344
|
+
origin.call lib
|
345
|
+
})
|
346
|
+
end
|
347
|
+
|
348
|
+
module InVFS
|
349
|
+
origin = Object.method(:require_relative)
|
350
|
+
define_singleton_method(:require_relative!, ->(lib, base) {
|
351
|
+
eval <<-REQUIRE, nil, base.path, base.lineno
|
352
|
+
origin.call lib
|
353
|
+
REQUIRE
|
354
|
+
})
|
355
|
+
end
|
356
|
+
|
357
|
+
module Kernel
|
358
|
+
prepend InVFS::Kernel
|
359
|
+
end
|
360
|
+
|
361
|
+
#
|
362
|
+
# あらためて Object に Kernel を include する必要がある
|
363
|
+
;
|
364
|
+
class Object
|
365
|
+
include Kernel
|
366
|
+
end
|
data/lib/invfs/zip.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!ruby
|
2
|
+
|
3
|
+
require "zip/filesystem"
|
4
|
+
require_relative "../invfs"
|
5
|
+
|
6
|
+
def InVFS.zip(*args)
|
7
|
+
InVFS::Zip.new(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
class InVFS::Zip
|
11
|
+
attr_reader :path, :zip, :zipfile
|
12
|
+
|
13
|
+
def initialize(path)
|
14
|
+
@path = String(path)
|
15
|
+
@zip = Zip::File.open(@path)
|
16
|
+
@zipfile = @zip.file
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# call-seq:
|
21
|
+
# to_path -> string
|
22
|
+
#
|
23
|
+
# REQUIRED method for VFS.
|
24
|
+
#
|
25
|
+
# This value MUST be not modifying in each objects.
|
26
|
+
#
|
27
|
+
def to_path
|
28
|
+
path
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# call-seq:
|
33
|
+
# file?(path) -> true OR false (OR nil)
|
34
|
+
#
|
35
|
+
# REQUIRED method for VFS.
|
36
|
+
#
|
37
|
+
def file?(path)
|
38
|
+
zipfile.file?(path)
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# call-seq:
|
43
|
+
# size(path) -> integer for file size
|
44
|
+
#
|
45
|
+
# REQUIRED method for VFS.
|
46
|
+
#
|
47
|
+
def size(path)
|
48
|
+
zipfile.size(path)
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# call-seq:
|
53
|
+
# read(path) -> string
|
54
|
+
#
|
55
|
+
# REQUIRED method for VFS.
|
56
|
+
#
|
57
|
+
def read(path)
|
58
|
+
zipfile.read(path)
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# optional method for VFS.
|
63
|
+
#
|
64
|
+
def to_s
|
65
|
+
%(#{path} (#{self.class}))
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
%(#<#{self.class}:#{path}>)
|
70
|
+
end
|
71
|
+
|
72
|
+
def pretty_print(q)
|
73
|
+
q.text inspect
|
74
|
+
end
|
75
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: invfs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.2'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- dearblue
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubyzip
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
description: Customization for "require" in Virtual Filesystem (VFS) support
|
28
|
+
email: dearblue@users.noreply.github.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.md
|
34
|
+
- lib/invfs.rb
|
35
|
+
- lib/invfs/zip.rb
|
36
|
+
files:
|
37
|
+
- LICENSE
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- gemstub.rb
|
41
|
+
- lib/invfs.rb
|
42
|
+
- lib/invfs/zip.rb
|
43
|
+
homepage: https://github.com/dearblue/ruby-invfs
|
44
|
+
licenses:
|
45
|
+
- BSD-2-Clause
|
46
|
+
metadata: {}
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- "--charset"
|
50
|
+
- UTF-8
|
51
|
+
- "-m"
|
52
|
+
- README.md
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 2.7.4
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: customization for "require" in VFS support
|
71
|
+
test_files: []
|