drx 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'activerecord'
3
+
4
+ #
5
+ # This is part of a blogging website. Users write posts. A post
6
+ # belongs to a user.
7
+ #
8
+
9
+ ActiveRecord::Base.logger = Logger.new(STDERR)
10
+ ActiveRecord::Base.colorize_logging = false
11
+
12
+ ActiveRecord::Base.establish_connection(
13
+ :adapter => 'sqlite3',
14
+ :database => ':memory:'
15
+ )
16
+
17
+ ActiveRecord::Schema.define do
18
+ create_table :users do |table|
19
+ table.column :name, :string
20
+ table.column :mail, :string
21
+ end
22
+ create_table :posts do |table|
23
+ table.column :user_id, :integer
24
+ table.column :title, :string
25
+ table.column :body, :string
26
+ end
27
+ end
28
+
29
+ class User < ActiveRecord::Base
30
+ has_many :posts
31
+ end
32
+
33
+ class Post < ActiveRecord::Base
34
+ belongs_to :users
35
+ end
36
+
37
+ david = User.create(:name => 'David')
38
+ david.posts.create(:title => 'Lies')
39
+ david.posts.create(:title => 'Truths')
40
+
41
+ post = User.find(1).posts.first
42
+
43
+ require 'drx'
44
+ post.see
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+
4
+ #
5
+ # This is part of a blogging website. Users write posts. A post
6
+ # belongs to a user.
7
+ #
8
+
9
+ DB = Sequel.sqlite(':memory:')
10
+
11
+ DB.create_table :posts do
12
+ primary_key :id
13
+ Integer :user_id
14
+ String :title
15
+ String :body
16
+ end
17
+
18
+ DB.create_table :users do
19
+ primary_key :id
20
+ String :name
21
+ String :email
22
+ end
23
+
24
+ class Post < Sequel::Model
25
+ many_to_one :user
26
+ end
27
+
28
+ class User < Sequel::Model
29
+ one_to_many :posts
30
+ end
31
+
32
+ post = Post.create(:title => 'Lies', :user => User.create(:name => 'David'))
33
+
34
+ require 'drx'
35
+ post.see
@@ -0,0 +1,103 @@
1
+ # Adds method arguments detection to ObjInfo
2
+
3
+ module Drx
4
+
5
+ class ObjInfo
6
+
7
+ class << self
8
+ # Whether to use the 'arguments' gem, if present.
9
+ # This isn't on by default because it's slow.
10
+ attr_accessor :use_arguments_gem
11
+ end
12
+
13
+ # Returns a Ruby 1.9.2-compatible array describing the arguments a method expects.
14
+ def method_arguments(method_name)
15
+ if ObjInfo.use_arguments_gem
16
+ _method_arguments__by_arguments_gem(method_name) || _method_arguments__by_arity(method_name)
17
+ else
18
+ _method_arguments__by_methopara(method_name) || _method_arguments__by_arity(method_name)
19
+ end
20
+ end
21
+
22
+ # Strategy: use the 'arguments' gem, which, in turn, uses either ParseTree (Ruby 1.8)
23
+ # or RubyParser (Ruby 1.9)
24
+ #
25
+ # pros: shows default values; works for 1.8 too.
26
+ # cons: very slow.
27
+ def _method_arguments__by_arguments_gem(method_name)
28
+ @@once__arguments ||= begin
29
+ begin
30
+ require 'arguments'
31
+ rescue LoadError
32
+ # Not installed.
33
+ end
34
+ 1
35
+ end
36
+ return nil if not defined? Arguments
37
+ args = Arguments.names(the_object, method_name, false)
38
+ # Convert this to a Ruby 1.9.2 format:
39
+ return args.map do |arg|
40
+ if arg.size == 2
41
+ [:opt, arg[0], arg[1]]
42
+ else
43
+ name = arg[0].to_s
44
+ case name[0,1]
45
+ when '*' then [:rest, name[1..-1]]
46
+ when '&' then [:block, name[1..-1]]
47
+ else [:req, name]
48
+ end
49
+ end
50
+ end
51
+ rescue SyntaxError => e
52
+ # We could just return nil here, to continue to the next strategy,
53
+ # but we want to inform the user of the suckiness of RubyParser.
54
+ raise
55
+ rescue Exception
56
+ nil
57
+ end
58
+
59
+ # Strategy: use Method#parameters (for ruby 1.9 only).
60
+ #
61
+ # pros: fast.
62
+ # cons: doesn't show default values.
63
+ def _method_arguments__by_methopara(method_name)
64
+ @@once__methopara ||= begin
65
+ # For ruby 1.9.0 and 1.9.1, we need to use a gem.
66
+ begin
67
+ require 'methopara'
68
+ rescue LoadError
69
+ # Not installed.
70
+ end
71
+ 1
72
+ end
73
+ method = the_object.instance_method(method_name)
74
+ if method.respond_to? :parameters
75
+ return method.parameters
76
+ end
77
+ rescue NotImplementedError
78
+ # For some methods #parameters raises an exception. We return nil
79
+ # to move on to the next strategy.
80
+ return nil
81
+ end
82
+
83
+ # Strategy: simulation via Method#arity.
84
+ #
85
+ # This is used as a fallback if any of the other strategies fail.
86
+ #
87
+ # pros: fast; work without any gems.
88
+ # cons: doesn't show argument names.
89
+ def _method_arguments__by_arity(method_name)
90
+ method = the_object.instance_method(method_name)
91
+ ary, rest = method.arity, false
92
+ if ary < 0
93
+ ary = -ary - 1
94
+ rest = true
95
+ end
96
+ args = [[:req]] * ary
97
+ args << [:rest] if rest
98
+ return args
99
+ end
100
+
101
+ end
102
+
103
+ end
data/lib/drx/graphviz.rb CHANGED
@@ -8,7 +8,8 @@ module Drx
8
8
  # - Windows' CMD.EXE too supports "2>&1"
9
9
  # - We're generating GIF, not PNG, because that's a format Tk
10
10
  # supports out of the box.
11
- GRAPHVIZ_COMMAND = 'dot "%s" -Tgif -o "%s" -Tcmapx -o "%s" 2>&1'
11
+ # - Each {extension} token is replaced by the filepath with that extension.
12
+ GRAPHVIZ_COMMAND = 'dot "{dot}" -Tgif -o "{gif}" -Tcmapx -o "{map}" 2>&1'
12
13
 
13
14
  @@sizes = {
14
15
  '100%' => "
@@ -128,7 +129,7 @@ module Drx
128
129
  out
129
130
  end
130
131
 
131
- def dot_fragment(opts = {}, &block) # :yield:
132
+ def dot_fragment(opts = {}, ancestors = [], &block) # :yield:
132
133
  out = ''
133
134
  # Note: since 'obj' may be a T_ICLASS, it doesn't respond to many methods,
134
135
  # including is_a?. So when we're querying things we're using Drx calls
@@ -147,9 +148,9 @@ module Drx
147
148
  return '' if seen
148
149
 
149
150
  if class_like?
150
- if spr = self.super and display_super?(spr)
151
- out << spr.dot_fragment(opts, &block)
152
- if insignificant_super_arrow?(opts)
151
+ if spr = self.super and display_super?(spr, ancestors)
152
+ out << spr.dot_fragment(opts, ancestors + [self], &block)
153
+ if insignificant_super_arrow?(opts, ancestors)
153
154
  # We don't want these relatively insignificant lines to clutter the display,
154
155
  # so we paint them lightly and tell DOT they aren't to affect the layout (width=0).
155
156
  out << "#{dot_id} -> #{spr.dot_id} [color=gray85, weight=0];" "\n"
@@ -172,6 +173,12 @@ module Drx
172
173
  # (To see the effect of this, set the weight unconditionally to '0' and
173
174
  # see the graph for DataMapper.)
174
175
  weight = t_iclass? ? 1 : 0
176
+
177
+ # However, here's a special case. DOT seems to have a bug: it sometimes
178
+ # doesn't draw the arrows going out of Class and Module (to their
179
+ # singletons). Making their weight 1 makes DOT draw them.
180
+ weight = 1 if self == _Class or self == _Module
181
+
175
182
  out << "#{dot_id} -> #{kls.dot_id} [style=dotted, weight=#{weight}];" "\n"
176
183
  out << "{ rank=same; #{dot_id}; #{kls.dot_id}; }" "\n"
177
184
  end
@@ -187,11 +194,13 @@ module Drx
187
194
  # following method is to break the cycle. Normally we break the cycle at
188
195
  # Module (and its singleton). When the user is examining a module, we
189
196
  # instead break the cycle at Class (and its singleton).
190
- def insignificant_super_arrow?(opts)
197
+ def insignificant_super_arrow?(opts, my_ancestors)
191
198
  if opts[:base].t_module?
192
- [Class, ObjInfo.new(Class).klass.the_object].include? the_object
199
+ self == _ClassS or
200
+ (self.super == _Module and (my_ancestors + [self]).include? _Class)
193
201
  else
194
- [Module, ObjInfo.new(Module).klass.the_object].include? the_object
202
+ self == _ModuleS or
203
+ (self.super == _Object and (my_ancestors + [self]).include? _Module)
195
204
  end
196
205
  end
197
206
 
@@ -203,7 +212,17 @@ module Drx
203
212
  # Usually this means that the ICLASS has a singleton (see "Singletons
204
213
  # of included modules" in display_super?()). We want to see this
205
214
  # singleton.
206
- return Module != kls.the_object
215
+
216
+ # Unfortunately, here is a special treatment for the 'arguments' gem,
217
+ # which our GUI uses. That gem includes the 'Arguments' module in both
218
+ # Class and Module (this is redundant!) and having the singleton twice
219
+ # in our graph may break its nice rectangular structure. So we don't
220
+ # show its singleton.
221
+ if defined? ::Arguments
222
+ return false if ::Arguments == klass.the_object
223
+ end
224
+
225
+ return kls != _Module
207
226
  else
208
227
  # Displaying a singleton's klass is confusing and usually unneeded.
209
228
  return !singleton?
@@ -211,16 +230,19 @@ module Drx
211
230
  end
212
231
 
213
232
  # Whether to display the super.
214
- def display_super?(spr)
215
- if (singleton? or t_iclass?) and Module == spr.the_object
216
- # Singletons of included modules, and modules included in them,
217
- # have their chain eventually reach Module. To prevent clutter,
218
- # we don't show this final link.
219
- #
220
- # "Singletons of included modules" often exist only for their
221
- # #included method. For example, DataMapper#Resource have
222
- # such a singleton.
223
- return false
233
+ def display_super?(spr, my_ancestors)
234
+ if spr == _Module
235
+ # Many objects have Module as their super (e.g., singletones
236
+ # of included modules, or modules included in them). To prevent
237
+ # clutter we print the arrow to Module only if it comes from
238
+ # Class (or a module included in it).
239
+ return (my_ancestors + [self]).include? _Class
240
+ #
241
+ # A somewhat irrelevant note (I don't have a better place to put it):
242
+ #
243
+ # "Singletons of included modules" often exist solely for their
244
+ # #included method. For example, DataMapper#Resource has
245
+ # such a singleton.
224
246
  end
225
247
  return true
226
248
  end
@@ -237,8 +259,19 @@ module Drx
237
259
  end
238
260
  end
239
261
 
262
+ # Shortcuts for some specific objects. An "S" suffix
263
+ # denotes a singleton.
264
+ protected
265
+ def _Module; ObjInfo.new(Module); end
266
+ def _ModuleS; ObjInfo.new(Module).klass; end
267
+ def _Object; ObjInfo.new(Object); end
268
+ def _ObjectS; ObjInfo.new(Object).klass; end
269
+ def _Class; ObjInfo.new(Class); end
270
+ def _ClassS; ObjInfo.new(Class).klass; end
271
+ public
272
+
240
273
  # Generates a diagram of the inheritance hierarchy. It accepts a hash
241
- # pointing to pathnames to write the result to. A Tempfiles hash
274
+ # pointing to filepaths to write the result to. A Tempfiles hash
242
275
  # can be used instead.
243
276
  #
244
277
  # the_object = "some object"
@@ -250,7 +283,8 @@ module Drx
250
283
  def generate_diagram(files, opts = {}, &block)
251
284
  source = self.dot_source(opts, &block)
252
285
  File.open(files['dot'], 'w') { |f| f.write(source) }
253
- command = GRAPHVIZ_COMMAND % [files['dot'], files['gif'], files['map']]
286
+ # Replace {extension} tokens with their corresponding filepaths:
287
+ command = GRAPHVIZ_COMMAND.gsub(/\{([a-z]+)\}/) { files[$1] }
254
288
  message = Kernel.`(command) # `
255
289
  if $? != 0
256
290
  error = <<-EOS % [command, message]
data/lib/drx/objinfo.rb CHANGED
@@ -19,6 +19,10 @@ module Drx
19
19
  Core::get_address(@obj)
20
20
  end
21
21
 
22
+ def ==(other)
23
+ other.is_a?(ObjInfo) and address == other.address
24
+ end
25
+
22
26
  def the_object
23
27
  @obj
24
28
  end
@@ -70,8 +74,9 @@ module Drx
70
74
  Core::get_iv_tbl(@obj)
71
75
  end
72
76
 
73
- # @todo: this could be nicer. perhaps define an [] accessor.
74
- def __get_ivar(name)
77
+ # Returns the value of an instance variable. Actually, of any sort
78
+ # of variable that's recorded in the variable-table.
79
+ def get_ivar(name)
75
80
  if class_like? and name.to_s =~ /^[A-Z]/
76
81
  # If it's a constant, it may be 'autoloaded'. We
77
82
  # trigger the loading by calling const_get().
@@ -120,7 +125,7 @@ module Drx
120
125
  if t_iclass?
121
126
  'include ' + klass.repr
122
127
  elsif singleton?
123
- attached = __get_ivar('__attached__') || self
128
+ attached = get_ivar('__attached__') || self
124
129
  attached.inspect + " 'S"
125
130
  else
126
131
  @obj.inspect
data/lib/drx/tk/app.rb CHANGED
@@ -11,8 +11,23 @@ module Drx
11
11
 
12
12
  class Application
13
13
 
14
+ # Loads ~/.drxrc.
15
+ #
16
+ # @see Application#user_customizations
17
+ def self.load_rc
18
+ @rc_loaded ||= begin
19
+ rc = File.join(ENV['HOME'] || Dir.pwd, '.drxrc')
20
+ load rc if File.exist? rc
21
+ 1
22
+ end
23
+ end
24
+
25
+ # Returns the top-level frame in which to show ourselves.
14
26
  def toplevel
15
- @toplevel ||= TkRoot.new
27
+ @toplevel ||= begin
28
+ # We're showing ourselves inside a TkRoot, unless one already exists.
29
+ Application.first_window? ? TkRoot.new : TkToplevel.new
30
+ end
16
31
  end
17
32
 
18
33
  def initialize
@@ -56,12 +71,16 @@ module Drx
56
71
  show 'headings'
57
72
  }
58
73
  @methodsbox = Tk::Tile::Treeview.new(toplevel) {
59
- columns 'name location'
74
+ columns 'name arguments location'
60
75
  heading_configure('name', :text => 'Name')
76
+ heading_configure('arguments', :text => 'Arguments')
61
77
  heading_configure('location', :text => 'Location')
62
78
  column_configure('name', :stretch => false )
79
+ column_configure('arguments', :stretch => false )
63
80
  column_configure('location', :stretch => false )
64
81
  show 'headings'
82
+ # We want the layout manager to allocate space for two columns only:
83
+ displaycolumns 'name location'
65
84
  }
66
85
 
67
86
  @graph_size_menu = Tk::Tile::Combobox.new(toplevel) {
@@ -83,6 +102,15 @@ module Drx
83
102
  save_graph tip
84
103
  }
85
104
 
105
+ @show_arguments_chk = TkCheckbutton.new(toplevel) {
106
+ text 'Show arguments'
107
+ variable TkVariable.new(0)
108
+ }
109
+ @use_arguments_gem_chk = TkCheckbutton.new(toplevel) {
110
+ text "Use the 'arguments' gem (slower)"
111
+ variable TkVariable.new(0)
112
+ }
113
+
86
114
  layout
87
115
 
88
116
  @varsbox.bind('<TreeviewSelect>') {
@@ -141,30 +169,45 @@ module Drx
141
169
  @graph_opts[:style] = @graph_style_menu.get
142
170
  refresh
143
171
  }
172
+ @show_arguments_chk.variable.trace('w') do |value,|
173
+ if value == 1
174
+ @use_arguments_gem_chk.raise
175
+ @methodsbox.displaycolumns 'name arguments location'
176
+ display_methods(current_object)
177
+ else
178
+ @use_arguments_gem_chk.lower
179
+ @methodsbox.displaycolumns 'name location'
180
+ end
181
+ end
182
+ @use_arguments_gem_chk.variable.trace('w') do |value,|
183
+ ObjInfo.use_arguments_gem = (value == 1)
184
+ display_methods(current_object)
185
+ end
186
+ @show_arguments_chk.variable.value = @show_arguments_chk.variable.value # Trigger the trace handler.
144
187
 
145
188
  output "Please visit the homepage, http://drx.rubyforge.org/, for usage instructions.\n", 'info'
146
189
 
147
- one_time_initialization
190
+ Application.load_rc
191
+ system_customizations
148
192
  user_customizations
149
193
  end
150
194
 
151
- def one_time_initialization
152
- @@one_time_initialization ||= begin
195
+ # Users may redefine this method in their ~/.drxrc
196
+ # to fine-tune the app.
197
+ def user_customizations
198
+ end
199
+
200
+ # The following are default customizations. They are subjective in
201
+ # nature and users may knock them out in their ~/.drxrc.
202
+ def system_customizations
203
+ if Application.first_window?
153
204
  # Try to make the Unixy GUI less ugly.
154
205
  if Tk.windowingsystem == 'x11' and Ttk.themes.include? 'clam'
155
206
  Ttk.set_theme 'clam'
156
207
  end
157
- rc = File.join(ENV['HOME'] || Dir.pwd, '.drxrc')
158
- load rc if File.exist? rc
159
- 1
160
208
  end
161
209
  end
162
210
 
163
- # Users may redefine this method in their ~/.drxrc
164
- # to fine-tune the app.
165
- def user_customizations
166
- end
167
-
168
211
  def vbox(*args); VBox.new(toplevel, args); end
169
212
  def hbox(*args); HBox.new(toplevel, args); end
170
213
  def separator; TkLabel.new(toplevel, :text => ' '); end
@@ -204,7 +247,8 @@ module Drx
204
247
  ), :weight => 50
205
248
  panes.add vbox(
206
249
  TkLabel.new(toplevel, :text => 'Methods (m_tbl):', :anchor => 'w'),
207
- [Scrolled.new(toplevel, @methodsbox), { :expand => true, :fill => 'both' } ]
250
+ [Scrolled.new(toplevel, @methodsbox), { :expand => true, :fill => 'both' } ],
251
+ hbox(@show_arguments_chk, separator, @use_arguments_gem_chk)
208
252
  ), :weight => 10
209
253
 
210
254
  main_frame.add(panes)
@@ -221,11 +265,10 @@ module Drx
221
265
  def open_up_editor(filename, lineno)
222
266
  command = sprintf(ENV['DRX_EDITOR_COMMAND'] || EDITOR_COMMAND, lineno, filename)
223
267
  output "Executing: #{command}...\n", 'info'
224
- if !fork
268
+ Thread.new do
225
269
  if !Kernel.system(command)
226
270
  output "Could not execute the command '#{command}'\n", 'error'
227
271
  end
228
- exit!
229
272
  end
230
273
  end
231
274
 
@@ -250,7 +293,7 @@ module Drx
250
293
  end
251
294
 
252
295
  def selected_var
253
- ObjInfo.new(current_object).__get_ivar(@varsbox.get_selection)
296
+ ObjInfo.new(current_object).get_ivar(@varsbox.get_selection)
254
297
  end
255
298
 
256
299
  def eval_code(code)
@@ -289,14 +332,20 @@ module Drx
289
332
  # Get rid of gazillions of Tk classes:
290
333
  vars = vars.reject { |v| v =~ /Tk|Ttk/ }
291
334
  vars.each do |name|
292
- value = if allowed_names.any? { |p| p === name }
293
- info.__get_ivar(name).inspect
294
- else
295
- # We don't want to inspect ruby's internal variables (because
296
- # they may not be Ruby values at all).
297
- ''
298
- end
299
- @varsbox.insert('', 'end', :text => name, :values => [ name, value ] )
335
+ begin
336
+ value = if allowed_names.any? { |p| p === name }
337
+ info.get_ivar(name).inspect
338
+ else
339
+ # We don't want to inspect ruby's internal variables (because
340
+ # they may not be Ruby values at all).
341
+ ''
342
+ end
343
+ @varsbox.insert('', 'end', :text => name, :values => [ name, value ] )
344
+ rescue NameError
345
+ # Referencing an autoloaded constant (in ObjInfo#get_ivar()) may
346
+ # raise a NameError. This happens when the source file autoloaded
347
+ # defines the moudle/class in the top-level. Example is Camping::Mab.
348
+ end
300
349
  end
301
350
  end
302
351
  end
@@ -321,6 +370,27 @@ module Drx
321
370
  end
322
371
  end
323
372
 
373
+ # Returns a string describing a method's arguments, for use in GUIs.
374
+ def pretty_arguments(info, name)
375
+ args = info.method_arguments(name)
376
+ return args.map do |arg|
377
+ case arg[0]
378
+ when :req; (arg[1] || 'arg').to_s
379
+ when :opt; (arg[1] || 'arg').to_s + '=' + (arg[2] || '?')
380
+ when :rest; '*' + (arg[1] || 'args').to_s
381
+ when :block; '&' + (arg[1] || 'arg').to_s
382
+ end
383
+ end.join ', '
384
+ rescue NameError
385
+ return '---'
386
+ rescue SyntaxError => e
387
+ 'SYNTAX ERROR: ' + e.to_s
388
+ end
389
+
390
+ def show_arguments?
391
+ @show_arguments_chk.variable == 1
392
+ end
393
+
324
394
  # Fills the methods listbox with a list of the object's methods.
325
395
  def display_methods(obj)
326
396
  @methodsbox.clear
@@ -328,7 +398,11 @@ module Drx
328
398
  if obj and info.class_like?
329
399
  methods = info.m_tbl.keys.map do |v| v.to_s end.sort
330
400
  methods.each do |name|
331
- @methodsbox.insert('', 'end', :text => name, :values => [ name, pretty_location(info, name) ] )
401
+ @methodsbox.insert('', 'end', :text => name, :values => [
402
+ name,
403
+ show_arguments? ? pretty_arguments(info, name) : '-',
404
+ pretty_location(info, name)
405
+ ])
332
406
  end
333
407
  end
334
408
  end
@@ -357,11 +431,19 @@ module Drx
357
431
  end
358
432
  end
359
433
 
434
+ # Updates the window title (usually shown in the taskbar).
435
+ def update_title(obj)
436
+ toplevel.title = 'Drx: ' + begin
437
+ obj.is_a?(Module) ? obj.name : obj.class.name
438
+ end.to_s # In case of singletons, #name returns nil, so to_s enforces a string.
439
+ end
440
+
360
441
  # Makes `obj` the primary object seen (the one which is the tip of the diagram).
361
442
  def navigate_to(obj)
362
443
  @current_object = obj
363
444
  @navigation_history << obj
364
445
  display_graph(obj)
446
+ update_title(obj)
365
447
  # Trigger the update of the variables and methods tables by selecting this object
366
448
  # in the imagemap.
367
449
  @im.active_url = @im.urls.first
@@ -391,9 +473,17 @@ module Drx
391
473
  navigate_to(current_object)
392
474
  end
393
475
 
476
+ class << self
477
+ attr_accessor :in_loop
478
+ def first_window?; !in_loop; end
479
+ end
480
+
394
481
  def run
395
- # @todo Skip this if Tk is already running.
482
+ return if Application.in_loop
483
+ # @todo Any other way to detect that Tk's mainloop is already running?
484
+ Application.in_loop = true
396
485
  Tk.mainloop
486
+ Application.in_loop = false
397
487
  Tk.restart # So that Tk doesn't complain 'can't invoke "frame" command: application has been destroyed' next time.
398
488
  end
399
489
  end
data/lib/drx.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'drx_core' # The C extension.
4
4
  require 'drx/objinfo'
5
+ require 'drx/arguments'
5
6
  require 'drx/graphviz'
6
7
 
7
8
  module Drx
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: drx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mooffie
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-04-02 00:00:00 +03:00
12
+ date: 2010-04-16 00:00:00 +03:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -27,6 +27,7 @@ files:
27
27
  - lib/drx/tk/imagemap.rb
28
28
  - lib/drx/tk/app.rb
29
29
  - lib/drx/objinfo.rb
30
+ - lib/drx/arguments.rb
30
31
  - lib/drx/tempfiles.rb
31
32
  - lib/drx.rb
32
33
  - ext/extconf.rb
@@ -34,7 +35,8 @@ files:
34
35
  - examples/drx_datamapper.rb
35
36
  - examples/drx_date.rb
36
37
  - examples/drx_guitar.rb
37
- - examples/drx_datamapper19.rb
38
+ - examples/drx_activerecord.rb
39
+ - examples/drx_sequel.rb
38
40
  has_rdoc: true
39
41
  homepage: http://drx.rubyforge.org/
40
42
  licenses: []
@@ -1,35 +0,0 @@
1
- require 'rubygems'
2
- require 'dm-core'
3
- $: << File.join('/home/mooffie/_drx.git/lib')
4
- require 'drx'
5
-
6
-
7
-
8
- #
9
- # This is part of a blogging website. Users write posts. A post
10
- # belongs to a user.
11
- #
12
-
13
- class Post
14
- include DataMapper::Resource
15
-
16
- property :post_id, Serial
17
- property :title, String
18
- property :body, Text
19
-
20
- belongs_to :user
21
- end
22
-
23
- class User
24
- include DataMapper::Resource
25
-
26
- property :user_uid, Serial
27
- property :name, String
28
- property :mail, String
29
- end
30
-
31
- post = Post.new
32
-
33
- p post
34
- require 'drx'
35
- post.see