glue 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,5 +1,71 @@
1
+ 15-04-2005 George Moschovitis <gm@navel.gr>
2
+
3
+ * lib/glue/aspects.rb (#append_features): fixed stupid include bug!
4
+ improved inherited code,
5
+ fix: use __meta to keep the advices.
6
+
7
+ * lib/glue/property.rb: include Aspects if calling property.
8
+
9
+ 14-04-2005 George Moschovitis <gm@navel.gr>
10
+
11
+ * lib/glue/aspects.rb: fixed proc aspect,
12
+ handle Module inclusion.
13
+
14
+ 13-04-2005 George Moschovitis <gm@navel.gr>
15
+
16
+ * lib/glue/aspects.rb (#include_advice_modules): iplemented.
17
+ cooler, added support for Aspects as modules,
18
+ added some aliases.
19
+
20
+ 12-04-2005 George Moschovitis <gm@navel.gr>
21
+
22
+ * Rakefile: updated to support autoupload to Rubyforge. [flgr]
23
+
24
+ 11-04-2005 George Moschovitis <gm@navel.gr>
25
+
26
+ * test/glue/tc_aspects.rb: introduced and implemented tests.
27
+
28
+ * lib/glue/aspects.rb: introduced,
29
+ copied code from /lib/nitro/filters.rb
30
+ (Aspect): introduced.
31
+ (Advice): introduced,
32
+ it works :)
33
+ (#wrap): handle methods with any arity.
34
+ use join instead of when.
35
+ (##wrap): recoded implementation.
36
+ allow for instance and class aspects,
37
+ many small fixes.
38
+
39
+ * created new public SVN repository, to allow for easier
40
+ patching by the community.
41
+
42
+ 09-04-2005 George Moschovitis <gm@navel.gr>
43
+
44
+ * test/*: fixes to pass again.
45
+
46
+ * lib/glue/flexob.rb (#update): added.
47
+
48
+ 08-04-2005 George Moschovitis <gm@navel.gr>
49
+
50
+ * Rakefile: removed flexob, extensions, ruby-breakpointer dependencies [james_b],
51
+ bumped required Ruby version.
52
+
53
+ * test/*: many small fixes.
54
+
55
+ * Rakefile: fixed to run tests.
56
+
57
+ * lib/glue.rb: include in TOPLEVEL binding for easy access.
58
+
59
+ * lib/glue/dynamic_include.rb: added alias.
60
+
61
+ * lib/glue/attribute.rb: removed call_xx method.
62
+
63
+ * MAJOR: changed the namespace from N to Glue.
64
+
1
65
  01-04-2005 George Moschovitis <gm@navel.gr>
2
66
 
67
+ * --- VERSION 0.15.0 ---
68
+
3
69
  * doc/RELEASES: updated.
4
70
 
5
71
  * README: updated.
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = Glue 0.15.0
1
+ = Glue 0.16.0 README
2
2
 
3
3
  Useful utilites and methods.
4
4
 
data/Rakefile CHANGED
@@ -6,11 +6,34 @@ require 'rake/rdoctask'
6
6
  require 'rake/testtask'
7
7
  require 'rake/gempackagetask'
8
8
 
9
+ # Initialize some variables.
10
+
11
+ readme = File.read('README')
12
+ Release = (readme[/^= (.+) README$/, 1] || 'unknown').downcase.tr(" ", "-")
13
+ Name = Release[/\D+/].sub(/\-+$/, '') || 'unknown'
14
+ Version = Release[/[\d.]+/] || 'unknown'
15
+
16
+ AuthorName, AuthorMail = 'George Moschovitis', 'gm@navel.gr'
17
+ RubyVersion = '1.8.2'
18
+
19
+ # Description = (readme[/README\s+(.+?)\n\n/m, 1] || "unknown").gsub(/\s+/, " ")
20
+ # Summary = Description[/^.+?>/] || "unknown"
21
+
22
+ # DocFiles = %w(README NEWS TODO COPYING GPL)
23
+ # RDocFiles = DocFiles - %w(GPL)
24
+ RDocOpts = ["--inline-source", "--line-numbers","--title", Name ]
25
+ # AdditionalFiles = DocFiles + %w(setup.rb)
26
+ VersionFile = MainFile = File.join("lib", Name + '.rb')
27
+
28
+ RubyForgeProject = 'nitro'
29
+ RubyForgeUser = 'gmosx'
30
+ Homepage = "http://#{RubyForgeProject}.rubyforge.org/"
31
+
9
32
  task :default => :package
10
33
 
11
34
  # Run all tests.
12
35
 
13
- Rake::TestTask.new(:test_all) do |t|
36
+ Rake::TestTask.new do |t|
14
37
  t.libs << 'test'
15
38
  t.test_files = FileList['test/**/*.rb']
16
39
  t.verbose = true
@@ -20,7 +43,7 @@ end
20
43
 
21
44
  Rake::RDocTask.new do |rd|
22
45
  rd.main = 'README'
23
- rd.rdoc_dir = 'doc/rdoc'
46
+ rd.rdoc_dir = 'rdoc'
24
47
  rd.rdoc_files.include('README', 'INSTALL', 'lib/**/*.rb')
25
48
  rd.options << '--all --inline-source'
26
49
  end
@@ -36,10 +59,10 @@ spec = Gem::Specification.new do |s|
36
59
  end
37
60
  s.summary = 'Glue utilities'
38
61
  s.description = 'A collection of utilities and useful classes'
39
- s.add_dependency 'extensions', '>= 0.5'
40
- s.add_dependency 'flexmock', '>= 0.0.3'
62
+ # s.add_dependency 'extensions', '>= 0.5'
63
+ # s.add_dependency 'flexmock', '>= 0.0.3'
41
64
 
42
- s.required_ruby_version = '>= 1.8.1'
65
+ s.required_ruby_version = '>= 1.8.2'
43
66
 
44
67
  s.files = FileList[
45
68
  '[A-Z]*', 'install.rb', '{doc,lib,test,vendor}/**/*'
@@ -75,3 +98,123 @@ task :install do
75
98
  ruby 'install.rb'
76
99
  end
77
100
 
101
+ # Release files to Rubyforge.
102
+ # The code for this task provided by Florian Gross.
103
+
104
+ desc "Publish the release files to RubyForge."
105
+ task :publish => [:package] do
106
+ files = ['gem', 'tgz', 'zip'].map { |ext| "dist/#{Release}.#{ext}" }
107
+
108
+ if RubyForgeProject then
109
+ require 'net/http'
110
+ require 'open-uri'
111
+
112
+ changes = ''
113
+ if File.exist?('doc/RELEASES') then
114
+ changes_re = /^== \s+ Version \s+ #{Regexp.quote(Version)} \s*
115
+ (.+?) (?:==|\Z)/mx
116
+ changes = File.read('doc/RELEASES')[changes_re, 1] || ''
117
+ end
118
+
119
+ project_uri = "http://rubyforge.org/projects/#{RubyForgeProject}/"
120
+ project_data = open(project_uri) { |data| data.read }
121
+ group_id = project_data[/[?&]group_id=(\d+)/, 1]
122
+ raise "Couldn't get group id" unless group_id
123
+
124
+ print "#{RubyForgeUser}@rubyforge.org's password: "
125
+ password = STDIN.gets.chomp
126
+
127
+ login_response = Net::HTTP.start('rubyforge.org', 80) do |http|
128
+ data = [
129
+ "login=1",
130
+ "form_loginname=#{RubyForgeUser}",
131
+ "form_pw=#{password}"
132
+ ].join("&")
133
+ http.post('/account/login.php', data)
134
+ end
135
+
136
+ cookie = login_response['set-cookie']
137
+ raise 'Login failed' unless cookie
138
+ headers = { 'Cookie' => cookie }
139
+
140
+ release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
141
+ release_data = open(release_uri, headers) { |data| data.read }
142
+ package_id = release_data[/[?&]package_id=(\d+)/, 1]
143
+ raise "Couldn't get package id" unless package_id
144
+
145
+ first_file = true
146
+ release_id = ""
147
+
148
+ files.each do |filename|
149
+ basename = File.basename(filename)
150
+ file_ext = File.extname(filename)
151
+ file_data = File.open(filename, "rb") { |file| file.read }
152
+
153
+ puts "Releasing #{basename}..."
154
+
155
+ release_response = Net::HTTP.start('rubyforge.org', 80) do |http|
156
+ release_date = Time.now.strftime('%Y-%m-%d %H:%M')
157
+ type_map = {
158
+ '.zip' => '3000',
159
+ '.tgz' => '3110',
160
+ '.gz' => '3110',
161
+ '.gem' => '1400',
162
+ '.md5sum' => '8100'
163
+ }; type_map.default = '9999'
164
+ type = type_map[file_ext]
165
+ boundary = 'rubyqMY6QN9bp6e4kS21H4y0zxcvoor'
166
+
167
+ query_hash = if first_file then
168
+ {
169
+ 'group_id' => group_id,
170
+ 'package_id' => package_id,
171
+ 'release_name' => Release,
172
+ 'release_date' => release_date,
173
+ 'type_id' => type,
174
+ 'processor_id' => '8000', # Any
175
+ 'release_notes' => '',
176
+ 'release_changes' => changes,
177
+ 'preformatted' => '1',
178
+ 'submit' => '1'
179
+ }
180
+ else
181
+ {
182
+ 'group_id' => group_id,
183
+ 'release_id' => release_id,
184
+ 'package_id' => package_id,
185
+ 'step2' => '1',
186
+ 'type_id' => type,
187
+ 'processor_id' => '8000', # Any
188
+ 'submit' => 'Add This File'
189
+ }
190
+ end
191
+
192
+ query = '?' + query_hash.map do |(name, value)|
193
+ [name, URI.encode(value)].join('=')
194
+ end.join('&')
195
+
196
+ data = [
197
+ "--" + boundary,
198
+ "Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
199
+ "Content-Type: application/octet-stream",
200
+ "Content-Transfer-Encoding: binary",
201
+ "", file_data, ""
202
+ ].join("\x0D\x0A")
203
+
204
+ release_headers = headers.merge(
205
+ 'Content-Type' => "multipart/form-data; boundary=#{boundary}"
206
+ )
207
+
208
+ target = first_file ? '/frs/admin/qrs.php' : '/frs/admin/editrelease.php'
209
+ http.post(target + query, data, release_headers)
210
+ end
211
+
212
+ if first_file then
213
+ release_id = release_response.body[/release_id=(\d+)/, 1]
214
+ raise("Couldn't get release id") unless release_id
215
+ end
216
+
217
+ first_file = false
218
+ end
219
+ end
220
+ end
data/doc/RELEASES CHANGED
@@ -1,11 +1,42 @@
1
- == Version 0.15.0 was released on 4/04/2005.
1
+ == Version 0.16.0
2
+
3
+ A snapshot of the latest developments. Many, many subtle improvements,
4
+ new features and a major cleanup of the source code.
5
+
6
+ Most notable attitions:
7
+
8
+ * Aspect Oriented Programming support. This new system
9
+ is used to reimplement features such as Controller filters,
10
+ Og callbacks and Og observers. By using this unified
11
+ system you can now add Observers to controllers and use
12
+ a metalanguage for wraping Og object callbacks:
13
+
14
+ class Controller
15
+ pre :force_login, :where => :prepend
16
+ wrap Benchmark, :on => :index
17
+ post :taraa, :on => login
18
+ end
19
+
20
+ module Timestamped
21
+ pre :on => :og_insert { |this| this.create_time = Time.now }
22
+ pre :on => :og_update { |this| this.update_time = Time.now }
23
+ pre :on => [:og_insert, :og_update] { |this| this.create_time = Time.now }
24
+ end
25
+
26
+ This feature will be used extensivelly in future versions
27
+ to improve logging, the shaders and more.
28
+
29
+
30
+ == Version 0.15.0 was released on 04/04/2005.
2
31
 
3
32
  * Dynamic include.
4
33
 
34
+
5
35
  == Version 0.14.0 was released on 28/03/2005.
6
36
 
7
37
  * Fixes nasty property inheritance bug.
8
38
 
39
+
9
40
  == Version 0.13.0 was released on 17/03/2005.
10
41
 
11
42
  The first release as a standalone gem.
data/install.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # * George Moschovitis <gm@navel.gr>
4
4
  # (c) 2004-2005 Navel, all rights reserved.
5
- # $Id$
5
+ # $Id: install.rb 1 2005-04-11 11:04:30Z gmosx $
6
6
 
7
7
  require 'rbconfig'
8
8
  require 'ftools'
data/lib/glue.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # George Moschovitis <gm@navel.gr>
6
6
  # (c) 2004-2005 Navel, all rights reserved.
7
- # $Id: glue.rb 340 2005-04-04 08:26:58Z gmosx $
7
+ # $Id: glue.rb 1 2005-04-11 11:04:30Z gmosx $
8
8
 
9
9
  require 'English'
10
10
  require 'pp'
@@ -12,10 +12,6 @@ require 'pp'
12
12
  require 'glue/property'
13
13
  require 'glue/attribute'
14
14
 
15
- # The standard namespace module.
16
-
17
- module N; end
18
-
19
15
  class NilClass
20
16
 
21
17
  # quite usefull for error tolerant apps.
@@ -57,10 +53,14 @@ module Glue
57
53
 
58
54
  # The version.
59
55
 
60
- Version = '0.15.0'
56
+ Version = '0.16.0'
61
57
 
62
58
  # Library path.
63
59
 
64
60
  LibPath = File.dirname(__FILE__)
65
61
 
66
62
  end
63
+
64
+ # Include in the top level binding for easy access.
65
+
66
+ include Glue
data/lib/glue/array.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2002-2005 Navel, all rights reserved.
3
- # $Id: array.rb 282 2005-03-10 12:24:53Z gmosx $
3
+ # $Id: array.rb 1 2005-04-11 11:04:30Z gmosx $
4
4
 
5
5
  require 'sync'
6
6
 
7
- module N
7
+ module Glue
8
8
 
9
9
  # A thread-safe array. We use a sync object instead of a
10
10
  # mutex, because it is re-entrant. An exclusive lock is
@@ -0,0 +1,237 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2005 Navel, all rights reserved.
3
+ # $Id: aspects.rb 20 2005-04-15 15:18:36Z gmosx $
4
+
5
+ require 'glue/property'
6
+
7
+ module Glue
8
+
9
+ # An Aspect is a class that defines advices.
10
+
11
+ class Aspect
12
+ class << self
13
+ def wrap(target, methods = target.instance_methods, pre = :pre, post = :post)
14
+ target.send(:include, Aspects) unless target.ancestors.include?(Aspects)
15
+ target.wrap(self, :pre => pre, :post => post)
16
+ end
17
+
18
+ alias_method :observe, :wrap
19
+ end
20
+
21
+ def wrap(target, methods = target.instance_methods, pre = :pre, post = :post)
22
+ target.send(:include, Aspects) unless target.ancestors.include?(Aspects)
23
+ target.wrap(self, :pre => pre, :post => post)
24
+ end
25
+ alias_method :observe, :wrap
26
+ end
27
+
28
+ # Add support for Aspect Oriented Programming (AOP).
29
+ #
30
+ # === Examples
31
+ #
32
+ # class Controller
33
+ # pre :force_login, :where => :prepend
34
+ # wrap Benchmark, :on => :index
35
+ # post :taraa, :on => login
36
+ # end
37
+ #
38
+ # module Timestamped
39
+ # pre :on => :og_insert { |this| this.create_time = Time.now }
40
+ # pre :on => :og_update { |this| this.update_time = Time.now }
41
+ # pre :on => [:og_insert, :og_update] { |this| this.create_time = Time.now }
42
+ # end
43
+
44
+ module Aspects
45
+
46
+ # Store the code and the metadata (options) for
47
+ # an Advice.
48
+
49
+ Advice = Struct.new(:code, :options)
50
+
51
+ # Apply the advices to the target class.
52
+
53
+ def self.wrap(target, methods = target.instance_methods)
54
+ include_advice_modules(target)
55
+
56
+ for m in [methods].flatten
57
+ args = []
58
+ target.instance_method(m).arity.times { |i| args << "a#{i}" }
59
+ args = args.join(',')
60
+
61
+ target.module_eval <<-end_eval, __FILE__, __LINE__
62
+ alias_method :__unwrapped_#{m}, :#{m}
63
+ def #{m}(#{args})
64
+ #{gen_advice_code(m, target.advices, :pre)}
65
+ __unwrapped_#{m}(#{args})
66
+ #{gen_advice_code(m, target.advices, :post)}
67
+ end
68
+ end_eval
69
+ end
70
+ end
71
+
72
+ # Include Modules that define advices.
73
+
74
+ def self.include_advice_modules(target)
75
+ for a in target.advices
76
+ if a.code.is_a?(Module) and (!a.code.class.ancestors.include?(Class))
77
+ target.module_eval %{ include #{a.code} }
78
+
79
+ options = a.options.reject { |k,v| k == :pre || k == :post }
80
+
81
+ method = (a.options[:pre] || 'pre').to_s
82
+ if a.code.instance_methods.include?(method)
83
+ options.update(:where => :prepend, :join => :pre)
84
+ target.advices << Advice.new(method, options)
85
+ end
86
+
87
+ method = (a.options[:post] || 'post').to_s
88
+ if a.code.instance_methods.include?(method)
89
+ options.update(:where => :append, :join => :post)
90
+ target.advices << Advice.new(method, options)
91
+ end
92
+ end
93
+ end
94
+
95
+ # Remove the original advice.
96
+
97
+ target.advices.delete_if do |a|
98
+ a.code.is_a?(Module) and (!a.code.class.ancestors.include?(Class))
99
+ end
100
+ end
101
+
102
+ # Generates the code to call the aspects.
103
+
104
+ def self.gen_advice_code(method, advices, join = :pre) # :nodoc:
105
+ code = ''
106
+
107
+ advices.each_with_index do |advice, idx|
108
+ o = options = advice.options
109
+
110
+ if only = options[:only] || options[:on]
111
+ next unless [only].flatten.include?(method.to_sym)
112
+ elsif except = options[:except]
113
+ next if [except].flatten.include?(method.to_sym)
114
+ end
115
+
116
+ advice = advice.code
117
+
118
+ if advice.is_a?(Symbol) or advice.is_a?(String)
119
+ next if o[:join] != join
120
+ code << "#{advice}; "
121
+ elsif advice.respond_to?('call')
122
+ next if o[:join] != join
123
+ code << "self.class.advices[#{idx}].code.call(self); "
124
+ elsif advice.is_a?(Class)
125
+ if advice.class.ancestors.include?(Class)
126
+ if m = o[join] and advice.methods.include?(m.to_s)
127
+ code << "#{advice}.#{m}(self); "
128
+ end
129
+ else
130
+ # Module, allready handled.
131
+ end
132
+ else
133
+ if m = o[join] and advice.methods.include?(m.to_s)
134
+ code << "self.class.advices[#{idx}].code.#{m}(self); "
135
+ end
136
+ end
137
+ end
138
+
139
+ return code
140
+ end
141
+
142
+ def self.append_features(base)
143
+ super
144
+ base.extend(ClassMethods)
145
+ base.module_eval %{
146
+ Glue::PropertyUtils.enchant(self)
147
+
148
+ def self.advices
149
+ __meta[:advices] || []
150
+ end
151
+
152
+ def self.advices=(advices)
153
+ __meta[:advices] = advices
154
+ end
155
+
156
+ # def self.inherited(child)
157
+ # super
158
+ # child.send(:include, Aspects)
159
+ # end
160
+ #
161
+ # def self.append_features(base)
162
+ # super
163
+ # base.send(:include, Aspects)
164
+ # end
165
+ }
166
+ end
167
+
168
+ module ClassMethods
169
+
170
+ # Add a pre (before) advice.
171
+
172
+ def pre(*args, &block)
173
+ o = options = {
174
+ :join => :pre,
175
+ :where => :prepend,
176
+ }
177
+ options.update(args.pop) if args.last.is_a?(Hash)
178
+
179
+ if block_given?
180
+ advices = [ Advice.new(block, options) ]
181
+ else
182
+ advices = args.collect { |a| Advice.new(a, options) }
183
+ end
184
+
185
+ if options[:where] == :prepend
186
+ self.advices = advices + self.advices
187
+ else
188
+ self.advices = self.advices + advices
189
+ end
190
+ end
191
+ alias_method :before, :pre
192
+
193
+ # Add a post (after) advice.
194
+
195
+ def post(*args, &block)
196
+ o = options = {
197
+ :join => :post,
198
+ :where => :append,
199
+ }
200
+ options.update(args.pop) if args.last.is_a?(Hash)
201
+
202
+ if block_given?
203
+ advices = [ Advice.new(block, options) ]
204
+ else
205
+ advices = args.collect { |a| Advice.new(a, options) }
206
+ end
207
+
208
+ if options[:where] == :prepend
209
+ self.advices = advices + self.advices
210
+ else
211
+ self.advices = self.advices + advices
212
+ end
213
+ end
214
+ alias_method :after, :post
215
+
216
+ # Add a wrap (arround) aspect. An aspect is a class that
217
+ # responds to the before and after advices.
218
+
219
+ def wrap(*args)
220
+ o = options = {
221
+ :pre => :pre,
222
+ :post => :post
223
+ }
224
+ options.update(args.pop) if args.last.is_a?(Hash)
225
+
226
+ for aspect in args
227
+ self.advices << Advice.new(aspect, options)
228
+ end
229
+ end
230
+ alias_method :around, :wrap
231
+ alias_method :observer, :wrap
232
+
233
+ end
234
+
235
+ end
236
+
237
+ end