io-afc 0.0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/io/afc.rb ADDED
@@ -0,0 +1,115 @@
1
+ require 'io/afc/version'
2
+ require 'io/afc/afc_base'
3
+ require 'io/afc/file'
4
+
5
+ class IO
6
+ class AFC < AFCBase
7
+ private
8
+ def dict_to_hash(dict)
9
+ v = nil
10
+ dict.inject({}){|s, k|
11
+ unless v
12
+ v = k
13
+ else
14
+ v = v.to_i if v=~/^\d+$/
15
+ s[k.to_sym] = v
16
+ v = nil
17
+ end
18
+ s
19
+ }
20
+ end
21
+
22
+ public
23
+
24
+ ##
25
+ # Connect to iOS-Device and yield block with handle if specified.
26
+ # @option [:uuid=>String] Target UUID. if nil, connect to default device.
27
+ # @option [:appid=>String] Mount application document folder if specified.
28
+ # @return [IO::AFC or block-result]
29
+ def self.connect(opt={}, &block)
30
+ dev = self.new(opt[:udid], opt[:appid])
31
+ return dev unless block_given?
32
+
33
+ begin
34
+ r = yield(dev)
35
+ dev.close
36
+ rescue Exception=>e
37
+ dev.close
38
+ throw e
39
+ end
40
+ r
41
+ end
42
+
43
+ ##
44
+ # Get storage info.
45
+ # @return [Hash]
46
+ #
47
+ # Example:
48
+ # afc.statfs
49
+ # => {:FSBlockSize=>4096,
50
+ # :FSFreeBytes=>27478216704,
51
+ # :FSTotalBytes=>30178787328,
52
+ # :Model=>"iPod5,1"}
53
+ def statfs
54
+ dict_to_hash(super)
55
+ end
56
+
57
+ ##
58
+ # Open file.
59
+ # @param [String] path for target file.
60
+ # @param [IO::Constants] mode for open mode. Default is IO::RDONLY.
61
+ def open(path, mode = IO::RDONLY, &block)
62
+ raise ArgumentError, "block is required." unless block_given?
63
+ File.open_file(self, path, mode, &block)
64
+ end
65
+
66
+ ##
67
+ # Get file/directory attribytes.
68
+ # @param [String] path.
69
+ # @return [Hash]
70
+ #
71
+ # Example:
72
+ # afc.getattr("/Safari")
73
+ # => {:st_birthtime=>2012-12-17 12:28:23 +0900,
74
+ # :st_mtime=>2013-06-22 20:32:01 +0900
75
+ # :st_ifmt=>"S_IFDIR",
76
+ # :st_nlink=>2,
77
+ # :st_blocks=>0,
78
+ # :st_size=>102}
79
+ #
80
+ # afc.getattr("/Safari/goog-phish-shavar.db")
81
+ # => {:st_birthtime=>2013-06-21 02:31:17 +0900,
82
+ # :st_mtime=>2013-06-21 02:31:18 +0900,
83
+ # :st_ifmt=>"S_IFREG",
84
+ # :st_nlink=>1,
85
+ # :st_blocks=>11256,
86
+ # :st_size=>5763072}
87
+ def getattr(path)
88
+ r = dict_to_hash(super(path))
89
+ [:st_birthtime, :st_mtime].each{|k|
90
+ r[k] = Time.at(r[k] / 1000000000.0)
91
+ }
92
+ r
93
+ end
94
+
95
+ ##
96
+ # Check exist
97
+ # @param [String] path.
98
+ # @return [String or nil] return type or nil.
99
+ def exist?(path)
100
+ begin
101
+ getattr(path)[:st_ifmt]
102
+ rescue Errno::ENOENT
103
+ nil
104
+ end
105
+ end
106
+
107
+ ##
108
+ # Read symlink.
109
+ # @param [String] path.
110
+ # @return [String or nil] return symlink-target.
111
+ def readlink(path)
112
+ getattr(path)[:LinkTarget]
113
+ end
114
+ end
115
+ end
Binary file
Binary file
@@ -0,0 +1,39 @@
1
+ $LOAD_PATH << "./ext"
2
+ $LOAD_PATH << "./lib"
3
+ require 'spec_helper'
4
+ require 'io/afc'
5
+
6
+
7
+ describe IO::AFC::File do
8
+ before do
9
+ @afc = IO::AFC.connect
10
+ end
11
+
12
+ context "method: open, " do
13
+ it "should require block." do
14
+ expect{ @afc.open("/") }.to raise_error(ArgumentError)
15
+ end
16
+
17
+ it "should be open '/'" do
18
+ r = @afc.open("/"){|f|
19
+ expect(f).to be_an_instance_of(IO::AFC::File)
20
+ expect(f.closed?).to eq(false)
21
+ f
22
+ }
23
+ expect(r).to be_an_instance_of(IO::AFC::File)
24
+ expect(r.closed?).to eq(true)
25
+ end
26
+
27
+ it "should be create file and write." do
28
+ @afc.open("/tempfile", IO::RDWR){|f|
29
+ expect(f).to be_an_instance_of(IO::AFC::File)
30
+ expect(f.write("Hello")).to eq(5)
31
+ }
32
+ end
33
+ end
34
+
35
+ after do
36
+ @afc.unlink("/tempfile") rescue nil
37
+ @afc.close
38
+ end
39
+ end
data/spec/afc_spec.rb ADDED
@@ -0,0 +1,87 @@
1
+ $LOAD_PATH << "./ext"
2
+ $LOAD_PATH << "./lib"
3
+ require 'spec_helper'
4
+ require 'io/afc'
5
+
6
+
7
+ describe IO::AFC do
8
+ it "should be connect." do
9
+ r = IO::AFC.connect{|afc|
10
+ expect(afc).to be_an_instance_of(IO::AFC)
11
+ expect(afc.closed?).to eq(false)
12
+ afc
13
+ }
14
+ expect(r).to be_an_instance_of(IO::AFC)
15
+ expect(r.closed?).to eq(true)
16
+ end
17
+
18
+ it "should be connect without block." do
19
+ afc = IO::AFC.connect
20
+ expect(afc).to be_an_instance_of(IO::AFC)
21
+ expect(afc.closed?).to eq(false)
22
+ expect(afc.close).to be_nil
23
+ expect(afc.closed?).to eq(true)
24
+ end
25
+
26
+
27
+ context "test for storage methods, " do
28
+ before do
29
+ @afc = IO::AFC.connect
30
+ end
31
+
32
+ it "should be mkdir and unlink." do
33
+ expect(@afc.mkdir("/tempdir")).to be_true
34
+ expect(@afc.unlink("/tempdir")).to be_true
35
+ end
36
+
37
+ it "should be getattr for directory." do
38
+ attr = @afc.getattr("/Safari")
39
+ expect(attr).to be_an_instance_of(Hash)
40
+ expect(attr[:st_birthtime]).to be_an_instance_of(Time)
41
+ expect(attr[:st_mtime]).to be_an_instance_of(Time)
42
+ expect(attr[:st_ifmt]).to eq("S_IFDIR")
43
+ end
44
+
45
+ context "with tempdir, " do
46
+ before do
47
+ make_directory("/tempdir")
48
+ @now = Time.now
49
+ end
50
+
51
+ it "should be getattr for directory (Time)." do
52
+ attr = @afc.getattr("/tempdir")
53
+ expect(attr[:st_birthtime].to_i).to be_within(@now.to_i+10).of(@now.to_i-10)
54
+ expect(attr[:st_mtime].to_i).to be_within(@now.to_i+10).of(@now.to_i+10)
55
+ end
56
+
57
+ it "should be mkdir, rename and unlink subdir." do
58
+ expect(@afc.mkdir("/tempdir/subdir")).to be_true
59
+ expect(@afc.rename("/tempdir/subdir", "/tempdir/subdir-2")).to be_true
60
+ expect(@afc.unlink("/tempdir/subdir-2")).to be_true
61
+ end
62
+
63
+ it "should be symlink, readlink and unlink." do
64
+ expect(@afc.symlink("subdir", "/tempdir/subdir-2")).to be_true
65
+ expect(@afc.readlink("/tempdir/subdir-2")).to eq("subdir")
66
+ expect(@afc.unlink("/tempdir/subdir-2")).to be_true
67
+ end
68
+
69
+ it "should be nil if readlink to not a link." do
70
+ expect(@afc.readlink("/tempdir")).to be_nil
71
+ end
72
+
73
+ it "should be check exists." do
74
+ expect(@afc.exist?("/tempdir")).to be_an_instance_of(String)
75
+ expect(@afc.exist?("/tempdir-not-exist")).to be_nil
76
+ end
77
+
78
+ after do
79
+ remove_directory("/tempdir")
80
+ end
81
+ end
82
+
83
+ after do
84
+ @afc.close
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,50 @@
1
+ $LOAD_PATH << "./ext"
2
+ require 'io/afc/afc_base'
3
+ require 'spec_helper'
4
+
5
+
6
+ describe IO::AFCBase do
7
+ context "Basic storage I/O, " do
8
+ context "File control, " do
9
+ before do
10
+ @afc = IO::AFCBase.new(nil, nil)
11
+ f = IO::AFCBase::Descriptor.new(@afc, "/tempfile", 2)
12
+ begin
13
+ f.write("hello")
14
+ ensure
15
+ f.close
16
+ end
17
+ end
18
+
19
+ it "should be utimens." do
20
+ t = Time.now - 200000000
21
+ expect(@afc.utimens("/tempfile", t)).to be_true
22
+ stat = dict_to_hash(@afc.getattr("/tempfile"))
23
+ expect(stat["st_mtime"].to_i).to eq(t.to_i*1000_000_000)
24
+ end
25
+
26
+ it "should be get file size." do
27
+ stat = dict_to_hash(@afc.getattr("/tempfile"))
28
+ expect(stat["st_size"].to_i).to eq(5)
29
+ end
30
+
31
+ it "should be truncate." do
32
+ expect(@afc.truncate("/tempfile", 1)).to be_true
33
+ stat = dict_to_hash(@afc.getattr("/tempfile"))
34
+ expect(stat["st_size"].to_i).to eq(1)
35
+ end
36
+
37
+ it "should be create hardlink." do
38
+ expect(@afc.link("/tempfile", "/tempfile-2")).to be_true
39
+ expect(@afc.readdir("/")).to be_include("tempfile-2")
40
+ end
41
+
42
+ after do
43
+ %w{/tempfile /tempfile-2}.each{|p|
44
+ @afc.unlink(p) rescue nil
45
+ }
46
+ @afc.close
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ $LOAD_PATH << "./ext"
2
+ require 'io/afc/afc_base'
3
+ require 'spec_helper'
4
+
5
+
6
+ describe IO::AFCBase do
7
+ context "Basic storage I/O, " do
8
+ context "File IO, " do
9
+ before do
10
+ @afc = IO::AFCBase.new(nil, nil)
11
+ end
12
+
13
+ it "should be create file and close." do
14
+ f = IO::AFCBase::Descriptor.new(@afc, "/tempfile", 2)
15
+ begin
16
+ expect(f).to be_an_instance_of(IO::AFCBase::Descriptor)
17
+ expect(f.closed?).to eq(false)
18
+ expect(f.write("hello")).to eq(5)
19
+ ensure
20
+ expect(f.close).to be_nil
21
+ expect(f.closed?).to eq(true)
22
+ end
23
+
24
+ f = IO::AFCBase::Descriptor.new(@afc, "/tempfile", 0)
25
+ begin
26
+ expect(f.read(-1)).to eq("hello")
27
+ expect(f.tell).to eq(5)
28
+ expect(f.seek(0, IO::SEEK_SET)).to be_true
29
+ expect(f.tell).to eq(0)
30
+ ensure
31
+ f.close
32
+ end
33
+
34
+ f = IO::AFCBase::Descriptor.new(@afc, "/tempfile", 2)
35
+ begin
36
+ expect(f.ftruncate(2)).to be_true
37
+ ensure
38
+ f.close
39
+ end
40
+ info = @afc.getattr("/tempfile")
41
+ size = info[info.find_index("st_size")-1].to_i
42
+ expect(size).to eq(2)
43
+ end
44
+
45
+ it "should be write 0 bytes when data is nil." do
46
+ f = IO::AFCBase::Descriptor.new(@afc, "/tempfile", 2)
47
+ begin
48
+ expect(f.write(nil)).to eq(0)
49
+ ensure
50
+ f.close
51
+ end
52
+ end
53
+
54
+ after do
55
+ @afc.unlink("/tempfile") rescue nil
56
+ @afc.close
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,79 @@
1
+ $LOAD_PATH << "./ext"
2
+ require 'spec_helper'
3
+ require 'io/afc/afc_base'
4
+
5
+
6
+ describe IO::AFCBase do
7
+ context "Basic storage I/O, " do
8
+ it "should be connect device." do
9
+ expect(IO::AFCBase.new(nil, nil)).to be_an_instance_of(IO::AFCBase)
10
+ end
11
+
12
+ it "should be close." do
13
+ afc = IO::AFCBase.new(nil, nil)
14
+ expect(afc.closed?).to eq(false)
15
+ expect(afc.close).to be_nil
16
+ expect(afc.closed?).to eq(true)
17
+ end
18
+
19
+ context "After connect, " do
20
+ before do
21
+ @afc = IO::AFCBase.new(nil, nil)
22
+ end
23
+
24
+ it "should be get attributes." do
25
+ attr = @afc.getattr("/")
26
+ expect(attr).to be_an_instance_of(Array)
27
+ end
28
+
29
+ it "should be readdir." do
30
+ dirs = @afc.readdir("/")
31
+ expect(dirs).to be_an_instance_of(Array)
32
+ end
33
+
34
+ it "should be get statfs." do
35
+ stat = @afc.statfs
36
+ expect(stat).to be_an_instance_of(Array)
37
+ end
38
+
39
+ it "should be make a directory, rename, and unlink." do
40
+ expect(@afc.mkdir("/tmp")).to be_true
41
+ expect(@afc.rename("/tmp", "/temp")).to be_true
42
+ expect(@afc.unlink("/temp")).to be_true
43
+ end
44
+
45
+ it "should be crerate a symlink." do
46
+ expect(@afc.symlink("hello", "/tempfile-2")).to be_true
47
+ expect(@afc.readdir("/")).to be_include("tempfile-2")
48
+ stat = dict_to_hash(@afc.getattr("/tempfile-2"))
49
+ expect(stat["LinkTarget"]).to eq("hello")
50
+ end
51
+
52
+ it "should be get device udid." do
53
+ expect(@afc.device_udid).to be_an_instance_of(String)
54
+ end
55
+
56
+ it "should be get device display name." do
57
+ expect(@afc.device_display_name).to be_an_instance_of(String)
58
+ end
59
+
60
+ it "should be get application list." do
61
+ apps = @afc.applications
62
+ expect(apps).to be_an_instance_of(Hash)
63
+ apps.each{|app, val|
64
+ expect(app).to be_an_instance_of(String)
65
+ expect(val).to be_an_instance_of(Hash)
66
+ expect(val["CFBundleDisplayName"]).to be_an_instance_of(String)
67
+ expect([true, false]).to include(val["UIFileSharingEnabled"])
68
+ }
69
+ end
70
+
71
+ after do
72
+ %w{/tempfile /tempfile-2 /tmp /temp}.each{|p|
73
+ @afc.unlink(p) rescue nil
74
+ }
75
+ @afc.close
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,48 @@
1
+ require 'thread'
2
+ require 'pry-nav'
3
+
4
+ puts Process.pid
5
+ #binding.pry
6
+
7
+
8
+ def dict_to_hash(dict)
9
+ v = nil
10
+ h = {}
11
+ dict.each_index{|n|
12
+ case n%2
13
+ when 0
14
+ v = dict[n]
15
+ when 1
16
+ k = dict[n]
17
+ h[k] = v
18
+ end
19
+ }
20
+ h
21
+ end
22
+
23
+
24
+ $mutex = Mutex.new
25
+ $dirs = {}
26
+ def make_directory(path)
27
+ $mutex.synchronize{
28
+ unless $dirs[path]
29
+ IO::AFC.connect{|afc|
30
+ afc.mkdir(path)
31
+ }
32
+ $dirs[path] = 0
33
+ end
34
+ $dirs[path] += 1
35
+ }
36
+ end
37
+
38
+ def remove_directory(path)
39
+ $mutex.synchronize{
40
+ $dirs[path] -= 1
41
+ if $dirs[path] == 0
42
+ IO::AFC.connect{|afc|
43
+ afc.unlink(path)
44
+ $dirs.delete(path)
45
+ }
46
+ end
47
+ }
48
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: io-afc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Toshiyuki Suzumura
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Access to the file system of iOS devices from Ruby.
47
+ email:
48
+ - suz.labo@amail.plala.or.jp
49
+ executables: []
50
+ extensions:
51
+ - ext/io/afc/extconf.rb
52
+ extra_rdoc_files: []
53
+ files:
54
+ - ext/io/afc/build.xcconfig
55
+ - ext/io/afc/extconf.rb
56
+ - ext/io/afc/ioafc.cpp
57
+ - ext/io/afc/ioafc.h
58
+ - ext/io/afc/ruby_wrapper.h
59
+ - Gemfile
60
+ - Gemfile.lock
61
+ - io-afc.gemspec
62
+ - io-afc.xcodeproj/project.pbxproj
63
+ - lib/io/afc/file.rb
64
+ - lib/io/afc/version.rb
65
+ - lib/io/afc.rb
66
+ - LICENSE.txt
67
+ - pkg/io-afc-0.0.3.1.gem
68
+ - pkg/io-afc-0.0.3.2.gem
69
+ - Rakefile
70
+ - README.md
71
+ - spec/afc_file_spec.rb
72
+ - spec/afc_spec.rb
73
+ - spec/basicio_filectrl_spec.rb
74
+ - spec/basicio_fileio_spec.rb
75
+ - spec/basicio_spec.rb
76
+ - spec/spec_helper.rb
77
+ homepage: https://github.com/suzumura-ss/ruby-io-afc
78
+ licenses:
79
+ - MIT
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ segments:
91
+ - 0
92
+ hash: 378568250842512408
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ segments:
100
+ - 0
101
+ hash: 378568250842512408
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.25
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Access to the file system of iOS devices from Ruby. Required 'libimobiledevice'.
108
+ test_files:
109
+ - spec/afc_file_spec.rb
110
+ - spec/afc_spec.rb
111
+ - spec/basicio_filectrl_spec.rb
112
+ - spec/basicio_fileio_spec.rb
113
+ - spec/basicio_spec.rb
114
+ - spec/spec_helper.rb