glue 0.15.0 → 0.16.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.
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