xtendr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ doc
19
+ .yardoc
20
+ pkg
21
+
22
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Louis J. Scoras
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ = xtendr
2
+
3
+ Ruby FFI binding for accessing extended file attributes.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Louis J. Scoras. See LICENSE for details.
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "xtendr"
8
+ gem.summary = %Q{FFI binding for accessing extended file attributes.}
9
+ gem.description = %Q{Xtendr provides access to extended file system attributes. Ruby 1.9 compatible.}
10
+ gem.email = "louis.j.scoras@gmail.com"
11
+ gem.homepage = "http://github.com/ljsc/xtendr"
12
+ gem.authors = ["Louis J. Scoras"]
13
+ gem.add_development_dependency "yard", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ begin
46
+ require 'yard'
47
+ YARD::Rake::YardocTask.new do |ydoc|
48
+ ydoc.options = %w[--no-private]
49
+ end
50
+ rescue LoadError
51
+ task :yardoc do
52
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
53
+ end
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,136 @@
1
+ require 'ffi'
2
+
3
+ module Xtendr
4
+ # Module to contain attached foreign functions.
5
+ # @private
6
+ module FFI
7
+ extend ::FFI::Library
8
+ ffi_lib 'System'
9
+ attach_function 'setxattr', [:string, :string, :pointer, :int, :int, :int], :int
10
+ attach_function 'getxattr', [:string, :string, :pointer, :int, :int, :int], :int
11
+ attach_function 'listxattr', [:string, :pointer, :int, :int], :int
12
+ attach_function 'removexattr', [:string, :string, :int], :int
13
+ end
14
+
15
+ # Get the value of an extended attribute for the current object. Calls
16
+ # `#to_s` on self to get the path to the file the attribute is set on.
17
+ #
18
+ # @param [String] attribute The extended attribute to retrieve.
19
+ # @return [String, nil] The value of the attribute, or nil if it is not set
20
+ # and no block is passed.
21
+ # @yield Block is run when the attribute is not found.
22
+ def getx(attribute, *opts)
23
+ getx! attribute, *opts
24
+ rescue Errno::ENOATTR
25
+ block_given? ? yield : nil
26
+ end
27
+
28
+ # Get the value of an extended attribute, raising an error if the attribute is
29
+ # not found.
30
+ #
31
+ # The bang version of getx works the the same way as `getx`, exept that when
32
+ # the attribute is not found. In that case, rather than returning `nil`, an
33
+ # ENOATTR error is raised.
34
+ # @raise [Errno::ENOATTR] Describes the attribute that couldn't be located.
35
+ # @param [String] attribute The extended attribute to retrieve.
36
+ # @return [String] The value of the requested attribute.
37
+ #
38
+ def getx!(attribute, *opts)
39
+ options = opts.include?(:no_follow) ? 1 : 0
40
+ len = FFI.getxattr(self.to_s, attribute, nil, 0, 0, options)
41
+ raise_error(attribute, ::FFI::LastError::error) if len < 0
42
+ buffer = ::FFI::Buffer.alloc_out(len)
43
+ FFI.getxattr(self.to_s, attribute, buffer, buffer.size, 0, options)
44
+ buffer.get_string(0, buffer.size)
45
+ end
46
+
47
+ # Set an extended attribute on a filesystem object.
48
+ # @param [String] attribute The extended attribute to retrieve.
49
+ # @param [String] value The value to set the extended attribute to.
50
+ # @param [Array<:create, :replace, :no_follow>] opts Optional flags which
51
+ # influence how setting is done.
52
+ # @return [nil]
53
+ #
54
+ # *:create*:: Raises an error if the attribute already exists.
55
+ #
56
+ def setx(attribute, value, *opts)
57
+ retval = FFI.setxattr(self.to_s, attribute, value, value.size, 0, setx_options(*opts))
58
+ raise_error(nil, ::FFI::LastError::error) if retval < 0
59
+ end
60
+
61
+ private
62
+ # Set the correct bitfields on the options flag for setting operations
63
+ # @param [Array<:replace, :create, :no_follow>] syms
64
+ # @private
65
+ def setx_options(*syms)
66
+ options = 0x00
67
+ options |= 0x04 if syms.include?(:replace)
68
+ options |= 0x02 if syms.include?(:create)
69
+ options |= 0x01 if syms.include?(:no_follow)
70
+ options
71
+ end
72
+
73
+ public
74
+
75
+ # List all of the extended attribute for the object.
76
+ # @return [Array<String>] the list of all keys which have extended attributes
77
+ # set.
78
+ def listx
79
+ len = FFI.listxattr(self.to_s, nil, 0, 0)
80
+ buffer = ::FFI::Buffer.alloc_out(len)
81
+ FFI.listxattr(self.to_s, buffer, buffer.size, 0)
82
+ buffer.get_bytes(0, buffer.size).split("\0")
83
+ end
84
+
85
+ # Removes the given extended attribute from the filesystem object.
86
+ # @param [String] attribute The attribute to remove the extended attribute
87
+ # for.
88
+ # @return [nil]
89
+ def removex(attribute)
90
+ FFI.removexattr(self.to_s, attribute, 0)
91
+ end
92
+
93
+ # Remove all extended file attributes from the object
94
+ def remove_allx
95
+ listx.each do |attribute|
96
+ removex(attribute)
97
+ end
98
+ end
99
+
100
+ protected
101
+ def raise_error(msg, code)
102
+ if code == Errno::ENOATTR::Errno
103
+ raise Errno::ENOATTR.new(msg)
104
+ else
105
+ raise SystemCallError.new(msg, code)
106
+ end
107
+ end
108
+ end
109
+
110
+ class Errno::ENOATTR < SystemCallError
111
+ # FIXME: This works on Mac OS X (Snow Leopard), but this really shouldn't be
112
+ # hardcoded. Unfortunatly, we probably can't get the error number symbolically
113
+ # without a C extension, which defeats the purpose of using ffi.
114
+ Errno = 93
115
+
116
+ def initialize(msg=nil)
117
+ super(msg)
118
+ end
119
+ end
120
+
121
+ class XtendrProxy
122
+ def initialize(file)
123
+ @file = file
124
+ end
125
+
126
+ def to_s
127
+ @file
128
+ end
129
+
130
+ include Xtendr
131
+ end
132
+
133
+ def Xtendr(file)
134
+ XtendrProxy.new(file)
135
+ end
136
+
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'xtendr'
7
+
8
+ class Test::Unit::TestCase
9
+ end
@@ -0,0 +1,106 @@
1
+ require 'helper'
2
+ require 'fileutils'
3
+
4
+ class TestXtendr < Test::Unit::TestCase
5
+ include FileUtils
6
+
7
+ def setup
8
+ @store = Xtendr(__FILE__)
9
+ end
10
+
11
+ def teardown
12
+ Xtendr(__FILE__).remove_allx
13
+ end
14
+
15
+ def test_module
16
+ test_file = __FILE__
17
+ test_file.extend(Xtendr)
18
+ test_file.setx('module', 'Foobar')
19
+ assert_equal 'Foobar', test_file.getx('module')
20
+ end
21
+
22
+ def test_proxy
23
+ @store.setx('proxy', 'Foobar')
24
+ assert_equal 'Foobar', @store.getx('proxy')
25
+ end
26
+
27
+ def test_failing
28
+ assert_nothing_raised do
29
+ @store.getx('missing')
30
+ end
31
+ end
32
+
33
+ def test_failing_bang
34
+ assert_raises Errno::ENOATTR do
35
+ @store.getx!('missing')
36
+ end
37
+ end
38
+
39
+ def test_get_default
40
+ assert_equal 'default', @store.getx('remove') { 'default' }
41
+ end
42
+
43
+ def test_listing
44
+ @store.setx('listme', 'true')
45
+ @store.setx('me_too', 'true')
46
+ assert @store.listx.include?('listme')
47
+ assert @store.listx.include?('me_too')
48
+ end
49
+
50
+ def test_removal
51
+ @store.setx('hide', 'true')
52
+ @store.setx('seek', 'true')
53
+ @store.removex('hide')
54
+ assert !@store.listx.include?('hide')
55
+ assert @store.listx.include?('seek')
56
+ end
57
+
58
+ def test_remove_all
59
+ @store.setx('hide', 'true')
60
+ @store.setx('seek', 'true')
61
+ @store.remove_allx
62
+ assert !@store.listx.include?('hide')
63
+ assert !@store.listx.include?('seek')
64
+ end
65
+
66
+ def test_create_option
67
+ assert_nothing_raised do
68
+ @store.setx('createme', 'done!', :create)
69
+ end
70
+ assert_raise Errno::EEXIST do
71
+ @store.setx('createme', 'done!', :create)
72
+ end
73
+ end
74
+
75
+ def test_replace_option
76
+ assert_raise Errno::ENOATTR do
77
+ @store.setx('replaceme', 'done!', :replace)
78
+ end
79
+ @store.setx('replaceme', 'done!')
80
+ assert_nothing_raised do
81
+ @store.setx('replaceme', 'done!', :replace)
82
+ end
83
+ end
84
+
85
+ def test_link_nofollow
86
+ link_file = File.expand_path('./test_link', File.dirname(__FILE__))
87
+ ln_s __FILE__, link_file
88
+ link_store = Xtendr(link_file)
89
+ link_store.setx('dontfollow', 'justdont', :create, :no_follow)
90
+ assert_nil @store.getx('dontfollow')
91
+ assert_equal 'justdont', link_store.getx('dontfollow', :no_follow)
92
+ ensure
93
+ rm link_file
94
+ end
95
+
96
+ def test_link_follow
97
+ link_file = File.expand_path('./test_link', File.dirname(__FILE__))
98
+ ln_s __FILE__, link_file
99
+ link_store = Xtendr(link_file)
100
+ link_store.setx('dontfollow', 'justdont', :create)
101
+ assert_equal 'justdont', @store.getx('dontfollow')
102
+ assert_nil link_store.getx('dontfollow', :no_follow)
103
+ ensure
104
+ rm link_file
105
+ end
106
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xtendr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Louis J. Scoras
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-22 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: yard
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Xtendr provides access to extended file system attributes. Ruby 1.9 compatible.
26
+ email: louis.j.scoras@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - lib/xtendr.rb
42
+ - test/helper.rb
43
+ - test/test_xtendr.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/ljsc/xtendr
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: FFI binding for accessing extended file attributes.
72
+ test_files:
73
+ - test/helper.rb
74
+ - test/test_xtendr.rb