lom 0.9.0 → 0.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 383b6cdf0a55888e40558ca5b24f46a7f5e5d033f27fc873e4625c42b385c571
4
- data.tar.gz: cef1eef2f7ee9b2b39118e9f54b2d15f1ba5d35518ed04e98cb09a047cbbb34e
3
+ metadata.gz: 6d37a56b9ef6c9dad3ae785439cb49560c641304fdb12f3d3d070541e8bf27b9
4
+ data.tar.gz: cd71e20fd089ab2b489bcdb2f857f508b8c283c0d4f2de875b81cfc722fab05b
5
5
  SHA512:
6
- metadata.gz: dd9eab41714ae145477e2756300b0041dd2d0a12f90485a3997e95266bbb121a33e8a3ed3660d8fd14e7372fd0cfa0fca6da6f800f61599a1c995da221b67b4f
7
- data.tar.gz: f435226cb86088cfa48db588b105396b79fc0172eec4a967168b507dffb1039f201f6d7261d371edf63883c7fe519ca0d6167c649259f282a7d38e592ee929e1
6
+ metadata.gz: 956540737ca9944e790aeead959ef079f61f8d9469b22867969858c993bd97897f742fd9ca43ef3b99110e76e81902474f88fd6a95e0bd216fee0b620ca5377e
7
+ data.tar.gz: 7340668aaeef6a7821e88d95bf5da772aedfcb4a3dce9ab89d858c08c2b4be9165e127f0c5871decce112edc06337ba0c7e611601d12b56a6cbfe5b52e490202
data/README.md CHANGED
@@ -104,7 +104,13 @@ class User < Dry::Struct
104
104
  }
105
105
  end
106
106
 
107
+ ldap_list :query, ->(str) do
108
+ Filtered.match(:uid, str) |
109
+ Filtered.match(:cn, str) |
110
+ Filtered.match(:givenName, str) | Filtered.match(:sn, str)
111
+ end
107
112
 
113
+
108
114
  #
109
115
  # Object structure
110
116
  #
data/lib/lom/filtered.rb CHANGED
@@ -13,10 +13,19 @@ class Filtered
13
13
  NONE = Object.new.freeze
14
14
  ANY = Object.new.freeze
15
15
 
16
- def initialize(src, filter = nil, paged: nil)
17
- @src = src
18
- @filter = filter
19
- @paged = paged
16
+ class NoSource < Error
17
+ end
18
+
19
+ def initialize(filter = nil, src: nil, paged: nil)
20
+ if filter.is_a?(Filtered)
21
+ @filter = filter.filter
22
+ @src = src || filter.src
23
+ @paged = paged || filter.paged
24
+ else
25
+ @filter = filter
26
+ @src = src
27
+ @paged = paged
28
+ end
20
29
  end
21
30
  attr_reader :src, :filter, :paged
22
31
 
@@ -30,7 +39,7 @@ class Filtered
30
39
  _operator_2('&', o)
31
40
  end
32
41
 
33
- # Take the negation of this fileter
42
+ # Take the negation of this filter
34
43
  def ~@
35
44
  _operator_1('!')
36
45
  end
@@ -51,24 +60,26 @@ class Filtered
51
60
  end
52
61
 
53
62
  # Iterate over matching data
54
- def each(*args, &block)
55
- @src.each(*args, filter: @filter, paged: self.paged, &block)
63
+ def each(*args, rawfilter: nil, &block)
64
+ raise NoSource if @src.nil?
65
+ @src.each(*args, filter: @filter, rawfilter: rawfilter,
66
+ paged: self.paged, &block)
56
67
  end
57
68
 
58
69
  # Retrieve matching data as a list of object
59
70
  #
60
71
  # @return [Array<Object>]
61
72
  #
62
- def all
63
- each(:object).to_a
73
+ def all(&rawfilter)
74
+ each(:object, rawfilter: rawfilter).to_a
64
75
  end
65
76
 
66
77
  # Retrieve matching data as a list of id
67
78
  #
68
79
  # @return [Array<String>]
69
80
  #
70
- def list
71
- each(:id).to_a
81
+ def list(&rawfilter)
82
+ each(:id, rawfilter: rawfilter).to_a
72
83
  end
73
84
 
74
85
  # Escape (and convert) a value for correct processing.
@@ -89,20 +100,20 @@ class Filtered
89
100
 
90
101
  # Test if an attribute exists
91
102
  def self.exists(attr, predicate: true)
92
- case predicate
93
- when true, nil then "(#{attr}=*)"
94
- when false, :none then "(!(#{attr}=*))"
95
- else raise ArgumentError
96
- end
103
+ self.new(case predicate
104
+ when true, nil then "(#{attr}=*)"
105
+ when false, :none then "(!(#{attr}=*))"
106
+ else raise ArgumentError
107
+ end)
97
108
  end
98
109
 
99
110
  # Test if an attribute is of the specified value
100
111
  def self.is(attr, val, predicate: true)
101
- case predicate
102
- when true, nil then "(#{attr}=#{escape(val)})"
103
- when false then "(!(#{attr}=#{escape(val)}))"
104
- else raise ArgumentError
105
- end
112
+ self.new(case predicate
113
+ when true, nil then "(#{attr}=#{escape(val)})"
114
+ when false then "(!(#{attr}=#{escape(val)}))"
115
+ else raise ArgumentError
116
+ end)
106
117
  end
107
118
 
108
119
  # Test if an attribute has the specified value.
@@ -110,11 +121,20 @@ class Filtered
110
121
  def self.has(attr, val)
111
122
  val = yield(val) if block_given?
112
123
 
113
- case val
114
- when ANY then "(#{attr}=*)"
115
- when NONE then "(!(#{attr}=*))"
116
- else "(#{attr}=#{escape(val)})"
117
- end
124
+ self.new(case val
125
+ when ANY then "(#{attr}=*)"
126
+ when NONE then "(!(#{attr}=*))"
127
+ else "(#{attr}=#{escape(val)})"
128
+ end)
129
+ end
130
+
131
+ # Test if an attribute match the specified value
132
+ def self.match(attr, val, predicate: true)
133
+ self.new(case predicate
134
+ when true, nil then "(#{attr}=*#{escape(val)}*)"
135
+ when false then "(!(#{attr}=*#{escape(val)}*))"
136
+ else raise ArgumentError
137
+ end)
118
138
  end
119
139
 
120
140
  # Test if an attribute as a time before the specified timestamp
@@ -122,7 +142,7 @@ class Filtered
122
142
  def self.before(attr, ts, predicate: true)
123
143
  ts = Date.today + ts if ts.kind_of?(Integer)
124
144
  ts = LOM.to_ldap_time(ts)
125
- "(#{attr}<=#{ts})".then {|f| predicate ? f : "(!#{f})" }
145
+ self.new("(#{attr}<=#{ts})".then {|f| predicate ? f : "(!#{f})" })
126
146
  end
127
147
 
128
148
  # Test if an attribute as a time after the specified timestamp
@@ -130,40 +150,40 @@ class Filtered
130
150
  def self.after(attr, ts, predicate: true)
131
151
  ts = Date.today - ts if ts.kind_of?(Integer)
132
152
  ts = LOM.to_ldap_time(ts)
133
- "(#{attr}>=#{ts})".then {|f| predicate ? f : "(!#{f})" }
153
+ self.new("(#{attr}>=#{ts})".then {|f| predicate ? f : "(!#{f})" })
134
154
  end
135
155
 
136
156
  private
137
157
 
138
158
  # Operation with 2 elements
139
159
  def _operator_2(op, o)
140
- if @src != o.src
160
+ if !@src.nil? && !o.src.nil? && @src != o.src
141
161
  raise ArgumentError, 'filter defined with different sources'
142
162
  end
143
163
  _filter = if !@filter.nil? && !o.filter.nil?
144
164
  then Net::LDAP.filter(op, @filter, o.filter)
145
165
  else @filter || o.filter
146
166
  end
147
- Filtered.new(@src, _filter, paged: o.paged || self.paged)
167
+ Filtered.new(_filter, src: @src || o.src, paged: o.paged || self.paged)
148
168
  end
149
169
 
150
170
  # Operation with 1 element
151
171
  def _operator_1(op)
152
- Filtered.new(@src, Net::LDAP.filter(op, @filter),
172
+ Filtered.new(Net::LDAP.filter(op, @filter), src: @src,
153
173
  paged: self.paged)
154
174
  end
155
175
 
156
176
  # Check if an ldap_list has been defined with that name
157
177
  def respond_to_missing?(method_name, include_private = false)
178
+ return super if @src.nil?
158
179
  @src.ldap_listing.include?(method_name) || super
159
180
  end
160
181
 
161
182
  # Call the ldap_list defined with that name
162
183
  def method_missing(method_name, *args, &block)
163
- if @src.ldap_listing.include?(method_name)
164
- self & @src.send(method_name, *args, &block)
165
- else
166
- super
184
+ if @src&.ldap_listing.include?(method_name)
185
+ then self & @src.send(method_name, *args, &block)
186
+ else super
167
187
  end
168
188
  end
169
189
 
@@ -188,7 +188,7 @@ module Extensions
188
188
  if LOM.debug.include?(:dry)
189
189
  return true
190
190
  end
191
- return true if op.empty? # That's a no-op
191
+ return true if ops.empty? # That's a no-op
192
192
  modify(:dn => dn, :operations => ops) # Apply modifications
193
193
  end
194
194
  end
data/lib/lom/mapper.rb CHANGED
@@ -37,7 +37,8 @@ module InstanceMethods
37
37
  # @return [true, false]
38
38
  #
39
39
  def save!
40
- attrs = instance_exec(self, &self.class._ldap_to)
40
+ model = self.class
41
+ attrs = instance_exec(self, &model.ldap_to)
41
42
  .transform_values {|v|
42
43
  # Don't use Array(), not what you think on
43
44
  # some classes such as Time
@@ -45,9 +46,9 @@ module InstanceMethods
45
46
  v = [ v ] unless v.is_a?(Array)
46
47
  v.to_ldap
47
48
  }
48
- id, _ = Array(attrs[self.class._ldap_prefix])
49
+ id, _ = Array(attrs[model.ldap_prefix])
49
50
  raise MappingError, 'prefix for dn has multiple values' if _
50
- dn = self.class.ldap_dn_from_id(id)
51
+ dn = model.ldap_dn_from_id(id)
51
52
 
52
53
  lh.update(dn: dn, attributes: attrs).then {|res|
53
54
  break res unless res.nil?
@@ -101,7 +102,10 @@ module Mapper
101
102
  @__ldap_lh = lh
102
103
  end
103
104
 
104
-
105
+ # Return the list of defined list (using ldap_list).
106
+ #
107
+ # @return [Array<Symbol>] list of defined ldap list
108
+ #
105
109
  def ldap_listing
106
110
  @__ldap_list
107
111
  end
@@ -118,16 +122,18 @@ module Mapper
118
122
  @__ldap_list << name
119
123
  define_singleton_method(name) do |*args|
120
124
  filter = body.call(*args)
121
- LOM::Filtered.new(self, filter)
125
+ LOM::Filtered.new(filter, src: self)
122
126
  end
123
127
  end
124
128
 
125
129
 
126
- def ldap_branch(v)
130
+ def ldap_branch(v = nil)
131
+ return _ldap_branch if v.nil?
127
132
  @__ldap_branch = v
128
133
  end
129
134
 
130
- def ldap_prefix(v)
135
+ def ldap_prefix(v = nil)
136
+ return _ldap_prefix if v.nil?
131
137
  @__ldap_prefix = v
132
138
  end
133
139
 
@@ -154,6 +160,8 @@ module Mapper
154
160
 
155
161
  # @note block will be executed in the mapped object instance
156
162
  def ldap_to(p=nil, &b)
163
+ return _ldap_to if p.nil? && b.nil?
164
+
157
165
  if (! p.nil? ^ b.nil?) || (p && !p.kind_of?(Proc))
158
166
  raise ArgumentError,
159
167
  'one and only one of proc/lamba/block need to be defined'
@@ -162,7 +170,15 @@ module Mapper
162
170
  end
163
171
 
164
172
 
165
-
173
+ # Convert a dn to it's corresponding id the current mapping.
174
+ #
175
+ # @raise [Error] dn belongs to this mapping (it is in the mapping
176
+ # branch), but is malformed (not a direct child, or
177
+ # wrong prefix)
178
+ #
179
+ # @return [String] id
180
+ # @return [nil] dn is not from this mapping
181
+ #
166
182
  def ldap_dn_to_id(dn)
167
183
  prefix = _ldap_prefix.to_s
168
184
  branch = _ldap_branch
@@ -171,8 +187,8 @@ module Mapper
171
187
  case prefix
172
188
  when String, Symbol
173
189
  k, v, _ = sub.to_a
174
- raise ArgumentError, "not a direct child" if _
175
- raise ArgumentError, "wrong prefix" if k.casecmp(prefix) != 0
190
+ raise Error, "not a direct child" if _
191
+ raise Error, "wrong prefix" if k.casecmp(prefix) != 0
176
192
  v
177
193
  end
178
194
  end
@@ -190,24 +206,45 @@ module Mapper
190
206
  self.new(*args)
191
207
  end
192
208
 
193
-
194
- def each(type = :object, filter: nil, paged: nil)
209
+
210
+ # Iterate over matching data.
211
+ #
212
+ # @note If using `rawfilter`, no optimization will be performed
213
+ # aned the ldap attributes will be retrieved,
214
+ # even if desired type is :id
215
+ #
216
+ # @param type [:object, :id] return object or id
217
+ # @param filter [String] extra ldap search filter
218
+ # @param rawfilter [Proc] filter on ldap entry
219
+ # @param paged [Array<Integer,Integer>] pagination information
220
+ #
221
+ # @yieldparam obj_or_id [Object, String] ldap converted element according to type
222
+ #
223
+ # @return [Enumerator] if no block given
224
+ # @return [self] if block given
225
+ #
226
+ def each(type = :object, filter: nil, rawfilter: nil, paged: nil)
195
227
  # Create Enumerator if no block given
196
228
  unless block_given?
197
- return enum_for(:each, type, filter: filter, paged: paged)
229
+ return enum_for(:each, type,
230
+ filter: filter, rawfilter: rawfilter, paged: paged)
198
231
  end
199
232
 
200
233
  # Merging filters
201
- filters = [ filter, _ldap_filter ].compact
202
- filter = filters.size == 2 ? "(&#{filters.join})" : filters.first
234
+ filter = Net::LDAP.filter('&', *[ filter, _ldap_filter ].compact)
203
235
 
204
236
  # Define attributes/converter according to selected type
205
237
  attributes, converter =
206
238
  case type
207
- when :id then [ :dn, ->(e) { ldap_dn_to_id(e.dn) } ]
208
- when :object then [ _ldap_attrs, ->(e) { _ldap_to_obj(e) } ]
239
+ when :id then [ rawfilter ? _ldap_attrs : :dn,
240
+ ->(e) { ldap_dn_to_id(e.dn) }
241
+ ]
242
+ when :object then [ _ldap_attrs,
243
+ ->(e) { _ldap_to_obj(e) }
244
+ ]
209
245
  else raise ArgumentError, 'type must be either :object or :id'
210
246
  end
247
+
211
248
 
212
249
  # Paginate
213
250
  # XXX: pagination is emulated, should be avoided
@@ -222,7 +259,9 @@ module Mapper
222
259
  :attributes => attributes,
223
260
  :scope => _ldap_scope) {|entry|
224
261
 
225
- if paged.nil?
262
+ if rawfilter && !rawfilter.call(entry)
263
+ next
264
+ elsif paged.nil?
226
265
  yield(converter.(entry))
227
266
  elsif skip > 0
228
267
  skip -= 1
@@ -233,18 +272,29 @@ module Mapper
233
272
  yield(converter.(entry))
234
273
  end
235
274
  }
275
+
276
+ # Return self
277
+ self
236
278
  end
237
279
 
238
280
  def paginate(page, page_size)
239
- LOM::Filtered.new(self, paged: [ page, page_size ])
281
+ LOM::Filtered.new(src: self, paged: [ page, page_size ])
240
282
  end
241
283
 
242
- def all
243
- each(:object).to_a
284
+ # Retrieve matching data as a list of object
285
+ #
286
+ # @return [Array<Object>]
287
+ #
288
+ def all(&rawfilter)
289
+ each(:object, rawfilter: rawfilter).to_a
244
290
  end
245
291
 
246
- def list
247
- each(:id).to_a
292
+ # Retrieve matching data as a list of id
293
+ #
294
+ # @return [Array<String>]
295
+ #
296
+ def list(&rawfilter)
297
+ each(:id, rawfilter: rawfilter).to_a
248
298
  end
249
299
 
250
300
  def get(name)
data/lib/lom/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class LOM
2
- VERSION = '0.9.0'
2
+ VERSION = '0.10.0'
3
3
  end
data/lom.gemspec CHANGED
@@ -8,15 +8,13 @@ Gem::Specification.new do |s|
8
8
  s.summary = "LDAP Object Mapper"
9
9
  s.description = <<~EOF
10
10
 
11
- Ease processing of parameters in Sinatra framework.
12
- Integrates well with dry-types, sequel, ...
11
+ Creation of mapping between ldap entry and ruby object.
12
+ Allowing easy retrieval of information, building of search filter,
13
+ and updating ldap.
13
14
 
14
- Example:
15
- want! :user, Dry::Types::String, User
16
- want? :expired, Dry::Types::Params::Bool.default(true)
17
15
  EOF
18
16
 
19
- s.homepage = 'https://gitlab.com/sdalu/lom'
17
+ s.homepage = 'https://github.com/sdalu/lom'
20
18
  s.license = 'MIT'
21
19
 
22
20
  s.authors = [ "Stéphane D'Alu" ]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stéphane D'Alu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-26 00:00:00.000000000 Z
11
+ date: 2021-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-ldap
@@ -52,14 +52,12 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '13'
55
- description: |2
55
+ description: |2+
56
56
 
57
- Ease processing of parameters in Sinatra framework.
58
- Integrates well with dry-types, sequel, ...
57
+ Creation of mapping between ldap entry and ruby object.
58
+ Allowing easy retrieval of information, building of search filter,
59
+ and updating ldap.
59
60
 
60
- Example:
61
- want! :user, Dry::Types::String, User
62
- want? :expired, Dry::Types::Params::Bool.default(true)
63
61
  email:
64
62
  - stephane.dalu@insa-lyon.fr
65
63
  executables: []
@@ -77,7 +75,7 @@ files:
77
75
  - lib/lom/mapper.rb
78
76
  - lib/lom/version.rb
79
77
  - lom.gemspec
80
- homepage: https://gitlab.com/sdalu/lom
78
+ homepage: https://github.com/sdalu/lom
81
79
  licenses:
82
80
  - MIT
83
81
  metadata: {}
@@ -101,3 +99,4 @@ signing_key:
101
99
  specification_version: 4
102
100
  summary: LDAP Object Mapper
103
101
  test_files: []
102
+ ...