goz 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.gem
3
3
  .bundle
4
4
  coverage/
5
+ fake_service.yaml
5
6
  Gemfile.lock
6
7
  goz.log
7
8
  html/
data/HISTORY.rdoc CHANGED
@@ -1,5 +1,11 @@
1
1
  = Goz History
2
2
 
3
+ == YYYY-MM-DD v0.0.4
4
+
5
+ * Added +Goz::Group::EtcGroup+
6
+ * Added +Goz::User::EtcPasswd+
7
+ * TBD
8
+
3
9
  == 2012-04-30 v0.0.3
4
10
 
5
11
  * Added +Goz::Database+
data/README.rdoc CHANGED
@@ -2,22 +2,7 @@
2
2
 
3
3
  == Usage
4
4
 
5
-
6
- # Cache
7
- require 'goz/cache'
8
- Goz::Cache.instance do |cache|
9
- ... do stuff ...
10
- end
11
-
12
- # Logger
13
- require 'goz/logger'
14
- Goz::Logger.instance('MyApp') do |logger|
15
- ... do stuff ...
16
- end
17
-
18
- == See Also
19
-
20
- Goz::Cache, Goz::Group, Goz::Logger, Goz::User
5
+ See https://github.com/blairc/goz-sinatra for an example implementation
21
6
 
22
7
  == Author
23
8
 
@@ -29,5 +14,5 @@ https://github.com/blairc/goz/
29
14
 
30
15
  == See Also
31
16
 
32
- https://github.com/blairc/goz-rails/
17
+ https://github.com/blairc/goz-sinatra/, https://github.com/blairc/goz-rails/
33
18
 
data/TODO.md ADDED
@@ -0,0 +1,16 @@
1
+ Goz To Do
2
+ =========
3
+
4
+ Goz v0.1.0
5
+ ----------
6
+ * Release v0.1.0
7
+
8
+
9
+ Future
10
+ ------
11
+ * Documentation!
12
+ * Add a real service
13
+ * Revisit-or-remove `Goz::Cache`
14
+ * Add support for asynchronous jobs (eg. interacting with external services)
15
+ * Rework API definition to support multiple implementations
16
+
data/goz.gemspec CHANGED
@@ -18,12 +18,16 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ['lib']
20
20
 
21
+ s.add_runtime_dependency 'activesupport'
22
+ s.add_runtime_dependency 'grouper-rest-client'
23
+ s.add_runtime_dependency 'json'
21
24
  s.add_runtime_dependency 'sequel'
25
+ s.add_runtime_dependency 'sqlite3'
22
26
 
27
+ s.add_development_dependency 'mocha'
23
28
  s.add_development_dependency 'rake'
24
29
  s.add_development_dependency 'rdoc'
25
30
  s.add_development_dependency 'rdoc-readme', '~> 0.1.2'
26
31
  s.add_development_dependency 'simplecov'
27
- s.add_development_dependency 'sqlite3'
28
32
  end
29
33
 
data/lib/goz.rb CHANGED
@@ -1,35 +1,19 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'goz/cache'
4
- require 'goz/event'
5
- require 'goz/event_type'
6
- require 'goz/group'
7
- require 'goz/logger'
8
- require 'goz/service'
9
- require 'goz/user'
10
- require 'goz/version'
3
+ require_relative 'goz/event'
4
+ require_relative 'goz/event_type'
5
+ require_relative 'goz/group'
6
+ require_relative 'goz/logger'
7
+ require_relative 'goz/service'
8
+ require_relative 'goz/user'
9
+ require_relative 'goz/version'
11
10
 
12
11
  #
13
12
  # = Goz - Generic self-service provisioning system
14
13
  #
15
14
  # == Usage
16
15
  #
17
- #
18
- # # Cache
19
- # require 'goz/cache'
20
- # Goz::Cache.instance do |cache|
21
- # ... do stuff ...
22
- # end
23
- #
24
- # # Logger
25
- # require 'goz/logger'
26
- # Goz::Logger.instance('MyApp') do |logger|
27
- # ... do stuff ...
28
- # end
29
- #
30
- # == See Also
31
- #
32
- # Goz::Cache, Goz::Group, Goz::Logger, Goz::User
16
+ # See https://github.com/blairc/goz-sinatra for an example implementation
33
17
  #
34
18
  # == Author
35
19
  #
@@ -41,7 +25,7 @@ require 'goz/version'
41
25
  #
42
26
  # == See Also
43
27
  #
44
- # https://github.com/blairc/goz-rails/
28
+ # https://github.com/blairc/goz-sinatra/, https://github.com/blairc/goz-rails/
45
29
  #
46
30
  module Goz
47
31
  # Your code goes here...
data/lib/goz/database.rb CHANGED
@@ -32,6 +32,8 @@ module Goz # :nodoc:
32
32
  #
33
33
  class Database
34
34
 
35
+ # XXX Better method for setting db url
36
+
35
37
  #
36
38
  # Sequel database instance
37
39
  #
@@ -39,6 +41,14 @@ module Goz # :nodoc:
39
41
  Goz::Database::Migrations.migrate!
40
42
 
41
43
 
44
+ # XXX http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/ClassMethods.html#method-i-db-3D
45
+ def self.connect!(url)
46
+ self.send( :remove_const, :'DB' ) if self.const_defined?( :'DB' )
47
+ self.const_set :'DB', Sequel.connect(url)
48
+ Goz::Database::Migrations.migrate!
49
+ self.instance
50
+ end
51
+
42
52
  #
43
53
  # Get database instance
44
54
  #
@@ -31,10 +31,11 @@ module Goz # :nodoc:
31
31
  change do
32
32
  DB.create_table(:groups) do
33
33
  primary_key :id
34
- String :display_name, :null => false
35
- String :identifier, :null => false
36
- String :name, :null => false
37
- timestamp :created_at, :null => false
34
+ String :display_name, :null => false
35
+ String :identifier, :null => false
36
+ String :klass, :null => false
37
+ String :name, :null => false
38
+ timestamp :created_at, :null => false
38
39
  timestamp :modified_at
39
40
  timestamp :synchronized_at
40
41
 
@@ -49,9 +50,9 @@ module Goz # :nodoc:
49
50
  change do
50
51
  DB.create_table(:services) do
51
52
  primary_key :id
52
- String :name, :null => false
53
- String :klass, :null => false
54
- timestamp :created_at, :null => false
53
+ String :name, :null => false
54
+ String :klass, :null => false
55
+ timestamp :created_at, :null => false
55
56
  timestamp :modified_at
56
57
  timestamp :synchronized_at
57
58
 
@@ -68,6 +69,7 @@ module Goz # :nodoc:
68
69
  String :identifier, :null => false
69
70
  String :login, :null => false
70
71
  String :name, :null => false
72
+ String :klass, :null => false
71
73
  timestamp :created_at, :null => false
72
74
  timestamp :modified_at
73
75
  timestamp :synchronized_at
@@ -126,6 +128,7 @@ module Goz # :nodoc:
126
128
  end
127
129
  end
128
130
 
131
+ # TODO Consider removing?
129
132
  Sequel.migration do
130
133
  change do
131
134
  event_types = DB[:event_types]
@@ -146,15 +149,18 @@ module Goz # :nodoc:
146
149
  change do
147
150
  DB.create_table(:events) do
148
151
  primary_key :id
149
- Integer :event_type_id, :null => false
152
+ String :event_type, :null => false
153
+ String :group, :null => true
150
154
  Integer :group_id, :null => true
155
+ String :service, :null => true
151
156
  Integer :service_id, :null => true
157
+ String :user, :null => true
152
158
  Integer :user_id, :null => true
153
159
  # TODO Some sort of boolean or enum completion status?
154
160
  timestamp :created_at, :null => false
155
161
  # TODO Some sort of completed_at timestamp?
156
162
 
157
- index :event_type_id
163
+ index :event_type
158
164
  index :group_id
159
165
  index :service_id
160
166
  index :user_id
data/lib/goz/event.rb CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  require 'goz/database'
4
4
  require 'goz/event_type'
5
+ require 'goz/group'
6
+ require 'goz/service'
7
+ require 'json'
5
8
 
6
9
  module Goz # :nodoc:
7
10
 
@@ -18,6 +21,9 @@ module Goz # :nodoc:
18
21
  #
19
22
  class Event < Sequel::Model
20
23
 
24
+ plugin :serialization, :json, :group
25
+ plugin :serialization, :json, :service
26
+ plugin :serialization, :json, :user
21
27
  plugin :validation_helpers
22
28
 
23
29
 
@@ -31,68 +37,124 @@ module Goz # :nodoc:
31
37
  end
32
38
 
33
39
  def self.admin_add(group, user)
34
- self.create( :event_type_id => Goz::EventType::ADMIN_ADD, :group_id => group.id, :user_id => user.id )
40
+ self.create :event_type => Goz::EventType::ADMIN_ADD,
41
+ :group => group,
42
+ :group_id => group.id,
43
+ :user => user,
44
+ :user_id => user.id
35
45
  end
36
46
 
37
47
  def self.admin_remove(group, user)
38
- self.create( :event_type_id => Goz::EventType::ADMIN_REMOVE, :group_id => group.id, :user_id => user.id )
48
+ self.create :event_type => Goz::EventType::ADMIN_REMOVE,
49
+ :group => group,
50
+ :group_id => group.id,
51
+ :user => user,
52
+ :user_id => user.id
39
53
  end
40
54
 
41
55
  def self.group_add(group)
42
- self.create :event_type_id => Goz::EventType::GROUP_ADD, :group_id => group.id
56
+ self.create :event_type => Goz::EventType::GROUP_ADD,
57
+ :group => group,
58
+ :group_id => group.id
43
59
  end
44
60
 
45
61
  def self.group_remove(group)
46
- self.create :event_type_id => Goz::EventType::GROUP_REMOVE, :group_id => group.id
62
+ self.create :event_type => Goz::EventType::GROUP_REMOVE,
63
+ :group => group,
64
+ :group_id => group.id
47
65
  end
48
66
 
49
67
  def self.group_sync(group)
50
- self.create :event_type_id => Goz::EventType::GROUP_SYNC, :group_id => group.id
68
+ self.create :event_type => Goz::EventType::GROUP_SYNC,
69
+ :group => group,
70
+ :group_id => group.id
51
71
  end
52
72
 
53
73
  def self.group_service_add(group, service)
54
- self.create( :event_type_id => Goz::EventType::GROUP_SERVICE_ADD, :group_id => group.id, :service_id => service.id )
74
+ self.create :event_type => Goz::EventType::GROUP_SERVICE_ADD,
75
+ :group => group,
76
+ :group_id => group.id,
77
+ :service => service,
78
+ :service_id => service.id
55
79
  end
56
80
 
57
81
  def self.group_service_remove(group, service)
58
- self.create( :event_type_id => Goz::EventType::GROUP_SERVICE_REMOVE, :group_id => group.id, :service_id => service.id )
82
+ self.create :event_type => Goz::EventType::GROUP_SERVICE_REMOVE,
83
+ :group => group,
84
+ :group_id => group.id,
85
+ :service => service,
86
+ :service_id => service.id
59
87
  end
60
88
 
61
89
  def self.group_service_sync(group, service)
62
- self.create( :event_type_id => Goz::EventType::GROUP_SERVICE_SYNC, :group_id => group.id, :service_id => service.id )
90
+ self.create :event_type => Goz::EventType::GROUP_SERVICE_SYNC,
91
+ :group => group,
92
+ :group_id => group.id,
93
+ :service => service,
94
+ :service_id => service.id
63
95
  end
64
96
 
65
97
  def self.member_add(group, user)
66
- self.create( :event_type_id => Goz::EventType::MEMBER_ADD, :group_id => group.id, :user_id => user.id )
98
+ self.create :event_type => Goz::EventType::MEMBER_ADD,
99
+ :group => group,
100
+ :group_id => group.id,
101
+ :user => user,
102
+ :user_id => user.id
67
103
  end
68
104
 
69
105
  def self.member_remove(group, user)
70
- self.create( :event_type_id => Goz::EventType::MEMBER_REMOVE, :group_id => group.id, :user_id => user.id )
106
+ self.create :event_type => Goz::EventType::MEMBER_REMOVE,
107
+ :group => group,
108
+ :group_id => group.id,
109
+ :user => user,
110
+ :user_id => user.id
71
111
  end
72
112
 
73
113
  def self.service_add(service)
74
- self.create( :event_type_id => Goz::EventType::SERVICE_ADD, :service_id => service.id )
114
+ self.create :event_type => Goz::EventType::SERVICE_ADD,
115
+ :service => service,
116
+ :service_id => service.id
75
117
  end
76
118
 
77
119
  def self.service_remove(service)
78
- self.create( :event_type_id => Goz::EventType::SERVICE_REMOVE, :service_id => service.id )
120
+ self.create :event_type => Goz::EventType::SERVICE_REMOVE,
121
+ :service => service,
122
+ :service_id => service.id
79
123
  end
80
124
 
81
125
  def self.service_sync(service)
82
- self.create( :event_type_id => Goz::EventType::SERVICE_SYNC, :service_id => service.id )
126
+ self.create :event_type => Goz::EventType::SERVICE_SYNC,
127
+ :service => service,
128
+ :service_id => service.id
129
+ end
130
+
131
+ def self.user_add(user)
132
+ self.create :event_type => Goz::EventType::USER_ADD,
133
+ :user => user,
134
+ :user_id => user.id
135
+ end
136
+
137
+ def self.user_remove(user)
138
+ self.create :event_type => Goz::EventType::USER_REMOVE,
139
+ :user => user,
140
+ :user_id => user.id
83
141
  end
84
142
 
85
143
  def self.user_sync(user)
86
- self.create( :event_type_id => Goz::EventType::USER_SYNC, :user_id => user.id )
144
+ self.create :event_type => Goz::EventType::USER_SYNC,
145
+ :user => user,
146
+ :user_id => user.id
87
147
  end
88
148
 
149
+
89
150
  #
90
151
  # Perform model valiations.
91
152
  #
92
153
  def validate
93
154
  super
94
- validates_presence :event_type_id
95
- validates_type Integer, [ :event_type_id, :group_id, :service_id, :user_id ]
155
+ validates_presence :event_type
156
+ validates_includes Goz::EventType::TYPES, :event_type
157
+ validates_type Integer, [ :group_id, :service_id, :user_id ]
96
158
  end
97
159
 
98
160
 
@@ -19,8 +19,8 @@ module Goz # :nodoc:
19
19
 
20
20
  plugin :validation_helpers
21
21
 
22
- TYPES = EventType.to_hash(:name, :id)
23
- TYPES.each_pair { |k, v| const_set k.upcase.to_s, v }
22
+ TYPES = EventType.map(:name)
23
+ TYPES.each { |t| const_set t.upcase.to_s, t }
24
24
 
25
25
 
26
26
  #
@@ -41,7 +41,7 @@ module Goz # :nodoc:
41
41
  self.created_at ||= Time.now
42
42
  end
43
43
 
44
- end # class EventType
44
+ end
45
45
 
46
- end # module Goz
46
+ end
47
47
 
data/lib/goz/group.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'active_support/inflector'
3
4
  require 'goz/database'
4
5
  require 'goz/event'
5
6
  require 'goz/group_service'
7
+ require 'goz/logger'
8
+
6
9
 
7
10
  module Goz # :nodoc:
8
11
 
@@ -34,7 +37,6 @@ module Goz # :nodoc:
34
37
  # https://github.com/blairc/goz/
35
38
  #
36
39
  class Group < Sequel::Model
37
- include Comparable
38
40
 
39
41
  plugin :validation_helpers
40
42
 
@@ -57,67 +59,85 @@ module Goz # :nodoc:
57
59
  :right_key => :service_id,
58
60
  :join_table => :group_services,
59
61
  :class => :'Goz::Service',
60
- :after_add => proc { |g, s| Goz::Event.group_service_add(g, s) },
61
- :after_remove => proc { |g, s| Goz::Event.group_service_remove(g, s) }
62
+ :after_add => proc { |g, s| after_add_service g, s },
63
+ :after_remove => proc { |g, s| after_remove_service g, s }
64
+
65
+
66
+ ATTRIBUTES = [ :display_name, :identifier, :klass, :name ]
67
+ TAG = self.name
68
+
69
+ @@api = Goz::Group
62
70
 
63
71
 
64
72
  #
65
- # Initialize new (non-persistent) Goz::Group object
73
+ # Return groups where user is an admin.
66
74
  #
67
- def initialize(*params)
68
- super
69
- yield self if block_given?
70
- self
75
+ def self.admin(user)
76
+ Goz::Logger.debug TAG, "admin( user=#{user.login} )"
77
+ return user.groups if Goz::Group.name == @@api.name
78
+ groups = self.api.groups(user).collect { |g| self.find_or_create g.to_hash }
79
+ groups.each { |g| yield g } if block_given?
80
+ groups
71
81
  end
72
82
 
73
- #
74
- # Compare equality.
75
- #
76
- def <=>(other)
77
- if self.display_name < other.display_name ||
78
- self.identifier < other.identifier ||
79
- self.name < other.name
80
- -1
81
- elsif self.display_name > other.display_name ||
82
- self.identifier > other.identifier ||
83
- self.name > other.name
84
- 1
85
- else
86
- 0
87
- end
83
+ # TODO DRY w/ "Service"
84
+ def self.after_add_service(g, s)
85
+ s.create(g) && Goz::Event.group_service_add(g, s)
86
+ end
87
+
88
+ # TODO DRY w/ "Service"
89
+ def self.after_remove_service(g, s)
90
+ s.destroy(g) && Goz::Event.group_service_remove(g, s)
88
91
  end
89
92
 
90
93
  #
91
- # Create Goz::Group or raise +RuntimeError+
94
+ # TODO
92
95
  #
93
- def self.create( params = {} )
94
- params = {} unless params.kind_of?(Hash)
95
- [ :display_name, :identifier, :name ].each do |k|
96
- raise( "invalid #{k.to_s}" ) if ( params[k].nil? || params[k].empty? )
97
- end
98
- super
96
+ def self.api( klass = nil, configuration = {} )
97
+ return @@api if klass.nil?
98
+ Goz::Logger.info TAG, "changing api from #{@@api} to #{klass}"
99
+ klass_name = klass.kind_of?(String) ? klass : klass.name
100
+ require klass_name.underscore
101
+ klass = klass.constantize if klass.kind_of?(String)
102
+ klass.configuration configuration
103
+ @@api = klass
99
104
  end
100
105
 
101
106
  #
102
107
  # Find Goz::Group by name or return +nil+
103
108
  #
104
109
  def self.find_by_name(name)
105
- self.find( :name => name )
110
+ Goz::Logger.debug TAG, "find_by_name( name=#{name} )"
111
+ g = self.find :name => name
112
+ return g unless g.nil?
113
+ return nil if Goz::Group == self.api
114
+ g = self.api.find_by_name name
115
+ return nil if g.nil?
116
+ return self.find_or_create g.to_hash
117
+ end
118
+
119
+ #
120
+ # Return groups where user is a member.
121
+ #
122
+ def self.member(user)
123
+ Goz::Logger.debug TAG, "member( user=#{user.login} )"
124
+ return user.memberships if Goz::Group.name == @@api.name
125
+ groups = self.api.memberships(user).collect { |g| self.find_or_create g.to_hash }
126
+ groups.each { |g| yield g } if block_given?
127
+ groups
106
128
  end
107
129
 
108
130
  #
109
131
  # Synchronize group with external data sources (if relevant).
110
132
  #
111
133
  def sync(force = false)
112
- if force || synchronized_at.nil? || synchronized_at < ( Time.now - ( 1 * 60 * 60 ) ) # XXX
113
- # XXX Synchronize group from remote source (if necessary)
114
- # XXX Synchronize admins
115
- # XXX Synchronize members
116
- self.services.each { |s| self.sync_service(s, force) }
117
- self.synchronized_at = Time.now
118
- self.save
119
- Goz::Event.group_sync(self)
120
- return true
134
+ Goz::Logger.debug TAG, "#{self.name} - #sync( force=#{force} )"
135
+ if sync_group && sync_admins && sync_members
136
+ self.services.each { |s| self.sync_service(s, force) } # TODO
137
+ self.synchronized_at = Time.now
138
+ self.save
139
+ Goz::Event.group_sync(self)
140
+ return true
121
141
  end
122
142
  false
123
143
  end
@@ -126,7 +146,12 @@ module Goz # :nodoc:
126
146
  # Synchronize group service with external data soruces (if relevant).
127
147
  #
128
148
  def sync_service(service, force = false )
129
- Goz::GroupService.first( :group_id => self.id, :service_id => service.id ).sync(force)
149
+ Goz::Logger.debug TAG, "#{self.name} - #sync_service( service.name=#{service.name}, force=#{force} )"
150
+ service.sync_group(self) # XXX
151
+ end
152
+
153
+ def to_hash
154
+ Hash[ ATTRIBUTES.map { |k| [ k, self[k] ] } ]
130
155
  end
131
156
 
132
157
  #
@@ -134,7 +159,7 @@ module Goz # :nodoc:
134
159
  #
135
160
  def validate
136
161
  super
137
- validates_presence [ :display_name, :identifier, :name ]
162
+ validates_presence [ :display_name, :identifier, :klass, :name ]
138
163
  validates_type Time, [ :created_at, :modified_at, :synchronized_at ]
139
164
  validates_unique [ :display_name, :identifier, :name ]
140
165
  end
@@ -168,7 +193,59 @@ module Goz # :nodoc:
168
193
  self.modified_at = Time.now
169
194
  end
170
195
 
171
- end # class Group
196
+ def klass_instance
197
+ return @@api if @@api.name == self.klass
198
+ unless @k
199
+ @k = self.klass.constantize
200
+ require self.klass.underscore
201
+ end
202
+ return @k
203
+ end
204
+
205
+ # Synchronize group admins with external data source
206
+ def sync_admins
207
+ Goz::Logger.debug TAG, "#{self.name} - #sync_admins()"
208
+ return true if Goz::Group.name == self.klass
209
+ g = klass_instance.find_by_name self.name
210
+ return false if g.nil?
211
+ t = g.admins.collect { |u| Goz::User.find_by_login u.login } # TODO Should be .find( key => value )
212
+ t.delete_if { |_| _.nil? }
213
+ t.each { |_| self.add_admin(_) unless self.admins.include?(_) }
214
+ self.admins.each { |_| self.remove_admin(_) unless t.include?(_) }
215
+ # TODO Log! if modified?
216
+ true
217
+ end
218
+
219
+ # Synchronize group with external data source
220
+ def sync_group
221
+ Goz::Logger.debug TAG, "#{self.name} - #sync_group()"
222
+ return true if Goz::Group.name == self.klass
223
+ g = klass_instance.find_by_name self.name
224
+ return false if g.nil?
225
+ [ :display_name, :identifier, :name ].each do |attr|
226
+ unless self[attr] == g[attr]
227
+ Goz::Logger.info TAG, "#{self.name} = #sync_group() - #{attr} changed"
228
+ self[attr] = g[attr]
229
+ end
230
+ end
231
+ true
232
+ end
233
+
234
+ # Synchronize groups members with external data source
235
+ def sync_members
236
+ Goz::Logger.debug TAG, "#{self.name} - #sync_members()"
237
+ return true if Goz::Group.name == self.klass
238
+ g = klass_instance.find_by_name self.name
239
+ return false if g.nil?
240
+ t = g.members.collect { |u| Goz::User.find_by_login u.login } # TODO Should be .find( key => value )
241
+ t.delete_if { |_| _.nil? }
242
+ t.each { |_| self.add_member(_) unless self.members.include?(_) }
243
+ self.members.each { |_| self.remove_member(_) unless t.include?(_) }
244
+ # TODO Log! if modified?
245
+ true
246
+ end
247
+
248
+ end
172
249
 
173
- end # module Goz
250
+ end
174
251