goz 0.0.3 → 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,43 @@
1
+ # encoding: utf-8
2
+
3
+ require 'goz/service'
4
+ require 'goz/logger'
5
+
6
+
7
+ module Goz # :nodoc:
8
+ class Service # :nodoc:
9
+
10
+ #
11
+ # = Goz::Service::Base - TODO
12
+ #
13
+ # == Usage
14
+ #
15
+ # TODO
16
+ #
17
+ # == Author
18
+ #
19
+ # blair christensen. <mailto:blair.christensen@gmail.com>
20
+ #
21
+ # == Homepage
22
+ #
23
+ # https://github.com/blairc/goz/
24
+ #
25
+ class Base
26
+
27
+ TAG = self.name
28
+
29
+ @@cf = {}
30
+
31
+
32
+ #
33
+ # Set configuration.
34
+ #
35
+ def self.configuration(configuration)
36
+ @@cf = configuration
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
43
+
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ require 'goz/service/base'
4
+ require 'yaml'
5
+
6
+
7
+ module Goz # :nodoc:
8
+ class Service # :nodoc:
9
+
10
+ #
11
+ # = Goz::Service:FakeService - TODO
12
+ #
13
+ # == Usage
14
+ #
15
+ # TODO
16
+ #
17
+ # == Author
18
+ #
19
+ # blair christensen. <mailto:blair.christensen@gmail.com>
20
+ #
21
+ # == Homepage
22
+ #
23
+ # https://github.com/blairc/goz/
24
+ #
25
+ class FakeService < Goz::Service::Base
26
+
27
+ FN = 'fake_service_database.yaml'
28
+ TAG = self.name
29
+
30
+ @@registry = nil
31
+
32
+
33
+ # XXX ALL UGLY HACKS!
34
+ def self.create(g)
35
+ Goz::Logger.debug TAG, "create( group.name=#{g.name} )"
36
+ load_registry
37
+ if @@registry.key? g.name
38
+ Goz::Logger.info TAG, "create( group.name=#{g.name} ) - group exists"
39
+ return true
40
+ end
41
+ Goz::Logger.info TAG, "create( group.name=#{g.name} ) - creating group"
42
+ @@registry[ g.name ] = { :group => g.to_hash }
43
+ save_registry
44
+ end
45
+
46
+ def self.load_registry
47
+ return @@registry unless @@registry.nil?
48
+ @@registry = File.exist?(FN) ? YAML.load_file(FN) : {}
49
+ end
50
+
51
+ def self.save_registry
52
+ File.open(FN, 'w') { |fh| fh.write YAML.dump(@@registry) }
53
+ end
54
+
55
+ def self.destroy(g)
56
+ Goz::Logger.debug TAG, "destroy( group.name=#{g.name} )"
57
+ load_registry
58
+ unless @@registry.key? g.name
59
+ Goz::Logger.info TAG, "destroy( group.name=#{g.name} ) - does not exist"
60
+ return true
61
+ end
62
+ Goz::Logger.info TAG, "destroy( group.name=#{g.name} ) - deleting group"
63
+ @@registry.delete g.name
64
+ save_registry
65
+ end
66
+
67
+ def self.sync_group(g, s)
68
+ Goz::Logger.debug TAG, "sync_group( group.name=#{g.name}, service.name=#{s.name} )"
69
+ load_registry
70
+ create(g) unless @@registry.key? g.name
71
+ @@registry[ g.name ][ :admins ] = {}
72
+ @@registry[ g.name ][ :members ] = {}
73
+ g.admins.each { |u| @@registry[ g.name ][:admins][ u.login ] = u.to_hash }
74
+ g.members.each { |u| @@registry[ g.name ][:members][ u.login ] = u.to_hash }
75
+ save_registry
76
+ end
77
+
78
+ end
79
+ end
80
+ end
81
+
data/lib/goz/test_case.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'digest/sha1'
3
4
  require 'goz/database'
4
5
  require 'test/unit'
5
6
 
@@ -26,23 +27,34 @@ module Goz # :nodoc:
26
27
  [ :groups, :services, :users ].each { |t| db[t].delete }
27
28
  end
28
29
 
29
- @groups = {
30
- :a => { :display_name => 'Group:A', :identifier => 'c6347f2a0375db1e2ebfe0ad70e24172a3193037', :name => 'gA' },
31
- :b => { :display_name => 'Group:B', :identifier => '341af322e54bea31f8d08d4f2c627a4db341781a', :name => 'gB' }
32
- }
30
+ @groups = {}
31
+ [ :a, :b ].each do |sym|
32
+ @groups[sym] = { :display_name => "Group:#{ sym.to_s.upcase }",
33
+ :identifier => Digest::SHA1.hexdigest( sym.to_s ),
34
+ :klass => 'Goz::Group',
35
+ :name => "g#{ sym.to_s.upcase }"
36
+ }
37
+ end
33
38
 
34
- @services = {
35
- :a => { :name => 'Service A', :klass => 'Goz::Service' },
36
- :b => { :name => 'Service B', :klass => 'Goz::Service' }
37
- }
39
+ @services = {}
40
+ [ :a, :b ].each do |sym|
41
+ @services[sym] = { :name => "Service #{ sym.to_s.upcase }",
42
+ :klass => 'Goz::Service'
43
+ }
44
+ end
38
45
 
39
- @users = {
40
- :a => { :email => 'a@example.org', :login => 'uA', :identifier => '40b071bd23a8fd2b8cff7f59da9211dca7806d47', :name => 'User A' },
41
- :b => { :email => 'b@example.org', :login => 'uB', :identifier => '40b071bd23a8fd2b8cff7f59da9211dca7806d47', :name => 'User B' }
42
- }
46
+ @users = {}
47
+ [ :a, :b ].each do |sym|
48
+ @users[sym] = { :email => "#{ sym.to_s }@example.org",
49
+ :identifier => Digest::SHA1.hexdigest( sym.to_s ),
50
+ :klass => 'Goz::User',
51
+ :login => sym.to_s,
52
+ :name => "User #{ sym.to_s.upcase }"
53
+ }
54
+ end
43
55
  end
44
56
 
45
- end # class TestCase
57
+ end
46
58
 
47
- end # module Goz
59
+ end
48
60
 
data/lib/goz/user.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'active_support/inflector'
3
4
  require 'goz/database'
4
5
  require 'goz/event'
6
+ require 'goz/logger'
7
+
5
8
 
6
9
  module Goz # :nodoc:
7
10
 
@@ -35,7 +38,6 @@ module Goz # :nodoc:
35
38
  # https://github.com/blairc/goz/
36
39
  #
37
40
  class User < Sequel::Model
38
- include Comparable
39
41
 
40
42
  plugin :validation_helpers
41
43
 
@@ -55,76 +57,89 @@ module Goz # :nodoc:
55
57
  :after_remove => proc { |u, g| Goz::Event.member_remove(g, u) }
56
58
 
57
59
 
60
+ ATTRIBUTES = [ :email, :identifier, :klass, :login, :name ]
61
+ TAG = self.name
62
+
63
+ @@api = Goz::User # XXX
64
+
65
+
58
66
  #
59
67
  # Initialize new (non-persistent) Goz::User object
60
68
  #
61
- def initialize(*params)
69
+ def initialize( *params )
62
70
  super
63
71
  yield self if block_given?
64
72
  self
65
73
  end
66
74
 
67
75
  #
68
- # Compare equality.
76
+ # TODO
69
77
  #
70
- def <=>(other)
71
- if self.login < other.login ||
72
- self.email < other.email ||
73
- self.name < other.name ||
74
- self.identifier < other.identifier
75
- -1
76
- elsif self.login > other.login ||
77
- self.email > other.email ||
78
- self.name > other.name ||
79
- self.identifier > other.identifier
80
- 1
81
- else
82
- 0
83
- end
78
+ def self.api( klass = nil, configuration = {} )
79
+ return @@api if klass.nil?
80
+ Goz::Logger.info TAG, "changing api from #{@@api} to #{klass}"
81
+ klass_name = klass.kind_of?(String) ? klass : klass.name
82
+ require klass_name.underscore
83
+ klass = klass.constantize if klass.kind_of?(String)
84
+ klass.configuration configuration
85
+ @@api = klass
84
86
  end
85
87
 
86
88
  #
87
- # Create Goz::User or raise +RuntimeError+
89
+ # Find Goz::User by identifier or return +nil+
88
90
  #
89
- def self.create( params = {} )
90
- params = {} unless params.kind_of?(Hash)
91
- [ :email, :identifier, :login, :name ].each do |k|
92
- raise( "invalid #{k.to_s}" ) if ( params[k].nil? || params[k].empty? )
91
+ def self.find_by_identifier(identifier)
92
+ Goz::Logger.debug TAG, "find_by_identifier( identifier=#{identifier} )"
93
+ u = self.find :identifier => identifier
94
+ return u unless u.nil?
95
+ return nil if Goz::User == self.api
96
+ u = self.api.find_by_identifier identifier
97
+ return nil if u.nil?
98
+ begin
99
+ return self.find_or_create u.to_hash
100
+ rescue => e
101
+ Goz::Logger.error TAG, "find_by_identifier( identifier=#{identifier} ) => #{ e.to_s }"
93
102
  end
94
- super
103
+ return nil
95
104
  end
96
105
 
97
106
  #
98
107
  # Find Goz::User by login or return +nil+
99
108
  #
100
109
  def self.find_by_login(login)
101
- Goz::Database.instance do |db|
102
- return User.find( :login => login )
103
- end
110
+ Goz::Logger.debug TAG, "find_by_login( login=#{login} )"
111
+ u = self.find :login => login
112
+ return u unless u.nil?
113
+ return nil if Goz::User == self.api
114
+ u = self.api.find_by_login login
115
+ return nil if u.nil?
116
+ return self.find_or_create u.to_hash
104
117
  end
105
118
 
106
119
  #
107
120
  # Synchronize user with external data sources (if relevant).
108
121
  #
109
122
  def sync(force = false)
110
- if force || synchronized_at.nil? || synchronized_at < ( Time.now - ( 1 * 60 * 60 ) ) # XXX
111
- # XXX Synchronize user with remote source (if necessary)
112
- # XXX Synchronize groups
113
- # XXX Synchronize memberships
114
- self.synchronized_at = Time.now
115
- self.save
116
- Goz::Event.user_sync(self)
117
- return true
123
+ Goz::Logger.debug TAG, "#{self.login} - #sync( force=#{force} )"
124
+ if sync_user && sync_groups && sync_memberships
125
+ self.synchronized_at = Time.now
126
+ self.save
127
+ Goz::Event.user_sync(self)
128
+ return true
118
129
  end
119
130
  false
120
131
  end
121
132
 
133
+ def to_hash
134
+ Hash[ ATTRIBUTES.map { |k| [ k, self[k] ] } ]
135
+ end
136
+
122
137
  #
123
138
  # Perform model valiations.
124
139
  #
125
140
  def validate
126
141
  super
127
- validates_presence [ :email, :identifier, :login, :name ]
142
+ validates_presence [ :email, :identifier, :klass, :login, :name ]
128
143
  validates_type Time, [ :created_at, :modified_at, :synchronized_at ]
129
144
  validates_unique [ :email, :identifier, :login ]
130
145
  end
@@ -134,12 +149,12 @@ module Goz # :nodoc:
134
149
 
135
150
  def after_destroy
136
151
  super
137
- Event.create :event_type_id => Goz::EventType::USER_REMOVE, :user_id => self.id
152
+ Goz::Event.user_remove(self)
138
153
  end
139
154
 
140
155
  def after_create
141
156
  super
142
- Event.create :event_type_id => Goz::EventType::USER_ADD, :user_id => self.id
157
+ Goz::Event.user_add(self)
143
158
  end
144
159
 
145
160
  def before_create
@@ -158,6 +173,52 @@ module Goz # :nodoc:
158
173
  self.modified_at = Time.now
159
174
  end
160
175
 
176
+ def klass_instance
177
+ return @@api if @@api.name == self.klass
178
+ unless @k
179
+ @k = self.klass.constantize
180
+ require self.klass.underscore
181
+ end
182
+ return @k
183
+ end
184
+
185
+ # Synchronize user groups with external data source
186
+ def sync_groups
187
+ Goz::Logger.debug TAG, "#{self.login} - #sync_groups()"
188
+ # TODO Does Sequel provide a better option for this?
189
+ t = Goz::Group.admin(self)
190
+ t.each { |_| self.add_group(_) unless self.groups.include?(_) }
191
+ self.groups.each { |_| self.remove_group(_) unless t.include?(_) }
192
+ # TODO Log! if modified?
193
+ true
194
+ end
195
+
196
+ # Synchronize user memberships with external data source
197
+ def sync_memberships
198
+ Goz::Logger.debug TAG, "#{self.login} - #sync_memberships()"
199
+ # TODO Does Sequel provide a better option for this?
200
+ t = Goz::Group.member(self)
201
+ t.each { |_| self.add_membership(_) unless self.memberships.include?(_) }
202
+ self.memberships.each { |_| self.remove_membership(_) unless t.include?(_) }
203
+ # TODO Log! if modified?
204
+ true
205
+ end
206
+
207
+ # Synchronize user with external data source
208
+ def sync_user
209
+ Goz::Logger.debug TAG, "#{self.login} - #sync_user()"
210
+ return true if Goz::User.name == self.klass
211
+ u = klass_instance.find_by_login self.login
212
+ return false if u.nil?
213
+ [ :email, :identifier, :login, :name ].each do |attr|
214
+ unless self[attr] == u[attr]
215
+ Goz::Logger.info TAG, "#{self.login} = #sync_user() - #{attr} changed"
216
+ self[attr] = u[attr]
217
+ end
218
+ end
219
+ true
220
+ end
221
+
161
222
  end # class User
162
223
 
163
224
  end # module Goz
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ require 'goz/logger'
4
+ require 'goz/user'
5
+
6
+
7
+ module Goz # :nodoc:
8
+ class User # :nodoc:
9
+
10
+ #
11
+ # = Goz::User::Base - TODO
12
+ #
13
+ # == Usage
14
+ #
15
+ # TODO
16
+ #
17
+ # == Author
18
+ #
19
+ # blair christensen. <mailto:blair.christensen@gmail.com>
20
+ #
21
+ # == Homepage
22
+ #
23
+ # https://github.com/blairc/goz/
24
+ #
25
+ class Base
26
+
27
+ TAG = self.name
28
+
29
+ attr_accessor :email, :identifier, :klass, :login, :name
30
+
31
+ @@cf = {}
32
+
33
+
34
+ #
35
+ # TODO
36
+ #
37
+ def [](key)
38
+ send key.to_sym
39
+ end
40
+
41
+ #
42
+ # Set configuration.
43
+ #
44
+ def self.configuration(configuration)
45
+ @@cf = configuration
46
+ end
47
+
48
+ #
49
+ # Find Goz::User by login or return +nil+.
50
+ #
51
+ # MUST be implemented by API extensions.
52
+ #
53
+ def self.find_by_login(login)
54
+ raise RuntimeError, "not implemented"
55
+ end
56
+
57
+ #
58
+ # Convert to Goz::User-style hash
59
+ #
60
+ def to_hash
61
+ return { :email => self.email,
62
+ :identifier => self.identifier,
63
+ :klass => self.klass,
64
+ :login => self.login,
65
+ :name => self.name
66
+ }
67
+ end
68
+
69
+ #
70
+ # TODO
71
+ #
72
+ def to_s
73
+ "email=#{email} | identifier=#{identifier} | klass=#{klass} | login=#{login} | name=#{name}"
74
+ end
75
+
76
+
77
+ end
78
+
79
+ end
80
+ end
81
+