totally_lazy 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef27c17e829a30f3c3efd0bacdb6023066e85326
4
- data.tar.gz: d583a528661e3c190245c64ffce9cf9b8093d55a
3
+ metadata.gz: 07387e8a468855a7da2085b260925debed565e11
4
+ data.tar.gz: cf13015fc8e70f75561464263ae624a18f59cedb
5
5
  SHA512:
6
- metadata.gz: 32ed21dfa78360ba8dec458c2f6646a4af09a448957b84e51c0ee686aa1fb6e031334f676e7bb5a51e759bceb9c8e88a942f022b39c80189e64877949f121aa2
7
- data.tar.gz: 9e903a0e2716f72c524753d35de1a5ecfdb0f71228b6aa4aef1f0f55e92c3bdf9a6cb38c9fdfad99acc49a19f2501992d4175180daa1b7805cde33b84f5bb305
6
+ metadata.gz: 01a92b3af2d701c2a900c74282d666642ed00cead1aac4d3781dbe5ece2ca2439d84cda0923c0e1ec3420f108443690061cfc18233281d484f07c7750a94a867
7
+ data.tar.gz: 164b856f25c3ebed7cb00134b118dc5815de8d522f3bc5cf395dc9f626b4ac118049528f7cc09a4b7b65532ae7f4c9f473fdd212d1a83f71e0a4364c52695f46
data/README.md CHANGED
@@ -17,10 +17,12 @@ This is a port of the java functional library [Totally Lazy](https://code.google
17
17
 
18
18
  ### Install
19
19
 
20
+ This gem requires ruby 2.x.x
21
+
20
22
  In your bundler Gemfile
21
23
 
22
24
  ```ruby
23
- gem totally_lazy, '~>0.0.4'
25
+ gem totally_lazy, '~>0.0.6'
24
26
  ```
25
27
 
26
28
  Or with rubygems
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.5
1
+ 0.0.6
data/lib/pair.rb CHANGED
@@ -25,6 +25,16 @@ module Pair
25
25
  @second.call
26
26
  end
27
27
 
28
+ def first=(v)
29
+ @first = -> { v }
30
+ end
31
+ alias key= first=
32
+
33
+ def second=(v)
34
+ @second = -> { v }
35
+ end
36
+ alias value= second=
37
+
28
38
  def each(&block)
29
39
  [first,second].each do |i|
30
40
  block.call(i)
@@ -3,30 +3,23 @@ module Predicates
3
3
  module Compare
4
4
 
5
5
  def equals(value)
6
- -> (v, meth=:self, invert=false) do
7
- invert ? inverted_value(v, value, meth, :==) : regular_value(v, value, meth, :==)
8
- end
6
+ value_predicate(:equals,:==,value)
9
7
  end
10
8
 
11
9
  alias equal_to equals
12
10
 
13
11
  def greater_than(value)
14
- -> (v, meth=:self, invert=false) do
15
- invert ? inverted_value(v, value, meth, :>) : regular_value(v, value, meth, :>)
16
- end
12
+ value_predicate(:greater_than,:>,value)
17
13
  end
18
14
 
19
15
  def less_than(value)
20
- -> (v, meth=:self, invert=false) do
21
- invert ? inverted_value(v, value, meth, :<) : regular_value(v, value, meth, :<)
22
- end
16
+ value_predicate(:less_than,:<,value)
23
17
  end
24
18
 
25
- def matches(regex)
26
- -> (v, meth=:self, invert=false) do
27
- invert ? inverted_regex(v, regex, meth) : regular_regex(v, regex, meth)
28
- end
19
+ def matches(value)
20
+ regex_predicate(:matches,value)
29
21
  end
22
+
30
23
  end
31
24
 
32
25
  end
@@ -3,19 +3,19 @@ module Predicates
3
3
  module Conversions
4
4
 
5
5
  def as_string
6
- -> (v) { v.to_s }
6
+ simple_predicate(:as_string, -> (v) { v.to_s } )
7
7
  end
8
8
 
9
9
  def as_int
10
- -> (v) { Type.responds(v, :to_i); v.to_i }
10
+ simple_predicate(:as_int, -> (v) { Type.responds(v, :to_i); v.to_i } )
11
11
  end
12
12
 
13
13
  def as_float
14
- -> (v) { Type.responds(v, :to_i); v.to_f }
14
+ simple_predicate(:as_float, -> (v) { Type.responds(v, :to_f); v.to_f } )
15
15
  end
16
16
 
17
17
  def as_array
18
- -> (v) { [v] }
18
+ simple_predicate(:as_array, -> (v) { [v] } )
19
19
  end
20
20
 
21
21
  end
@@ -3,15 +3,11 @@ module Predicates
3
3
  module Numbers
4
4
 
5
5
  def even
6
- -> (v, meth=:self, invert=false) do
7
- invert ? inverted(v, meth, :even?) : regular(v, meth, :even?)
8
- end
6
+ self_predicate(:even,:even?)
9
7
  end
10
8
 
11
9
  def odd
12
- -> (v, meth=:self, invert=false) do
13
- invert ? inverted(v, meth, :odd?) : regular(v, meth, :odd?)
14
- end
10
+ self_predicate(:odd,:odd?)
15
11
  end
16
12
  #
17
13
  # def between(lower, higher)
@@ -1,57 +1,141 @@
1
1
  module Predicates
2
2
 
3
- def inverted(v, meth, pred)
4
- if meth == :self
5
- Type.responds(v, pred)
6
- v unless v.send(pred)
3
+ class Predicate
4
+
5
+ def inverted(v, meth, pred)
6
+ if meth == :self
7
+ Type.responds(v, pred)
8
+ v unless v.send(pred)
9
+ end
10
+ end
11
+
12
+ def regular(v, meth, pred)
13
+ if meth == :self
14
+ Type.responds(v, pred)
15
+ v if v.send(pred)
16
+ else
17
+ r = v.send(meth)
18
+ Type.responds(r, pred)
19
+ v if r.send(pred)
20
+ end
21
+ end
22
+
23
+ def inverted_value(v, value, meth, pred)
24
+ if meth == :self
25
+ Type.responds(v, pred)
26
+ v unless v.send(pred, value)
27
+ end
28
+ end
29
+
30
+ def regular_value(v, value, meth, pred)
31
+ if meth == :self
32
+ Type.responds(v, pred)
33
+ v if v.send(pred, value)
34
+ else
35
+ r = v.send(meth)
36
+ Type.responds(r, pred)
37
+ v if r.send(pred, value)
38
+ end
39
+ end
40
+
41
+ def regular_regex(v, regex, meth)
42
+ if meth == :self
43
+ v if v.to_s.match(regex)
44
+ else
45
+ r = v.send(meth)
46
+ v if r.to_s.match(regex)
47
+ end
7
48
  end
8
- end
9
49
 
10
- def regular(v, meth, pred)
11
- if meth == :self
12
- Type.responds(v, pred)
13
- v if v.send(pred)
14
- else
15
- r = v.send(meth)
16
- Type.responds(r, pred)
17
- v if r.send(pred)
50
+ def inverted_regex(v, regex, meth)
51
+ if meth == :self
52
+ v unless v.to_s.match(regex)
53
+ else
54
+ r = v.send(meth)
55
+ v unless r.to_s.match(regex)
56
+ end
18
57
  end
58
+
59
+ end
60
+
61
+ def value_predicate(name, pred, value)
62
+ ValuePredicate.new(name, pred, value)
63
+ end
64
+
65
+ def regex_predicate(name, value)
66
+ RegexPredicate.new(name, value)
67
+ end
68
+
69
+ def self_predicate(name,pred)
70
+ SelfPredicate.new(name,pred)
71
+ end
72
+
73
+ def simple_predicate(name,exec)
74
+ SimplePredicate.new(name,exec)
19
75
  end
20
76
 
21
- def inverted_value(v, value, meth, pred)
22
- if meth == :self
23
- Type.responds(v, pred)
24
- v unless v.send(pred, value)
77
+
78
+ class ValuePredicate < Predicates::Predicate
79
+
80
+ attr_reader :name, :pred, :value
81
+
82
+ def initialize(name, pred, value)
83
+ @name = name
84
+ @pred = pred
85
+ @value = value
86
+ end
87
+
88
+ def exec
89
+ -> (v, meth=:self, invert=false) do
90
+ invert ? inverted_value(v, @value, meth, @pred) : regular_value(v, @value, meth, @pred)
91
+ end
25
92
  end
93
+
26
94
  end
27
95
 
28
- def regular_value(v, value, meth, pred)
29
- if meth == :self
30
- Type.responds(v, pred)
31
- v if v.send(pred, value)
32
- else
33
- r = v.send(meth)
34
- Type.responds(r, pred)
35
- v if r.send(pred, value)
96
+ class SelfPredicate < Predicates::Predicate
97
+
98
+ attr_reader :name, :pred
99
+
100
+ def initialize(name, pred)
101
+ @name = name
102
+ @pred = pred
103
+ end
104
+
105
+ def exec
106
+ -> (v, meth=:self, invert=false) do
107
+ invert ? inverted(v, meth, @pred) : regular(v, meth, @pred)
108
+ end
36
109
  end
110
+
37
111
  end
38
112
 
39
- def regular_regex(v, regex, meth)
40
- if meth == :self
41
- v if v.to_s.match(regex)
42
- else
43
- r = v.send(meth)
44
- v if r.to_s.match(regex)
113
+ class SimplePredicate < Predicates::Predicate
114
+ attr_reader :name, :exec
115
+
116
+ def initialize(name, exec)
117
+ @name = name
118
+ @exec = exec
45
119
  end
120
+
46
121
  end
47
122
 
48
- def inverted_regex(v, regex, meth)
49
- if meth == :self
50
- v unless v.to_s.match(regex)
51
- else
52
- r = v.send(meth)
53
- v unless r.to_s.match(regex)
123
+ class RegexPredicate < Predicates::Predicate
124
+
125
+ attr_reader :name, :value
126
+
127
+ def initialize(name, value)
128
+ @name = name
129
+ @value = value
54
130
  end
131
+
132
+ def exec
133
+ -> (v, meth=:self, invert=false) do
134
+ invert ? inverted_regex(v, @value, meth) : regular_regex(v, @value, meth)
135
+ end
136
+ end
137
+
55
138
  end
56
139
 
140
+
57
141
  end
@@ -5,9 +5,9 @@ class WhereProcessor
5
5
 
6
6
  def apply(predicates, invert=false)
7
7
  if invert
8
- @value if predicates.map { |x| x.value.call(@value, x.key) }.contains?(nil)
8
+ @value if predicates.map { |x| x.value.exec.call(@value, x.key) }.contains?(nil)
9
9
  else
10
- @value unless predicates.map { |x| x.value.call(@value, x.key) }.contains?(nil)
10
+ @value unless predicates.map { |x| x.value.exec.call(@value, x.key) }.contains?(nil)
11
11
  end
12
12
  end
13
13
  end
data/lib/sequence.rb CHANGED
@@ -4,6 +4,9 @@ end
4
4
  class UnsupportedTypeException < RuntimeError
5
5
  end
6
6
 
7
+ class UnsupportedMethodException < RuntimeError
8
+ end
9
+
7
10
  module Sequences
8
11
 
9
12
  # Creates a sequence
@@ -84,7 +87,7 @@ module Sequences
84
87
  def map(predicate=nil, &block)
85
88
  if predicate
86
89
  Sequence.new(self) { |yielder, val|
87
- v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.call(val)
90
+ v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.exec.call(val)
88
91
  yielder << v unless v.nil?
89
92
  }
90
93
  else
@@ -121,7 +124,7 @@ module Sequences
121
124
  def select(predicate=nil, &block)
122
125
  if predicate
123
126
  Sequence.new(self) { |yielder, val|
124
- v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.call(val)
127
+ v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.exec.call(val)
125
128
  yielder << v unless v.nil?
126
129
  }
127
130
  else
@@ -139,7 +142,7 @@ module Sequences
139
142
  def reject(predicate=nil, &block)
140
143
  if predicate
141
144
  Sequence.new(self) { |yielder, val|
142
- v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates, true) : predicate.call(val, :self, true)
145
+ v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates, true) : predicate.exec.call(val, :self, true)
143
146
  yielder << v unless v.nil?
144
147
  }
145
148
  else
@@ -187,15 +190,15 @@ module Sequences
187
190
  end
188
191
 
189
192
  def take(n)
190
- taken = 0
191
- Sequence.new(self) { |yielder, val|
192
- if taken < n
193
- yielder << val
194
- taken += 1
195
- else
196
- raise StopIteration
193
+ Sequence.new(Sequence::Generator.new do |g|
194
+ self.each_with_index do |v, i|
195
+ if i < n
196
+ g.yield v
197
+ else
198
+ raise StopIteration
199
+ end
197
200
  end
198
- }
201
+ end)
199
202
  end
200
203
 
201
204
  def take_while(&block)
@@ -334,6 +337,18 @@ module Sequences
334
337
  end)
335
338
  end
336
339
 
340
+ def to_maps(symbolize=true)
341
+ Sequence.new(Sequence::Generator.new do |g|
342
+ self.each_slice(2) do |k, v|
343
+ if symbolize
344
+ g.yield k.to_s.to_sym => v
345
+ else
346
+ g.yield k => v
347
+ end
348
+ end
349
+ end)
350
+ end
351
+
337
352
  def from_pairs
338
353
  Sequence.new(Sequence::Generator.new do |g|
339
354
  self.entries.map { |e| Type.check(e, Pair::Pair); [e.key, e.value] }.flatten.each { |i| g.yield i }
@@ -372,6 +387,25 @@ module Sequences
372
387
  end
373
388
  end
374
389
 
390
+ def update(item)
391
+ Sequence.new(Sequence::Generator.new do |g|
392
+ if item.is_a?(Hash)
393
+ self.map do |e|
394
+ item.map { |k, v|
395
+ raise(UnsupportedMethodException.new, "Tried to call method: #{k} on #{e.class} but method not supported") unless e.respond_to?(k) or e.respond_to?(":#{k}=")
396
+ begin
397
+ e.send(k, v) if e.respond_to?(k); e
398
+ rescue
399
+ e.send("#{k}=", v) if e.respond_to?("#{k}="); e
400
+ end
401
+ }.first
402
+ end
403
+ else
404
+ self.map { item }
405
+ end.each { |i| g.yield i }
406
+ end)
407
+ end
408
+
375
409
  def marshal_dump
376
410
  serialize
377
411
  end
@@ -396,6 +430,24 @@ module Sequences
396
430
  to_a.flatten
397
431
  end
398
432
 
433
+ def sorting_by(*attr, &block)
434
+ if attr.empty?
435
+ Sequence.new(Sequence::Generator.new do |g|
436
+ self.sort_by { |e| block.call(e) }.each { |i| g.yield i }
437
+ end)
438
+ else
439
+ Sequence.new(Sequence::Generator.new do |g|
440
+ self.sort_by { |e| attr.map{|v| e.send(v)} }.each { |i| g.yield i }
441
+ end)
442
+ end
443
+ end
444
+
445
+ def sorting
446
+ Sequence.new(Sequence::Generator.new do |g|
447
+ self.sort.each { |i| g.yield i }
448
+ end)
449
+ end
450
+
399
451
  def get_or_else(index, or_else)
400
452
  blank?(sequence[index]) ? or_else : sequence[index]
401
453
  end
@@ -418,7 +470,7 @@ module Sequences
418
470
  if predicate
419
471
  Sequence.new(Sequence::Generator.new do |g|
420
472
  Parallel.map(self.entries, options) { |val|
421
- predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.call(val)
473
+ predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.exec.call(val)
422
474
  }.each { |i| g.yield i unless i.nil? }
423
475
  end)
424
476
  else
data/lib/totally_lazy.rb CHANGED
@@ -14,6 +14,7 @@ require_relative 'predicates/where_processor'
14
14
  require_relative 'parallel/parallel'
15
15
  require_relative 'generators'
16
16
  require_relative 'any'
17
+ require_relative 'utils'
17
18
 
18
19
  include Sequences
19
20
  include Option
@@ -25,6 +26,7 @@ include Predicates::Compare
25
26
  include Predicates::Where
26
27
  include Generators
27
28
  include Any
29
+ include Maps
28
30
 
29
31
 
30
32
  # p sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).filter(
@@ -127,6 +129,35 @@ include Any
127
129
  # p f - s
128
130
 
129
131
 
132
+ # p Seq.range(1,20).filter(odd).update(10).to_a
133
+
134
+ # require 'ostruct'
135
+ # #
136
+ # #
137
+ # # p Seq.repeat(->{OpenStruct.new(name:Any.string(5),age:Any.int(2))}).take(100000).update(age:11,name:'woops').take(1000).head
138
+ # #
139
+ # # p sequence({apple:1,pear:2}).each_slice(2).map{|s| [s[0],s[1]]}.to_a
140
+ #
141
+ # a = Seq.range(1,10).map{|n| OpenStruct.new(name:Any.string(5),age:Any.int(2)) }.to_a
142
+ #
143
+ # orig = sequence(a)
144
+ #
145
+ # updates = orig.filter(where(age:greater_than(70))).update(age:2)
146
+ #
147
+ # p orig.count
148
+ # p updates.count
149
+
150
+ # p where(id:equals(1)).as_map.entries
151
+
152
+ #
153
+
154
+ # p sequence(1,2,3,4).filter(where(is equal_to(2))).entries
155
+
156
+ # p where(is equal_to 2).predicates.head.value
157
+
158
+
159
+ # p sequence(1,2,3,4,5,6).map(as_string).to_a
160
+
130
161
 
131
162
 
132
163
 
data/lib/utils.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Maps
2
+
3
+ module_function
4
+
5
+ def merge(sequence_of_maps)
6
+ sequence_of_maps.reduce({}){|a,b| a.merge(b) }
7
+ end
8
+
9
+ end
@@ -192,4 +192,26 @@ describe 'Sequence' do
192
192
  expect(sequence(:name,'kings',:age,39).in_pairs.to_a).to eq(sequence(pair(:name,'kings'),pair(:age,39)).to_a)
193
193
  end
194
194
 
195
+ it 'should update items in a sequence' do
196
+ expect(Seq.repeat(->{OpenStruct.new(name:Any.string(5),age:Any.int(2))}).take(100000).update(age:11,name:'woops').take(10).map{|s| [s.name,s.age]}.head).to eq(['woops',11])
197
+ expect(Seq.range(1,10).filter(odd).update(2)).to eq(sequence(2,2,2,2,2))
198
+ end
199
+
200
+ it 'should sort by attributes' do
201
+ expect(sequence(pair(:a,1),pair(:d,4),pair(:c,3),pair(:b,2)).sorting_by{|e| e.value }.to_a).to eq([{:a=>1}, {:b=>2}, {:c=>3}, {:d=>4}])
202
+ expect(sequence(pair(:a,1),pair(:d,4),pair(:c,3),pair(:b,2)).sorting_by(:value).to_a).to eq([{:a=>1}, {:b=>2}, {:c=>3}, {:d=>4}])
203
+ expect(sequence(
204
+ OpenStruct.new(first_name:'David',age:19,job:'computers'),
205
+ OpenStruct.new(first_name:'Chris',age:19,job:'art'),
206
+ OpenStruct.new(first_name:'Andrew',age:32,job:'dancing'),
207
+ OpenStruct.new(first_name:'Andrew',age:18,job:'dancing'),
208
+ ).sorting_by{|e| [e.first_name,e.age] }.map{|e| [e.first_name,e.age,e.job]}.to_a).to eq([['Andrew', 18, 'dancing'], ['Andrew', 32, 'dancing'], ['Chris', 19, 'art'], ['David', 19, 'computers']])
209
+ end
210
+
211
+ it 'should sort' do
212
+ expect(sequence(5,2,3,1,4).sorting).to eq(sequence(1,2,3,4,5))
213
+ end
214
+
215
+
216
+
195
217
  end
data/spec/util_spec.rb ADDED
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'Maps' do
4
+
5
+ it 'should return a sequence of maps as a merged map' do
6
+ expect(Maps.merge(sequence(:a,2,:b,4).to_maps)).to eq({a:2,b:4})
7
+ expect(Maps.merge(sequence(1,2,3,4).to_maps(false))).to eq({1=>2,3=>4})
8
+ end
9
+
10
+ end
data/totally_lazy.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: totally_lazy 0.0.5 ruby lib
5
+ # stub: totally_lazy 0.0.6 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "totally_lazy"
9
- s.version = "0.0.5"
9
+ s.version = "0.0.6"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Kingsley Hendrickse"]
14
- s.date = "2014-09-22"
14
+ s.date = "2014-10-03"
15
15
  s.description = "Port of java functional library totally lazy to ruby"
16
16
  s.email = "kingsleyhendrickse@me.com"
17
17
  s.extra_rdoc_files = [
@@ -44,6 +44,7 @@ Gem::Specification.new do |s|
44
44
  "lib/sequence.rb",
45
45
  "lib/totally_lazy.rb",
46
46
  "lib/type_check.rb",
47
+ "lib/utils.rb",
47
48
  "spec/functor_spec.rb",
48
49
  "spec/generators_spec.rb",
49
50
  "spec/option_spec.rb",
@@ -53,6 +54,7 @@ Gem::Specification.new do |s|
53
54
  "spec/serialization_spec.rb",
54
55
  "spec/spec_helper.rb",
55
56
  "spec/type_check_spec.rb",
57
+ "spec/util_spec.rb",
56
58
  "totally_lazy.gemspec"
57
59
  ]
58
60
  s.homepage = "http://github.com/kingsleyh/totally_lazy"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: totally_lazy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kingsley Hendrickse
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-22 00:00:00.000000000 Z
11
+ date: 2014-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -169,6 +169,7 @@ files:
169
169
  - lib/sequence.rb
170
170
  - lib/totally_lazy.rb
171
171
  - lib/type_check.rb
172
+ - lib/utils.rb
172
173
  - spec/functor_spec.rb
173
174
  - spec/generators_spec.rb
174
175
  - spec/option_spec.rb
@@ -178,6 +179,7 @@ files:
178
179
  - spec/serialization_spec.rb
179
180
  - spec/spec_helper.rb
180
181
  - spec/type_check_spec.rb
182
+ - spec/util_spec.rb
181
183
  - totally_lazy.gemspec
182
184
  homepage: http://github.com/kingsleyh/totally_lazy
183
185
  licenses: