xtendr 0.1.0

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.
@@ -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