treequel 1.0.0 → 1.0.1

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,101 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rack'
4
+ require 'treequel'
5
+
6
+ ### A collection of Rack middleware for adding LDAP authentication/authorization
7
+ ### to Rack applications.
8
+ module LdapAuthentification
9
+
10
+ ### The simplest kind of authentication -- if the user provides credentials that allow
11
+ ### her to bind to the directory, access is granted.
12
+ class BindAuth
13
+
14
+ ### Create a new LdapAuthentication::Bind middleware that will wrap +app+. Supported options
15
+ ### are:
16
+ ###
17
+ ### url::
18
+ ### The LDAP URL (RFC4516) used to specify how to bind to the directory.
19
+ def initialize( app, options )
20
+ @app = app
21
+ @url = options[:url]
22
+ @dir, @attributes, @scope, @filter = self.split_url( @url )
23
+ end
24
+
25
+
26
+ ######
27
+ public
28
+ ######
29
+
30
+ ### Rack Interface: handle a request.
31
+ def call( env )
32
+ request = Rack::Request.new( env )
33
+ if self.can_bind?( request.params.values_at('uid','password') )
34
+ return @app.call( env )
35
+ else
36
+ return [
37
+ 401,
38
+ {
39
+ 'Content-Type' => 'text/plain',
40
+ 'Content-Length' => '0',
41
+ 'WWW-Authenticate' => www_authenticate.to_s
42
+ },
43
+ []
44
+ ]
45
+ end
46
+ end
47
+
48
+
49
+ #########
50
+ protected
51
+ #########
52
+
53
+ ### Parse the given +url+ into a Treequel::Directory, an Array of selection attributes,
54
+ ### a scope, and a filter for selecting users.
55
+ def split_url( url )
56
+ url = URI.parse( url )
57
+ directory = Treequel.directory( url )
58
+
59
+ parts = url.query.split( '?' )
60
+ attributes = self.normalize_attributes( parts.shift )
61
+ scope = self.normalize_scope( parts.shift )
62
+ filter = self.normalize_filter( parts.shift )
63
+
64
+ return directory, attributes, scope, filter
65
+ end
66
+
67
+
68
+ ### Parse the attributes in the given +attrlist+ and return an Array of Symbols suitable
69
+ ### for passing to Treequel::Branch#select.
70
+ def normalize_attributes( attrlist=nil )
71
+ return [] unless attrlist
72
+ return attrlist.split( /,/ ).
73
+ reject {|attrname| attrname !~ /^[a-z][^\w\-]$/i }.
74
+ collect {|attrname| attrname.untaint.to_sym }
75
+ end
76
+
77
+
78
+ ### Parse the scope from the given +scopename+ and return it as one of the Symbols:
79
+ ### [ :sub, :one, :base ]
80
+ def normalize_scope( scopename )
81
+ if scopename =~ /^(one|sub|base)/i
82
+ return $1.untaint.to_sym
83
+ else
84
+ raise ArgumentError, "Invalid scope %p" % [ scopename ]
85
+ end
86
+ end
87
+
88
+
89
+ ### Returns +true+ if the specified +user+ can bind to the directory
90
+ ### with the specified +password+.
91
+ def can_bind?( username, password )
92
+ filter = @filter.gsub( /:username/, username )
93
+ @user = @dir.scope( @scope.to_sym ).filter( filter )
94
+ @dir.bind( @user, password )
95
+ return true
96
+ rescue LDAP::ResultError => err
97
+ # How the hell do you log from Rack middleware?
98
+ return false
99
+ end
100
+
101
+ end # class BindAuth
@@ -332,6 +332,17 @@ class Treequel::Branchset
332
332
  end
333
333
 
334
334
 
335
+ ### Return a clone of the receiving Branchset that will return instances of the
336
+ ### give +branchclass+ instead of Treequel::Branch objects. This may be a subclass
337
+ ### of Treequel::Branch, but it doesn't need to be as long as they duck-type the
338
+ ### same.
339
+ def as( branchclass )
340
+ newset = self.clone
341
+ newset.branch = branchclass.new( self.branch.directory, self.branch.dn )
342
+ return newset
343
+ end
344
+
345
+
335
346
  # Hiding this until we figure out how to do server-side ordering (i.e.,
336
347
  # http://tools.ietf.org/html/rfc2891)
337
348
 
@@ -207,9 +207,7 @@ class Treequel::Directory
207
207
  end
208
208
 
209
209
 
210
- ### Bind as the specified +user_dn+ and +password+. If the optional +block+ is given,
211
- ### it will be executed with the receiver bound, then returned to its previous state when
212
- ### the block exits.
210
+ ### Bind as the specified +user_dn+ and +password+.
213
211
  def bind( user_dn, password )
214
212
  user_dn = user_dn.dn if user_dn.respond_to?( :dn )
215
213
 
@@ -490,7 +490,7 @@ class Treequel::Filter
490
490
  Treequel::Filter.new( :or, subfilters )
491
491
  else
492
492
  Treequel.logger.debug " value is a scalar; creating a single filter"
493
- Treequel::Filter.new( key, expr )
493
+ Treequel::Filter.new([ key.to_sym, expr ])
494
494
  end
495
495
  end
496
496
 
@@ -116,23 +116,7 @@ module Treequel # :nodoc:
116
116
  end # module Delegation
117
117
 
118
118
 
119
- #
120
119
  # Add logging to a Treequel class. Including classes get #log and #log_debug methods.
121
- #
122
- # == Version
123
- #
124
- # $Id$
125
- #
126
- # == Authors
127
- #
128
- # * Michael Granger <ged@FaerieMUD.org>
129
- #
130
- # :include: LICENSE
131
- #
132
- # --
133
- #
134
- # Please see the file LICENSE in the 'docs' directory for licensing details.
135
- #
136
120
  module Loggable
137
121
 
138
122
  LEVEL = {
@@ -319,6 +303,71 @@ module Treequel # :nodoc:
319
303
 
320
304
  end # module AttributeDeclarations
321
305
 
306
+
307
+ ### A collection of ANSI color utility functions
308
+ module ANSIColorUtilities
309
+
310
+ # Set some ANSI escape code constants (Shamelessly stolen from Perl's
311
+ # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
312
+ ANSI_ATTRIBUTES = {
313
+ 'clear' => 0,
314
+ 'reset' => 0,
315
+ 'bold' => 1,
316
+ 'dark' => 2,
317
+ 'underline' => 4,
318
+ 'underscore' => 4,
319
+ 'blink' => 5,
320
+ 'reverse' => 7,
321
+ 'concealed' => 8,
322
+
323
+ 'black' => 30, 'on_black' => 40,
324
+ 'red' => 31, 'on_red' => 41,
325
+ 'green' => 32, 'on_green' => 42,
326
+ 'yellow' => 33, 'on_yellow' => 43,
327
+ 'blue' => 34, 'on_blue' => 44,
328
+ 'magenta' => 35, 'on_magenta' => 45,
329
+ 'cyan' => 36, 'on_cyan' => 46,
330
+ 'white' => 37, 'on_white' => 47
331
+ }
332
+
333
+ ###############
334
+ module_function
335
+ ###############
336
+
337
+ ### Create a string that contains the ANSI codes specified and return it
338
+ def ansi_code( *attributes )
339
+ attributes.flatten!
340
+ attributes.collect! {|at| at.to_s }
341
+ return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
342
+ attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
343
+
344
+ if attributes.empty?
345
+ return ''
346
+ else
347
+ return "\e[%sm" % attributes
348
+ end
349
+ end
350
+
351
+
352
+ ### Colorize the given +string+ with the specified +attributes+ and return it, handling
353
+ ### line-endings, color reset, etc.
354
+ def colorize( *args )
355
+ string = ''
356
+
357
+ if block_given?
358
+ string = yield
359
+ else
360
+ string = args.shift
361
+ end
362
+
363
+ ending = string[/(\s)$/] || ''
364
+ string = string.rstrip
365
+
366
+ return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
367
+ end
368
+
369
+ end # module ANSIColorUtilities
370
+
322
371
  end # module Treequel
323
372
 
324
373
  # vim: set nosta noet ts=4 sw=4:
@@ -6,7 +6,7 @@ require 'bigdecimal'
6
6
  require 'date'
7
7
 
8
8
  require 'treequel'
9
- # require 'treequel/constants'
9
+ require 'treequel/mixins'
10
10
 
11
11
 
12
12
  module Treequel # :nodoc:
@@ -87,6 +87,81 @@ module Treequel # :nodoc:
87
87
  end # class LogFormatter
88
88
 
89
89
 
90
+ #
91
+ # A ANSI-colorized formatter for Logger instances.
92
+ #
93
+ # == Usage
94
+ #
95
+ # require 'treequel/utils'
96
+ # Treequel.logger.formatter = Treequel::ColorLogFormatter.new( Treequel.logger )
97
+ #
98
+ # == Version
99
+ #
100
+ # $Id$
101
+ #
102
+ # == Authors
103
+ #
104
+ # * Michael Granger <ged@FaerieMUD.org>
105
+ #
106
+ # :include: LICENSE
107
+ #
108
+ #--
109
+ #
110
+ # Please see the file LICENSE in the 'docs' directory for licensing details.
111
+ #
112
+ class ColorLogFormatter < Logger::Formatter
113
+ extend Treequel::ANSIColorUtilities
114
+
115
+ # Color settings
116
+ LEVEL_FORMATS = {
117
+ :debug => colorize( :bold, :black ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s {%6$s} -- %7$s\n"},
118
+ :info => colorize( :normal ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
119
+ :warn => colorize( :bold, :yellow ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
120
+ :error => colorize( :red ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
121
+ :fatal => colorize( :bold, :red, :on_white ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
122
+ }
123
+
124
+
125
+ ### Initialize the formatter with a reference to the logger so it can check for log level.
126
+ def initialize( logger, settings={} ) # :notnew:
127
+ settings = LEVEL_FORMATS.merge( settings )
128
+
129
+ @logger = logger
130
+ @settings = settings
131
+
132
+ super()
133
+ end
134
+
135
+ ######
136
+ public
137
+ ######
138
+
139
+ # The Logger object associated with the formatter
140
+ attr_accessor :logger
141
+
142
+ # The formats, by level
143
+ attr_accessor :settings
144
+
145
+
146
+ ### Log using the format associated with the severity
147
+ def call( severity, time, progname, msg )
148
+ args = [
149
+ time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
150
+ time.usec, # %2$d
151
+ Process.pid, # %3$d
152
+ Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
153
+ severity, # %5$s
154
+ progname, # %6$s
155
+ msg # %7$s
156
+ ]
157
+
158
+ return self.settings[ severity.downcase.to_sym ] % args
159
+ end
160
+ end # class LogFormatter
161
+
162
+
163
+
164
+
90
165
  #
91
166
  # An alternate formatter for Logger instances that outputs +div+ HTML
92
167
  # fragments.
data/lib/treequel.rb CHANGED
@@ -32,10 +32,10 @@ end
32
32
  module Treequel
33
33
 
34
34
  # Library version
35
- VERSION = '1.0.0'
35
+ VERSION = '1.0.1'
36
36
 
37
37
  # VCS revision
38
- REVISION = %q$rev: e352bc86498a $
38
+ REVISION = %q$rev: ca660bd12f7f $
39
39
 
40
40
  # Load the logformatters and some other stuff first
41
41
  require 'treequel/constants'
data/rake/manual.rb CHANGED
@@ -69,7 +69,7 @@ module Manual
69
69
  DEFAULT_CONFIG = {
70
70
  'filters' => [ 'erb', 'links', 'textile' ],
71
71
  'layout' => 'default.page',
72
- 'cleanup' => true,
72
+ 'cleanup' => false,
73
73
  }.freeze
74
74
 
75
75
  # Pattern to match a source page with a YAML header
data/rake/packaging.rb CHANGED
@@ -133,3 +133,12 @@ end
133
133
 
134
134
 
135
135
 
136
+ desc "Add development depdendencies to the gemspec -- this is meant to be chained " +
137
+ "together with :gem"
138
+ task :include_dev_dependencies do
139
+ DEVELOPMENT_DEPENDENCIES.each do |name, version|
140
+ version = '>= 0' if version.length.zero?
141
+ GEMSPEC.add_development_dependency( name, version )
142
+ end
143
+ end
144
+
data/rake/publishing.rb CHANGED
@@ -34,7 +34,7 @@ class Net::SMTP
34
34
 
35
35
  def do_ssl_start( helodomain, user, secret, authtype )
36
36
  raise IOError, 'SMTP session already started' if @started
37
- check_auth_args user, secret, authtype if user or secret
37
+ check_auth_args user, secret if user or secret
38
38
 
39
39
  # Open the connection
40
40
  @debug_output << "opening connection to #{@address}...\n" if @debug_output
@@ -238,7 +238,7 @@ begin
238
238
 
239
239
  desc 'Publish the new release to RubyForge'
240
240
  task :publish => [:clean, :package, :notes] do |task|
241
- project = GEMSPEC.rubyforge_project
241
+ project = RUBYFORGE_PROJECT
242
242
 
243
243
  if $publish_privately
244
244
  log "Skipping push of release files to RubyForge"
@@ -346,6 +346,17 @@ describe Treequel::Branchset do
346
346
  @branchset.all.should == [:matching_branches]
347
347
  end
348
348
 
349
+ #
350
+ # #as
351
+ #
352
+ it "can create a new branchset cloned from itself that will return instances of a different branch class" do
353
+ subclass = Class.new( Treequel::Branch )
354
+ @branch.stub!( :directory ).and_return( :the_directory )
355
+ @branch.stub!( :dn ).and_return( TEST_HOSTS_DN )
356
+ newset = @branchset.as( subclass )
357
+ newset.branch.should be_an_instance_of( subclass )
358
+ end
359
+
349
360
  end
350
361
 
351
362
  describe "instance with no filter, and scope set to 'onelevel'" do
@@ -71,6 +71,10 @@ describe Treequel::Filter do
71
71
  Treequel::Filter.new( :uid, 'bigthung' ).to_s.should == '(uid=bigthung)'
72
72
  end
73
73
 
74
+ it "parses a String+value hash as a simple item equal filter" do
75
+ Treequel::Filter.new( 'uid' => 'bigthung' ).to_s.should == '(uid=bigthung)'
76
+ end
77
+
74
78
  it "parses a single-item Symbol+value hash as a simple item equal filter" do
75
79
  Treequel::Filter.new({ :uidNumber => 3036 }).to_s.should == '(uidNumber=3036)'
76
80
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: treequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Granger, Mahlon E. Smith
@@ -9,9 +9,39 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-13 00:00:00 -07:00
12
+ date: 2009-11-19 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: columnize
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.3.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: termios
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.4
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: ruby-terminfo
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.1.1
44
+ version:
15
45
  - !ruby/object:Gem::Dependency
16
46
  name: ruby-ldap
17
47
  type: :runtime
@@ -27,9 +57,10 @@ description: |-
27
57
  the impedence mismatch of trying to treat LDAP as if it's a relational database, and
28
58
  instead embrace its hierarchical, free-form nature.
29
59
  email:
30
- - ged@FaerieMUD.org
31
60
  - mahlon@martini.nu
61
+ - ged@FaerieMUD.org
32
62
  executables:
63
+ - treequel.orig
33
64
  - treeirb
34
65
  - treequel
35
66
  extensions: []
@@ -43,74 +74,76 @@ files:
43
74
  - ChangeLog
44
75
  - README
45
76
  - LICENSE
46
- - spec/treequel/branch_spec.rb
47
- - spec/treequel/branchcollection_spec.rb
48
- - spec/treequel/branchset_spec.rb
49
- - spec/treequel/directory_spec.rb
50
- - spec/treequel/filter_spec.rb
51
- - spec/treequel/mixins_spec.rb
77
+ - spec/treequel_spec.rb
52
78
  - spec/treequel/schema/attributetype_spec.rb
53
79
  - spec/treequel/schema/ldapsyntax_spec.rb
54
- - spec/treequel/schema/matchingrule_spec.rb
55
80
  - spec/treequel/schema/matchingruleuse_spec.rb
81
+ - spec/treequel/schema/matchingrule_spec.rb
56
82
  - spec/treequel/schema/objectclass_spec.rb
57
83
  - spec/treequel/schema_spec.rb
84
+ - spec/treequel/filter_spec.rb
58
85
  - spec/treequel/utils_spec.rb
59
- - spec/treequel_spec.rb
86
+ - spec/treequel/branch_spec.rb
87
+ - spec/treequel/directory_spec.rb
88
+ - spec/treequel/branchset_spec.rb
89
+ - spec/treequel/mixins_spec.rb
90
+ - spec/treequel/branchcollection_spec.rb
60
91
  - spec/lib/constants.rb
61
92
  - spec/lib/helpers.rb
93
+ - bin/treequel.orig
62
94
  - bin/treeirb
63
95
  - bin/treequel
64
- - lib/treequel/branch.rb
65
- - lib/treequel/branchcollection.rb
66
- - lib/treequel/branchset.rb
67
- - lib/treequel/constants.rb
68
- - lib/treequel/directory.rb
69
- - lib/treequel/exceptions.rb
70
- - lib/treequel/filter.rb
71
- - lib/treequel/mixins.rb
96
+ - lib/treequel.rb
97
+ - lib/treequel/schema/matchingrule.rb
72
98
  - lib/treequel/schema/attributetype.rb
73
99
  - lib/treequel/schema/ldapsyntax.rb
74
- - lib/treequel/schema/matchingrule.rb
75
100
  - lib/treequel/schema/matchingruleuse.rb
76
101
  - lib/treequel/schema/objectclass.rb
102
+ - lib/treequel/directory.rb
103
+ - lib/treequel/exceptions.rb
104
+ - lib/treequel/branchcollection.rb
105
+ - lib/treequel/mixins.rb
106
+ - lib/treequel/utils.rb
77
107
  - lib/treequel/schema.rb
108
+ - lib/treequel/constants.rb
109
+ - lib/treequel/branchset.rb
110
+ - lib/treequel/filter.rb
111
+ - lib/treequel/branch.rb
78
112
  - lib/treequel/sequel_integration.rb
79
- - lib/treequel/utils.rb
80
- - lib/treequel.rb
81
- - rake/191_compat.rb
82
- - rake/dependencies.rb
83
- - rake/helpers.rb
113
+ - rake/style.rb
114
+ - rake/rdoc.rb
84
115
  - rake/hg.rb
85
- - rake/manual.rb
116
+ - rake/svn.rb
86
117
  - rake/packaging.rb
87
118
  - rake/publishing.rb
88
- - rake/rdoc.rb
89
- - rake/style.rb
90
- - rake/svn.rb
91
- - rake/testing.rb
92
- - rake/verifytask.rb
119
+ - rake/191_compat.rb
120
+ - rake/dependencies.rb
93
121
  - rake/win32.rb
122
+ - rake/manual.rb
123
+ - rake/verifytask.rb
124
+ - rake/helpers.rb
125
+ - rake/testing.rb
126
+ - ./examples/ldap_state.rb
94
127
  - ./examples/company-directory.rb
95
128
  - ./examples/ldap-monitor.rb
96
- - ./examples/ldap_state.rb
129
+ - ./examples/ldap-rack-auth.rb
97
130
  - ./examples/ldap-monitor/views/backends.erb
98
131
  - ./examples/ldap-monitor/views/connections.erb
132
+ - ./examples/ldap-monitor/views/listeners.erb
99
133
  - ./examples/ldap-monitor/views/databases.erb
100
- - ./examples/ldap-monitor/views/dump_subsystem.erb
101
134
  - ./examples/ldap-monitor/views/index.erb
135
+ - ./examples/ldap-monitor/views/dump_subsystem.erb
102
136
  - ./examples/ldap-monitor/views/layout.erb
103
- - ./examples/ldap-monitor/views/listeners.erb
104
137
  - ./examples/ldap-monitor/public/css/master.css
105
- - ./examples/ldap-monitor/public/images/card_small.png
106
- - ./examples/ldap-monitor/public/images/chain_small.png
107
- - ./examples/ldap-monitor/public/images/globe_small.png
108
- - ./examples/ldap-monitor/public/images/globe_small_green.png
109
138
  - ./examples/ldap-monitor/public/images/plug.png
110
139
  - ./examples/ldap-monitor/public/images/shadows/large-30-down.png
111
- - ./examples/ldap-monitor/public/images/tick.png
140
+ - ./examples/ldap-monitor/public/images/globe_small.png
141
+ - ./examples/ldap-monitor/public/images/chain_small.png
112
142
  - ./examples/ldap-monitor/public/images/tick_circle.png
143
+ - ./examples/ldap-monitor/public/images/globe_small_green.png
144
+ - ./examples/ldap-monitor/public/images/card_small.png
113
145
  - ./examples/ldap-monitor/public/images/treequel-favicon.png
146
+ - ./examples/ldap-monitor/public/images/tick.png
114
147
  - Rakefile.local
115
148
  has_rdoc: true
116
149
  homepage: http://deveiate.org/projects/Treequel
@@ -145,25 +178,25 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
178
  version:
146
179
  requirements:
147
180
  - ldap >=0
148
- rubyforge_project: treequel
181
+ rubyforge_project: deveiate
149
182
  rubygems_version: 1.3.5
150
183
  signing_key:
151
184
  specification_version: 3
152
185
  summary: An honest LDAP library
153
186
  test_files:
154
- - spec/treequel/branch_spec.rb
155
- - spec/treequel/branchcollection_spec.rb
156
- - spec/treequel/branchset_spec.rb
157
- - spec/treequel/directory_spec.rb
158
- - spec/treequel/filter_spec.rb
159
- - spec/treequel/mixins_spec.rb
187
+ - spec/treequel_spec.rb
160
188
  - spec/treequel/schema/attributetype_spec.rb
161
189
  - spec/treequel/schema/ldapsyntax_spec.rb
162
- - spec/treequel/schema/matchingrule_spec.rb
163
190
  - spec/treequel/schema/matchingruleuse_spec.rb
191
+ - spec/treequel/schema/matchingrule_spec.rb
164
192
  - spec/treequel/schema/objectclass_spec.rb
165
193
  - spec/treequel/schema_spec.rb
194
+ - spec/treequel/filter_spec.rb
166
195
  - spec/treequel/utils_spec.rb
167
- - spec/treequel_spec.rb
196
+ - spec/treequel/branch_spec.rb
197
+ - spec/treequel/directory_spec.rb
198
+ - spec/treequel/branchset_spec.rb
199
+ - spec/treequel/mixins_spec.rb
200
+ - spec/treequel/branchcollection_spec.rb
168
201
  - spec/lib/constants.rb
169
202
  - spec/lib/helpers.rb