├── LICENSE ├── Manifest ├── README.textile ├── Rakefile ├── lib └── ambition │ └── adapters │ ├── active_ldap.rb │ └── active_ldap │ ├── base.rb │ ├── query.rb │ └── select.rb └── test ├── enumerable_test.rb ├── helper.rb ├── integration_test.rb └── select_test.rb /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007 Chris Wanstrath & Matthew King 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /Manifest: -------------------------------------------------------------------------------- 1 | lib/ambition/adapters/active_ldap/base.rb 2 | lib/ambition/adapters/active_ldap/query.rb 3 | lib/ambition/adapters/active_ldap/select.rb 4 | lib/ambition/adapters/active_ldap.rb 5 | LICENSE 6 | Manifest 7 | README 8 | test/enumerable_test.rb 9 | test/helper.rb 10 | test/integration_test.rb 11 | test/select_test.rb 12 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h2. An Ambitious ActiveLdap Adapter 2 | 3 | Write LDAP search filters in Ruby. Awesomeness courtesy of Chris Wanstrath's Ambition library. 4 | 5 | You must be using ActiveLdap 0.9 or greater. 6 | 7 | h2. Get It 8 | 9 | @$ sudo gem install ambitious-activeldap@ 10 | 11 | 12 |
13 | require 'rubygems'
14 | require 'ambition/adapters/active_ldap'
15 | 
16 | 17 | h2. Examples 18 | 19 | Let @User@ be a subclass of @ActiveLdap::Base@ 20 | 21 | 22 | User.select { |u| u.givenName == 'Severian' }.each do |user| 23 | puts user.name 24 | end 25 | 26 | 27 | Queries don't run until you kick them. This adapter supports only the basic kicker methods: @each@, @each_with_index@, @map@, @entries@, @to_a@. 28 | 29 | This adapter also doesn't support the methods that require sorting, slicing, or counting, because OpenLDAP effectively doesn't support such things. ActiveLdap lets you request sorted and sliced result sets, but it does the work in Ruby, so you might as well do it yourself. 30 | 31 | You can call @to_s@, but not @to_hash@ on a query. @to_s@ returns the LDAP search filter as a string. 32 | 33 | 34 | >> User.select { |m| m.givenName == 'Jonas' }.to_s 35 | => "(givenName=Jonas)" 36 | 37 | 38 | 39 | 40 | h2. Issues.find(:all, :filter => "(type=bug)") 41 | 42 | Found a bug? Sweet. Add it at "the Lighthouse":http://err.lighthouseapp.com/projects/466-plugins/tickets/new. 43 | 44 | More information on Ambition: 45 | 46 | * "http://ambition.rubyforge.org":http://ambition.rubyforge.org 47 | * "http://groups.google.com/group/ambition-rb/":http://groups.google.com/group/ambition-rb/ 48 | 49 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | 3 | Version = '0.1.1' 4 | 5 | begin 6 | require 'rubygems' 7 | gem 'echoe', '>=2.7' 8 | ENV['RUBY_FLAGS'] = "" 9 | require 'echoe' 10 | 11 | Echoe.new('ambitious-activeldap') do |p| 12 | p.dependencies << 'activeldap >=0.9' 13 | p.summary = "An ambitious adapter for ActiveLDAP" 14 | p.author = 'Matthew King' 15 | p.email = "automatthew@gmail.com" 16 | 17 | p.project = 'ambition' 18 | p.url = "http://ambition.rubyforge.org/" 19 | p.test_pattern = 'test/*_test.rb' 20 | p.version = Version 21 | p.dependencies << 'ambition >=0.5.0' 22 | end 23 | 24 | rescue LoadError 25 | puts "Not doing any of the Echoe gemmy stuff, because you don't have the specified gem versions" 26 | end 27 | 28 | desc 'Install as a gem' 29 | task :install_gem do 30 | puts `rake manifest package && gem install pkg/ambitious-activeldap-#{Version}.gem` 31 | end 32 | -------------------------------------------------------------------------------- /lib/ambition/adapters/active_ldap.rb: -------------------------------------------------------------------------------- 1 | require 'ambition' 2 | require 'active_ldap' 3 | require 'ambition/adapters/active_ldap/query' 4 | require 'ambition/adapters/active_ldap/base' 5 | require 'ambition/adapters/active_ldap/select' 6 | 7 | ActiveLdap::Base.extend Ambition::API 8 | ActiveLdap::Base.ambition_adapter = Ambition::Adapters::ActiveLdap 9 | -------------------------------------------------------------------------------- /lib/ambition/adapters/active_ldap/base.rb: -------------------------------------------------------------------------------- 1 | module Ambition 2 | module Adapters 3 | module ActiveLdap 4 | class Base 5 | def sanitize(object) 6 | case object 7 | when true then 'TRUE' 8 | when false then 'FALSE' 9 | else object.to_s 10 | end 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/ambition/adapters/active_ldap/query.rb: -------------------------------------------------------------------------------- 1 | module Ambition 2 | module Adapters 3 | module ActiveLdap 4 | class Query 5 | 6 | def kick 7 | options = {} 8 | string = to_s 9 | options[:filter] = string unless string.empty? 10 | owner.find(:all, options) 11 | end 12 | 13 | def size 14 | raise "Not Implemented" 15 | end 16 | 17 | def to_s 18 | Array(clauses[:select]).join 19 | clauses[:select].first.to_s 20 | end 21 | 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/ambition/adapters/active_ldap/select.rb: -------------------------------------------------------------------------------- 1 | module Ambition 2 | module Adapters 3 | module ActiveLdap 4 | class Select < Base 5 | def call(*methods) 6 | method = methods.first.to_s 7 | if method[-1] == ?? 8 | "(#{method[0...-1]}=#{sanitize true})" 9 | else 10 | method 11 | end 12 | end 13 | 14 | def chained_call(*methods) 15 | call(*methods) 16 | end 17 | 18 | def both(left, right) 19 | "(&#{left}#{sanitize right})" 20 | end 21 | 22 | def either(left, right) 23 | "(|#{left}#{sanitize right})" 24 | end 25 | 26 | def ==(left, right) 27 | "(#{left}=#{sanitize right})" 28 | end 29 | 30 | # != 31 | def not_equal(left, right) 32 | "(!(#{left}=#{sanitize right}))" 33 | end 34 | 35 | def =~(left, right) 36 | end 37 | 38 | # !~ 39 | def not_regexp(left, right) 40 | end 41 | 42 | def <(left, right) 43 | self.<=(left, right) 44 | end 45 | 46 | def >(left, right) 47 | self.>=(left, right) 48 | end 49 | 50 | def >=(left, right) 51 | "(#{left}>=#{sanitize right})" 52 | end 53 | 54 | def <=(left, right) 55 | "(#{left}<=#{sanitize right})" 56 | end 57 | 58 | def include?(left, right) 59 | bits = left.map { |item| "(#{right}=#{item})" } 60 | "(|#{bits})" 61 | end 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /test/enumerable_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/helper' 2 | 3 | context "Each" do 4 | specify "simple ==" do 5 | hash = { :filter => "(uid=mking)" } 6 | LDAPUser.expects(:find).with(:all, hash).returns([]) 7 | LDAPUser.select { |m| m.uid == 'mking' }.each do |user| 8 | puts user.cn 9 | end 10 | end 11 | 12 | xspecify "limit and conditions" do 13 | hash = { :limit => 5, :filter => "(uid=mking)" } 14 | LDAPUser.expects(:find).with(:all, hash).returns([]) 15 | LDAPUser.select { |m| m.uid == 'mking' }.first(5).each do |user| 16 | puts user.cn 17 | end 18 | end 19 | 20 | xspecify "limit and conditions and order" do 21 | hash = { :limit => 5, :filter => "(uid=mking)", :sort_by => 'sn' } 22 | LDAPUser.expects(:find).with(:all, hash).returns([]) 23 | LDAPUser.select { |m| m.uid == 'mking' }.sort_by { |m| m.sn }.first(5).each do |user| 24 | puts user.cn 25 | end 26 | end 27 | 28 | xspecify "limit and order" do 29 | hash = { :limit => 5, :sort_by => 'sn' } 30 | LDAPUser.expects(:find).with(:all, hash).returns([]) 31 | LDAPUser.sort_by { |m| m.sn }.first(5).each do |user| 32 | puts user.name 33 | end 34 | end 35 | end 36 | 37 | context "Enumerable Methods" do 38 | specify "map" do 39 | hash = { :filter => "(uid=mking)" } 40 | LDAPUser.expects(:find).with(:all, hash).returns([]) 41 | LDAPUser.select { |m| m.uid == 'mking' }.map { |u| u.name } 42 | end 43 | 44 | specify "each_with_index" do 45 | hash = { :filter => "(uid=mking)" } 46 | LDAPUser.expects(:find).with(:all, hash).returns([]) 47 | LDAPUser.select { |m| m.uid == 'mking' }.each_with_index do |user, i| 48 | puts "#{i}: #{user.name}" 49 | end 50 | end 51 | 52 | # specify "any?" do 53 | # LDAPUser.expects(:count).with(:conditions => "users.age > 21").returns(1) 54 | # LDAPUser.any? { |u| u.age > 21 }.should == true 55 | # end 56 | 57 | # specify "all?" do 58 | # LDAPUser.expects(:count).at_least_once.returns(10, 20) 59 | # LDAPUser.all? { |u| u.age > 21 }.should == false 60 | # 61 | # LDAPUser.expects(:count).at_least_once.returns(10, 10) 62 | # LDAPUser.all? { |u| u.age > 21 }.should == true 63 | # end 64 | # 65 | # specify "empty?" do 66 | # LDAPUser.expects(:count).with(:conditions => "users.age > 21").returns(1) 67 | # LDAPUser.select { |u| u.age > 21 }.empty?.should.equal false 68 | # 69 | # LDAPUser.expects(:count).with(:conditions => "users.age > 21").returns(0) 70 | # LDAPUser.select { |u| u.age > 21 }.empty?.should.equal true 71 | # end 72 | # 73 | specify "entries" do 74 | LDAPUser.expects(:find).with(:all, {}) 75 | LDAPUser.entries 76 | 77 | hash = { :filter => "(uid=mking)" } 78 | LDAPUser.expects(:find).with(:all, hash).returns([]) 79 | LDAPUser.select { |m| m.uid == 'mking' }.entries 80 | end 81 | 82 | specify "to_a" do 83 | LDAPUser.expects(:find).with(:all, {}) 84 | LDAPUser.to_a 85 | end 86 | # 87 | # xspecify "each_slice" do 88 | # end 89 | # 90 | # xspecify "max" do 91 | # end 92 | # 93 | # xspecify "min" do 94 | # end 95 | end 96 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | %w( rubygems test/spec mocha redgreen English ).each { |f| require f } 2 | 3 | $LOAD_PATH.unshift *[ File.dirname(__FILE__) + '/../lib', File.dirname(__FILE__) + '/../../../lib' ] 4 | require 'ambition/adapters/active_ldap' 5 | 6 | ActiveLdap::Base.class_eval do 7 | def self.find(*args) 8 | 'dummy find method' 9 | end 10 | end 11 | 12 | class LDAPUser < ActiveLdap::Base 13 | def self.table_name 14 | # in real life would call the base class method on the ActiveLdap model class 15 | 'ou=people,dc=automatthew,dc=com' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/integration_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/helper' 2 | 3 | context "LDAP model after including LDAP Adapter" do 4 | specify "should still have original find method" do 5 | LDAPUser.find(:all).should == "dummy find method" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/select_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/helper' 2 | 3 | context "LDAP Adapter :: Select" do 4 | context "Filter (using select)" do 5 | specify "simple ==" do 6 | filter = LDAPUser.select { |m| m.name == 'jon' }.to_s 7 | filter.should == "(name=jon)" 8 | end 9 | 10 | specify "simple !=" do 11 | filter = LDAPUser.select { |m| m.name != 'jon' }.to_s 12 | filter.should == "(!(name=jon))" 13 | end 14 | 15 | specify "simple == && ==" do 16 | filter = LDAPUser.select { |m| m.name == 'jon' && m.age == 21 }.to_s 17 | filter.should == "(&(name=jon)(age=21))" 18 | end 19 | 20 | specify "simple == || ==" do 21 | filter = LDAPUser.select { |m| m.name == 'jon' || m.age == 21 }.to_s 22 | filter.should == "(|(name=jon)(age=21))" 23 | end 24 | 25 | specify "mixed && and ||" do 26 | filter = LDAPUser.select { |m| m.name == 'jon' || m.age == 21 && m.password == 'pass' }.to_s 27 | filter.should == "(|(name=jon)(&(age=21)(password=pass)))" 28 | end 29 | 30 | specify "grouped && and ||" do 31 | filter = LDAPUser.select { |m| (m.name == 'jon' || m.name == 'rick') && m.age == 21 }.to_s 32 | filter.should == "(&(|(name=jon)(name=rick))(age=21))" 33 | end 34 | 35 | specify "simple >/<" do 36 | # LDAP apparently only supports >= and <= 37 | filter = LDAPUser.select { |m| m.age > 21 }.to_s 38 | filter.should == "(age>=21)" 39 | 40 | filter = LDAPUser.select { |m| m.age >= 21 }.to_s 41 | filter.should == "(age>=21)" 42 | 43 | filter = LDAPUser.select { |m| m.age < 21 }.to_s 44 | filter.should == "(age<=21)" 45 | 46 | filter = LDAPUser.select { |m| m.age <= 21 }.to_s 47 | filter.should == "(age<=21)" 48 | end 49 | 50 | specify "array.include? item" do 51 | filter = LDAPUser.select { |m| [1, 2, 3, 4].include? m.id }.to_s 52 | # I'm not sure whether this is idiomatic, but it works. 53 | filter.should == "(|(id=1)(id=2)(id=3)(id=4))" 54 | end 55 | 56 | specify "variable'd array.include? item" do 57 | array = [1, 2, 3, 4] 58 | filter = LDAPUser.select { |m| array.include? m.id }.to_s 59 | filter.should == "(|(id=1)(id=2)(id=3)(id=4))" 60 | end 61 | 62 | specify "simple == with variables" do 63 | me = 'chris' 64 | filter = LDAPUser.select { |m| m.name == me }.to_s 65 | filter.should == "(name=#{me})" 66 | end 67 | 68 | specify "simple == with true" do 69 | filter = LDAPUser.select { |m| m.disabled == true }.to_s 70 | filter.should == "(disabled=TRUE)" 71 | end 72 | 73 | xspecify "implicit true" do 74 | filter = LDAPUser.select { |m| m.disabled }.to_s 75 | filter.should == "(disabled=TRUE)" 76 | end 77 | 78 | specify "predicate method" do 79 | filter = LDAPUser.select { |m| m.disabled? }.to_s 80 | filter.should == "(disabled=TRUE)" 81 | end 82 | end 83 | end 84 | --------------------------------------------------------------------------------