rhodes-framework 1.0.10 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (198) hide show
  1. data/Manifest.txt +159 -30
  2. data/Rakefile +3 -19
  3. data/lib/erb.rb +1 -1
  4. data/lib/rho/rho.rb +94 -107
  5. data/lib/rho/rhofsconnector.rb +4 -0
  6. data/lib/rho/rhoutils.rb +26 -0
  7. data/lib/rho/rhoviewhelpers.rb +1 -1
  8. data/lib/rhodes.rb +2 -2
  9. data/lib/rhoframework.rb +9 -9
  10. data/lib/rhom/rhom.rb +1 -1
  11. data/lib/rhom/rhom_db_adapter.rb +36 -15
  12. data/lib/rhom/rhom_db_adapterME.rb +2 -3
  13. data/lib/rhom/rhom_object.rb +14 -2
  14. data/lib/rhom/rhom_object_factory.rb +42 -11
  15. data/lib/version.rb +2 -2
  16. data/spec/README +1 -0
  17. data/spec/Rakefile +1 -0
  18. data/spec/{configs/account.rb → app/Account/config.rb} +0 -0
  19. data/spec/{configs/case.rb → app/Case/config.rb} +0 -0
  20. data/spec/app/Question/config.rb +3 -0
  21. data/spec/app/Settings/controller.rb +10 -0
  22. data/spec/app/Settings/index.erb +11 -0
  23. data/spec/app/SpecRunner/controller.rb +15 -0
  24. data/spec/app/SpecRunner/index.erb +14 -0
  25. data/spec/app/application.rb +4 -0
  26. data/spec/app/index.erb +17 -0
  27. data/spec/app/layout.erb +27 -0
  28. data/spec/app/loading.html +11 -0
  29. data/spec/app/mspec.rb +11 -0
  30. data/spec/app/mspec/expectations.rb +2 -0
  31. data/spec/app/mspec/expectations/expectations.rb +17 -0
  32. data/spec/app/mspec/expectations/should.rb +25 -0
  33. data/spec/app/mspec/fileutils.rb +1590 -0
  34. data/spec/app/mspec/guards.rb +16 -0
  35. data/spec/app/mspec/guards/background.rb +21 -0
  36. data/spec/app/mspec/guards/bug.rb +24 -0
  37. data/spec/app/mspec/guards/compliance.rb +37 -0
  38. data/spec/app/mspec/guards/conflict.rb +18 -0
  39. data/spec/app/mspec/guards/endian.rb +44 -0
  40. data/spec/app/mspec/guards/extensions.rb +20 -0
  41. data/spec/app/mspec/guards/guard.rb +166 -0
  42. data/spec/app/mspec/guards/noncompliance.rb +20 -0
  43. data/spec/app/mspec/guards/platform.rb +43 -0
  44. data/spec/app/mspec/guards/quarantine.rb +17 -0
  45. data/spec/app/mspec/guards/runner.rb +34 -0
  46. data/spec/app/mspec/guards/superuser.rb +17 -0
  47. data/spec/app/mspec/guards/support.rb +20 -0
  48. data/spec/app/mspec/guards/tty.rb +20 -0
  49. data/spec/app/mspec/guards/version.rb +38 -0
  50. data/spec/app/mspec/helpers.rb +11 -0
  51. data/spec/app/mspec/helpers/argv.rb +43 -0
  52. data/spec/app/mspec/helpers/bignum.rb +5 -0
  53. data/spec/app/mspec/helpers/const_lookup.rb +9 -0
  54. data/spec/app/mspec/helpers/environment.rb +23 -0
  55. data/spec/app/mspec/helpers/fixture.rb +20 -0
  56. data/spec/app/mspec/helpers/flunk.rb +5 -0
  57. data/spec/app/mspec/helpers/io.rb +17 -0
  58. data/spec/app/mspec/helpers/language_version.rb +20 -0
  59. data/spec/app/mspec/helpers/ruby_exe.rb +123 -0
  60. data/spec/app/mspec/helpers/scratch.rb +17 -0
  61. data/spec/app/mspec/helpers/tmp.rb +32 -0
  62. data/spec/app/mspec/matchers.rb +23 -0
  63. data/spec/app/mspec/matchers/base.rb +95 -0
  64. data/spec/app/mspec/matchers/be_an_instance_of.rb +26 -0
  65. data/spec/app/mspec/matchers/be_ancestor_of.rb +24 -0
  66. data/spec/app/mspec/matchers/be_close.rb +27 -0
  67. data/spec/app/mspec/matchers/be_empty.rb +20 -0
  68. data/spec/app/mspec/matchers/be_false.rb +20 -0
  69. data/spec/app/mspec/matchers/be_kind_of.rb +24 -0
  70. data/spec/app/mspec/matchers/be_nil.rb +20 -0
  71. data/spec/app/mspec/matchers/be_true.rb +20 -0
  72. data/spec/app/mspec/matchers/complain.rb +56 -0
  73. data/spec/app/mspec/matchers/eql.rb +26 -0
  74. data/spec/app/mspec/matchers/equal.rb +26 -0
  75. data/spec/app/mspec/matchers/equal_element.rb +78 -0
  76. data/spec/app/mspec/matchers/equal_utf16.rb +34 -0
  77. data/spec/app/mspec/matchers/have_constant.rb +30 -0
  78. data/spec/app/mspec/matchers/have_instance_method.rb +24 -0
  79. data/spec/app/mspec/matchers/have_method.rb +24 -0
  80. data/spec/app/mspec/matchers/have_private_instance_method.rb +24 -0
  81. data/spec/app/mspec/matchers/include.rb +32 -0
  82. data/spec/app/mspec/matchers/match_yaml.rb +47 -0
  83. data/spec/app/mspec/matchers/method.rb +14 -0
  84. data/spec/app/mspec/matchers/output.rb +67 -0
  85. data/spec/app/mspec/matchers/output_to_fd.rb +71 -0
  86. data/spec/app/mspec/matchers/raise_error.rb +48 -0
  87. data/spec/app/mspec/matchers/respond_to.rb +24 -0
  88. data/spec/app/mspec/matchers/stringsymboladapter.rb +8 -0
  89. data/spec/app/mspec/mocks.rb +3 -0
  90. data/spec/app/mspec/mocks/mock.rb +159 -0
  91. data/spec/app/mspec/mocks/object.rb +20 -0
  92. data/spec/app/mspec/mocks/proxy.rb +136 -0
  93. data/spec/app/mspec/pp.rb +893 -0
  94. data/spec/app/mspec/runner.rb +15 -0
  95. data/spec/app/mspec/runner/actions.rb +8 -0
  96. data/spec/app/mspec/runner/actions/debug.rb +17 -0
  97. data/spec/app/mspec/runner/actions/filter.rb +40 -0
  98. data/spec/app/mspec/runner/actions/gdb.rb +17 -0
  99. data/spec/app/mspec/runner/actions/tag.rb +133 -0
  100. data/spec/app/mspec/runner/actions/taglist.rb +56 -0
  101. data/spec/app/mspec/runner/actions/tagpurge.rb +56 -0
  102. data/spec/app/mspec/runner/actions/tally.rb +116 -0
  103. data/spec/app/mspec/runner/actions/timer.rb +22 -0
  104. data/spec/app/mspec/runner/context.rb +188 -0
  105. data/spec/app/mspec/runner/example.rb +34 -0
  106. data/spec/app/mspec/runner/exception.rb +43 -0
  107. data/spec/app/mspec/runner/filters.rb +4 -0
  108. data/spec/app/mspec/runner/filters/match.rb +22 -0
  109. data/spec/app/mspec/runner/filters/profile.rb +54 -0
  110. data/spec/app/mspec/runner/filters/regexp.rb +7 -0
  111. data/spec/app/mspec/runner/filters/tag.rb +29 -0
  112. data/spec/app/mspec/runner/formatters.rb +10 -0
  113. data/spec/app/mspec/runner/formatters/describe.rb +24 -0
  114. data/spec/app/mspec/runner/formatters/dotted.rb +98 -0
  115. data/spec/app/mspec/runner/formatters/file.rb +19 -0
  116. data/spec/app/mspec/runner/formatters/html.rb +81 -0
  117. data/spec/app/mspec/runner/formatters/method.rb +93 -0
  118. data/spec/app/mspec/runner/formatters/specdoc.rb +41 -0
  119. data/spec/app/mspec/runner/formatters/spinner.rb +99 -0
  120. data/spec/app/mspec/runner/formatters/summary.rb +11 -0
  121. data/spec/app/mspec/runner/formatters/unit.rb +21 -0
  122. data/spec/app/mspec/runner/formatters/yaml.rb +44 -0
  123. data/spec/app/mspec/runner/mspec.rb +361 -0
  124. data/spec/app/mspec/runner/object.rb +24 -0
  125. data/spec/app/mspec/runner/shared.rb +12 -0
  126. data/spec/app/mspec/runner/tag.rb +32 -0
  127. data/spec/app/mspec/utils/name_map.rb +129 -0
  128. data/spec/app/mspec/utils/options.rb +441 -0
  129. data/spec/app/mspec/utils/ruby_name.rb +8 -0
  130. data/spec/app/mspec/utils/script.rb +220 -0
  131. data/spec/app/mspec/utils/version.rb +53 -0
  132. data/spec/app/mspec/version.rb +5 -0
  133. data/spec/app/spec/fixtures/client_info.txt +2 -0
  134. data/spec/app/spec/fixtures/object_values.txt +90 -0
  135. data/spec/{rho_controller_spec.rb → app/spec/rho_controller_spec.rb} +4 -7
  136. data/spec/{rho_spec.rb → app/spec/rho_spec.rb} +15 -36
  137. data/spec/{rhom_object_factory_spec.rb → app/spec/rhom_object_factory_spec.rb} +108 -72
  138. data/spec/{rhom_spec.rb → app/spec/rhom_spec.rb} +8 -4
  139. data/spec/app/spec/spec_helper.rb +15 -0
  140. data/spec/app/spec/webview_spec.rb +27 -0
  141. data/spec/app/spec_runner.rb +26 -0
  142. data/spec/build.yml +28 -0
  143. data/spec/public/css/base.css +39 -0
  144. data/spec/public/css/blackberry.css +99 -0
  145. data/spec/public/css/iphone.css +392 -0
  146. data/spec/public/css/rho.css +3 -0
  147. data/spec/public/css/xhtml.css +114 -0
  148. data/spec/public/images/IUI_LICENSE.txt +21 -0
  149. data/spec/public/images/backButton.png +0 -0
  150. data/spec/public/images/blueButton.png +0 -0
  151. data/spec/public/images/cancel.png +0 -0
  152. data/spec/public/images/grayButton.png +0 -0
  153. data/spec/public/images/iui-logo-touch-icon.png +0 -0
  154. data/spec/public/images/listArrow.png +0 -0
  155. data/spec/public/images/listArrowSel.png +0 -0
  156. data/spec/public/images/listGroup.png +0 -0
  157. data/spec/public/images/loading.gif +0 -0
  158. data/spec/public/images/pinstripes.png +0 -0
  159. data/spec/public/images/right_button.png +0 -0
  160. data/spec/public/images/selection.png +0 -0
  161. data/spec/public/images/thumb.png +0 -0
  162. data/spec/public/images/toggle.png +0 -0
  163. data/spec/public/images/toggleOn.png +0 -0
  164. data/spec/public/images/toolButton.png +0 -0
  165. data/spec/public/images/toolButton_new.png +0 -0
  166. data/spec/public/images/toolbar.png +0 -0
  167. data/spec/public/images/whiteButton.png +0 -0
  168. data/spec/public/js/application.js +1 -0
  169. data/spec/public/js/jquery-1.2.6.min.js +32 -0
  170. data/spec/public/js/rho.js +4 -0
  171. data/spec/public/js/rhogeolocation-wm.js +59 -0
  172. data/spec/public/js/rhogeolocation.js +11 -0
  173. data/spec/rhoconfig.txt +19 -0
  174. metadata +169 -39
  175. data/History.txt +0 -37
  176. data/README.rdoc +0 -2
  177. data/lib/TestServe.rb +0 -9
  178. data/res/sqlite3/constants.rb +0 -49
  179. data/res/sqlite3/database.rb +0 -715
  180. data/res/sqlite3/driver/dl/api.rb +0 -154
  181. data/res/sqlite3/driver/dl/driver.rb +0 -307
  182. data/res/sqlite3/driver/native/driver.rb +0 -257
  183. data/res/sqlite3/errors.rb +0 -68
  184. data/res/sqlite3/pragmas.rb +0 -271
  185. data/res/sqlite3/resultset.rb +0 -176
  186. data/res/sqlite3/sqlite3_api.rb +0 -0
  187. data/res/sqlite3/statement.rb +0 -230
  188. data/res/sqlite3/translator.rb +0 -109
  189. data/res/sqlite3/value.rb +0 -57
  190. data/res/sqlite3/version.rb +0 -14
  191. data/rhodes.gemspec +0 -18
  192. data/spec/app_manifest.txt +0 -4
  193. data/spec/configs/contact.rb +0 -3
  194. data/spec/configs/employee.rb +0 -3
  195. data/spec/spec.opts +0 -1
  196. data/spec/spec_helper.rb +0 -49
  197. data/spec/stubs.rb +0 -39
  198. data/spec/syncdbtest.sqlite +0 -0
@@ -0,0 +1,8 @@
1
+ require 'mspec/utils/version'
2
+
3
+ module StringSymbolAdapter
4
+ def convert_name(name)
5
+ version = SpecVersion.new(RUBY_VERSION) <=> "1.9"
6
+ version < 0 ? name.to_s : name
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ require 'mspec/mocks/mock'
2
+ require 'mspec/mocks/proxy'
3
+ require 'mspec/mocks/object'
@@ -0,0 +1,159 @@
1
+ require 'mspec/expectations/expectations'
2
+
3
+ module Mock
4
+ def self.reset
5
+ @mocks = @stubs = nil
6
+ end
7
+
8
+ def self.mocks
9
+ @mocks ||= Hash.new { |h,k| h[k] = [] }
10
+ end
11
+
12
+ def self.stubs
13
+ @stubs ||= Hash.new { |h,k| h[k] = [] }
14
+ end
15
+
16
+ def self.replaced_name(obj, sym)
17
+ :"__ms_#{obj.__id__}_#{sym}__"
18
+ end
19
+
20
+ def self.replaced_key(obj, sym)
21
+ [replaced_name(obj, sym), obj, sym]
22
+ end
23
+
24
+ def self.replaced?(key)
25
+ !!(mocks.keys + stubs.keys).find { |k| k.first == key.first }
26
+ end
27
+
28
+ def self.install_method(obj, sym, type=nil)
29
+ meta = class << obj; self; end
30
+
31
+ key = replaced_key obj, sym
32
+ if (sym.to_sym == :respond_to? or obj.respond_to?(sym)) and !replaced?(key)
33
+ meta.__send__ :alias_method, key.first, sym.to_sym
34
+ end
35
+
36
+ # LB: We don't support String eval, using define_method approach instead
37
+ # meta.class_eval <<-END
38
+ # def #{sym}(*args, &block)
39
+ # Mock.verify_call self, :#{sym}, *args, &block
40
+ # end
41
+ # END
42
+ meta.class_eval do
43
+ define_method(sym.to_sym) { |*args, &block| Mock.verify_call self, sym.to_sym, *args, &block }
44
+ end
45
+
46
+ proxy = MockProxy.new type
47
+
48
+ if proxy.mock?
49
+ MSpec.expectation
50
+ MSpec.actions :expectation, MSpec.current.state
51
+ end
52
+
53
+ if proxy.stub?
54
+ stubs[key].unshift proxy
55
+ else
56
+ mocks[key] << proxy
57
+ end
58
+
59
+ proxy
60
+ end
61
+
62
+ def self.name_or_inspect(obj)
63
+ obj.instance_variable_get(:@name) || obj.inspect
64
+ end
65
+
66
+ def self.verify_count
67
+ mocks.each do |key, proxies|
68
+ replaced, obj, sym = *key
69
+ proxies.each do |proxy|
70
+ qualifier, count = proxy.count
71
+ pass = case qualifier
72
+ when :at_least
73
+ proxy.calls >= count
74
+ when :at_most
75
+ proxy.calls <= count
76
+ when :exactly
77
+ proxy.calls == count
78
+ when :any_number_of_times
79
+ true
80
+ else
81
+ false
82
+ end
83
+ unless pass
84
+ Expectation.fail_with(
85
+ "Mock '#{name_or_inspect obj}' expected to receive '#{sym}' " \
86
+ "#{qualifier.to_s.sub('_', ' ')} #{count} times",
87
+ "but received it #{proxy.calls} times")
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ def self.verify_call(obj, sym, *args, &block)
94
+ compare = *args
95
+ if RUBY_VERSION >= '1.9'
96
+ compare = compare.first if compare.length <= 1
97
+ end
98
+
99
+ key = replaced_key obj, sym
100
+ proxies = mocks[key] + stubs[key]
101
+ proxies.each do |proxy|
102
+ pass = case proxy.arguments
103
+ when :any_args
104
+ true
105
+ when :no_args
106
+ compare.nil?
107
+ else
108
+ proxy.arguments == compare
109
+ end
110
+
111
+ if proxy.yielding?
112
+ if block
113
+ proxy.yielding.each do |args_to_yield|
114
+ if block.arity == -1 || block.arity == args_to_yield.size
115
+ block.call(*args_to_yield)
116
+ else
117
+ Expectation.fail_with(
118
+ "Mock '#{name_or_inspect obj}' asked to yield " \
119
+ "|#{proxy.yielding.join(', ')}| on #{sym}\n",
120
+ "but a block with arity #{block.arity} was passed")
121
+ end
122
+ end
123
+ else
124
+ Expectation.fail_with(
125
+ "Mock '#{name_or_inspect obj}' asked to yield " \
126
+ "|[#{proxy.yielding.join('], [')}]| on #{sym}\n",
127
+ "but no block was passed")
128
+ end
129
+ end
130
+
131
+ if pass
132
+ proxy.called
133
+ return proxy.returning
134
+ end
135
+ end
136
+
137
+ if sym.to_sym == :respond_to?
138
+ return obj.__send__(replaced_name(obj, sym), compare)
139
+ else
140
+ Expectation.fail_with("Mock '#{name_or_inspect obj}': method #{sym}\n",
141
+ "called with unexpected arguments (#{Array(compare).join(' ')})")
142
+ end
143
+ end
144
+
145
+ def self.cleanup
146
+ symbols = mocks.keys + stubs.keys
147
+ symbols.uniq.each do |replaced, obj, sym|
148
+ meta = class << obj; self; end
149
+
150
+ if meta.instance_methods.include?(replaced.to_s)
151
+ meta.__send__ :alias_method, sym.to_sym, replaced
152
+ meta.__send__ :remove_method, replaced
153
+ else
154
+ meta.__send__ :remove_method, sym.to_sym
155
+ end
156
+ end
157
+ reset
158
+ end
159
+ end
@@ -0,0 +1,20 @@
1
+ require 'mspec/mocks/proxy'
2
+
3
+ class Object
4
+ def stub!(sym)
5
+ Mock.install_method self, sym, :stub
6
+ end
7
+
8
+ def should_receive(sym)
9
+ Mock.install_method self, sym
10
+ end
11
+
12
+ def should_not_receive(sym)
13
+ proxy = Mock.install_method self, sym
14
+ proxy.exactly(0).times
15
+ end
16
+
17
+ def mock(name, options={})
18
+ MockObject.new name, options
19
+ end
20
+ end
@@ -0,0 +1,136 @@
1
+ class MockObject
2
+ def initialize(name, options={})
3
+ @name = name
4
+ @null = options[:null_object]
5
+ end
6
+
7
+ def method_missing(sym, *args, &block)
8
+ @null ? self : super
9
+ end
10
+ end
11
+
12
+ class MockProxy
13
+ def initialize(type=nil)
14
+ @multiple_returns = nil
15
+ @returning = nil
16
+ @yielding = []
17
+ @arguments = :any_args
18
+ @type = type || :mock
19
+ end
20
+
21
+ def mock?
22
+ @type == :mock
23
+ end
24
+
25
+ def stub?
26
+ @type == :stub
27
+ end
28
+
29
+ def count
30
+ @count ||= mock? ? [:exactly, 1] : [:any_number_of_times, 0]
31
+ end
32
+
33
+ def arguments
34
+ @arguments
35
+ end
36
+
37
+ def returning
38
+ if @multiple_returns
39
+ if @returning.size == 1
40
+ @multiple_returns = false
41
+ return @returning = @returning.shift
42
+ end
43
+ return @returning.shift
44
+ end
45
+ @returning
46
+ end
47
+
48
+ def times
49
+ self
50
+ end
51
+
52
+ def calls
53
+ @calls ||= 0
54
+ end
55
+
56
+ def called
57
+ @calls = calls + 1
58
+ end
59
+
60
+ def exactly(n)
61
+ @count = [:exactly, n_times(n)]
62
+ self
63
+ end
64
+
65
+ def at_least(n)
66
+ @count = [:at_least, n_times(n)]
67
+ self
68
+ end
69
+
70
+ def at_most(n)
71
+ @count = [:at_most, n_times(n)]
72
+ self
73
+ end
74
+
75
+ def once
76
+ exactly 1
77
+ end
78
+
79
+ def twice
80
+ exactly 2
81
+ end
82
+
83
+ def any_number_of_times
84
+ @count = [:any_number_of_times, 0]
85
+ self
86
+ end
87
+
88
+ def with(*args)
89
+ raise ArgumentError, "you must specify the expected arguments" if args.empty?
90
+ @arguments = *args
91
+ if RUBY_VERSION >= '1.9'
92
+ @arguments = @arguments.first if @arguments.length <= 1
93
+ end
94
+ self
95
+ end
96
+
97
+ def and_return(*args)
98
+ case args.size
99
+ when 0
100
+ @returning = nil
101
+ when 1
102
+ @returning = args[0]
103
+ else
104
+ @multiple_returns = true
105
+ @returning = args
106
+ count[1] = args.size if count[1] < args.size
107
+ end
108
+ self
109
+ end
110
+
111
+ def and_yield(*args)
112
+ @yielding << args
113
+ self
114
+ end
115
+
116
+ def yielding
117
+ @yielding
118
+ end
119
+
120
+ def yielding?
121
+ !@yielding.empty?
122
+ end
123
+
124
+ private
125
+
126
+ def n_times(n)
127
+ case n
128
+ when :once
129
+ 1
130
+ when :twice
131
+ 2
132
+ else
133
+ Integer n
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,893 @@
1
+ # == Pretty-printer for Ruby objects.
2
+ #
3
+ # = Which seems better?
4
+ #
5
+ # non-pretty-printed output by #p is:
6
+ # #<PP:0x81fedf0 @genspace=#<Proc:0x81feda0>, @group_queue=#<PrettyPrint::GroupQueue:0x81fed3c @queue=[[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], []]>, @buffer=[], @newline="\n", @group_stack=[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#<IO:0x8114ee4>>
7
+ #
8
+ # pretty-printed output by #pp is:
9
+ # #<PP:0x81fedf0
10
+ # @buffer=[],
11
+ # @buffer_width=0,
12
+ # @genspace=#<Proc:0x81feda0>,
13
+ # @group_queue=
14
+ # #<PrettyPrint::GroupQueue:0x81fed3c
15
+ # @queue=
16
+ # [[#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
17
+ # []]>,
18
+ # @group_stack=
19
+ # [#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
20
+ # @indent=0,
21
+ # @maxwidth=79,
22
+ # @newline="\n",
23
+ # @output=#<IO:0x8114ee4>,
24
+ # @output_width=2>
25
+ #
26
+ # I like the latter. If you do too, this library is for you.
27
+ #
28
+ # = Usage
29
+ #
30
+ # pp(obj)
31
+ #
32
+ # output +obj+ to +$>+ in pretty printed format.
33
+ #
34
+ # It returns +nil+.
35
+ #
36
+ # = Output Customization
37
+ # To define your customized pretty printing function for your classes,
38
+ # redefine a method #pretty_print(+pp+) in the class.
39
+ # It takes an argument +pp+ which is an instance of the class PP.
40
+ # The method should use PP#text, PP#breakable, PP#nest, PP#group and
41
+ # PP#pp to print the object.
42
+ #
43
+ # = Author
44
+ # Tanaka Akira <akr@m17n.org>
45
+
46
+ # This class implements a pretty printing algorithm. It finds line breaks and
47
+ # nice indentations for grouped structure.
48
+ #
49
+ # By default, the class assumes that primitive elements are strings and each
50
+ # byte in the strings have single column in width. But it can be used for
51
+ # other situations by giving suitable arguments for some methods:
52
+ # * newline object and space generation block for PrettyPrint.new
53
+ # * optional width argument for PrettyPrint#text
54
+ # * PrettyPrint#breakable
55
+ #
56
+ # There are several candidate uses:
57
+ # * text formatting using proportional fonts
58
+ # * multibyte characters which has columns different to number of bytes
59
+ # * non-string formatting
60
+ #
61
+ # == Bugs
62
+ # * Box based formatting?
63
+ # * Other (better) model/algorithm?
64
+ #
65
+ # == References
66
+ # Christian Lindig, Strictly Pretty, March 2000,
67
+ # http://www.st.cs.uni-sb.de/~lindig/papers/#pretty
68
+ #
69
+ # Philip Wadler, A prettier printer, March 1998,
70
+ # http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier
71
+ #
72
+ # == Author
73
+ # Tanaka Akira <akr@m17n.org>
74
+ #
75
+ class PrettyPrint
76
+
77
+ # This is a convenience method which is same as follows:
78
+ #
79
+ # begin
80
+ # q = PrettyPrint.new(output, maxwidth, newline, &genspace)
81
+ # ...
82
+ # q.flush
83
+ # output
84
+ # end
85
+ #
86
+ def PrettyPrint.format(output='', maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n})
87
+ q = PrettyPrint.new(output, maxwidth, newline, &genspace)
88
+ yield q
89
+ q.flush
90
+ output
91
+ end
92
+
93
+ # This is similar to PrettyPrint::format but the result has no breaks.
94
+ #
95
+ # +maxwidth+, +newline+ and +genspace+ are ignored.
96
+ #
97
+ # The invocation of +breakable+ in the block doesn't break a line and is
98
+ # treated as just an invocation of +text+.
99
+ #
100
+ def PrettyPrint.singleline_format(output='', maxwidth=nil, newline=nil, genspace=nil)
101
+ q = SingleLine.new(output)
102
+ yield q
103
+ output
104
+ end
105
+
106
+ # Creates a buffer for pretty printing.
107
+ #
108
+ # +output+ is an output target. If it is not specified, '' is assumed. It
109
+ # should have a << method which accepts the first argument +obj+ of
110
+ # PrettyPrint#text, the first argument +sep+ of PrettyPrint#breakable, the
111
+ # first argument +newline+ of PrettyPrint.new, and the result of a given
112
+ # block for PrettyPrint.new.
113
+ #
114
+ # +maxwidth+ specifies maximum line length. If it is not specified, 79 is
115
+ # assumed. However actual outputs may overflow +maxwidth+ if long
116
+ # non-breakable texts are provided.
117
+ #
118
+ # +newline+ is used for line breaks. "\n" is used if it is not specified.
119
+ #
120
+ # The block is used to generate spaces. {|width| ' ' * width} is used if it
121
+ # is not given.
122
+ #
123
+ def initialize(output='', maxwidth=79, newline="\n", &genspace)
124
+ @output = output
125
+ @maxwidth = maxwidth
126
+ @newline = newline
127
+ @genspace = genspace || lambda {|n| ' ' * n}
128
+
129
+ @output_width = 0
130
+ @buffer_width = 0
131
+ @buffer = []
132
+
133
+ root_group = Group.new(0)
134
+ @group_stack = [root_group]
135
+ @group_queue = GroupQueue.new(root_group)
136
+ @indent = 0
137
+ end
138
+ attr_reader :output, :maxwidth, :newline, :genspace
139
+ attr_reader :indent, :group_queue
140
+
141
+ def current_group
142
+ @group_stack.last
143
+ end
144
+
145
+ # first? is a predicate to test the call is a first call to first? with
146
+ # current group.
147
+ #
148
+ # It is useful to format comma separated values as:
149
+ #
150
+ # q.group(1, '[', ']') {
151
+ # xxx.each {|yyy|
152
+ # unless q.first?
153
+ # q.text ','
154
+ # q.breakable
155
+ # end
156
+ # ... pretty printing yyy ...
157
+ # }
158
+ # }
159
+ #
160
+ # first? is obsoleted in 1.8.2.
161
+ #
162
+ def first?
163
+ warn "PrettyPrint#first? is obsoleted at 1.8.2."
164
+ current_group.first?
165
+ end
166
+
167
+ def break_outmost_groups
168
+ while @maxwidth < @output_width + @buffer_width
169
+ return unless group = @group_queue.deq
170
+ until group.breakables.empty?
171
+ data = @buffer.shift
172
+ @output_width = data.output(@output, @output_width)
173
+ @buffer_width -= data.width
174
+ end
175
+ while !@buffer.empty? && Text === @buffer.first
176
+ text = @buffer.shift
177
+ @output_width = text.output(@output, @output_width)
178
+ @buffer_width -= text.width
179
+ end
180
+ end
181
+ end
182
+
183
+ # This adds +obj+ as a text of +width+ columns in width.
184
+ #
185
+ # If +width+ is not specified, obj.length is used.
186
+ #
187
+ def text(obj, width=obj.length)
188
+ if @buffer.empty?
189
+ @output << obj
190
+ @output_width += width
191
+ else
192
+ text = @buffer.last
193
+ unless Text === text
194
+ text = Text.new
195
+ @buffer << text
196
+ end
197
+ text.add(obj, width)
198
+ @buffer_width += width
199
+ break_outmost_groups
200
+ end
201
+ end
202
+
203
+ def fill_breakable(sep=' ', width=sep.length)
204
+ group { breakable sep, width }
205
+ end
206
+
207
+ # This tells "you can break a line here if necessary", and a +width+\-column
208
+ # text +sep+ is inserted if a line is not broken at the point.
209
+ #
210
+ # If +sep+ is not specified, " " is used.
211
+ #
212
+ # If +width+ is not specified, +sep.length+ is used. You will have to
213
+ # specify this when +sep+ is a multibyte character, for example.
214
+ #
215
+ def breakable(sep=' ', width=sep.length)
216
+ group = @group_stack.last
217
+ if group.break?
218
+ flush
219
+ @output << @newline
220
+ @output << @genspace.call(@indent)
221
+ @output_width = @indent
222
+ @buffer_width = 0
223
+ else
224
+ @buffer << Breakable.new(sep, width, self)
225
+ @buffer_width += width
226
+ break_outmost_groups
227
+ end
228
+ end
229
+
230
+ # Groups line break hints added in the block. The line break hints are all
231
+ # to be used or not.
232
+ #
233
+ # If +indent+ is specified, the method call is regarded as nested by
234
+ # nest(indent) { ... }.
235
+ #
236
+ # If +open_obj+ is specified, <tt>text open_obj, open_width</tt> is called
237
+ # before grouping. If +close_obj+ is specified, <tt>text close_obj,
238
+ # close_width</tt> is called after grouping.
239
+ #
240
+ def group(indent=0, open_obj='', close_obj='', open_width=open_obj.length, close_width=close_obj.length)
241
+ text open_obj, open_width
242
+ group_sub {
243
+ nest(indent) {
244
+ yield
245
+ }
246
+ }
247
+ text close_obj, close_width
248
+ end
249
+
250
+ def group_sub
251
+ group = Group.new(@group_stack.last.depth + 1)
252
+ @group_stack.push group
253
+ @group_queue.enq group
254
+ begin
255
+ yield
256
+ ensure
257
+ @group_stack.pop
258
+ if group.breakables.empty?
259
+ @group_queue.delete group
260
+ end
261
+ end
262
+ end
263
+
264
+ # Increases left margin after newline with +indent+ for line breaks added in
265
+ # the block.
266
+ #
267
+ def nest(indent)
268
+ @indent += indent
269
+ begin
270
+ yield
271
+ ensure
272
+ @indent -= indent
273
+ end
274
+ end
275
+
276
+ # outputs buffered data.
277
+ #
278
+ def flush
279
+ @buffer.each {|data|
280
+ @output_width = data.output(@output, @output_width)
281
+ }
282
+ @buffer.clear
283
+ @buffer_width = 0
284
+ end
285
+
286
+ class Text
287
+ def initialize
288
+ @objs = []
289
+ @width = 0
290
+ end
291
+ attr_reader :width
292
+
293
+ def output(out, output_width)
294
+ @objs.each {|obj| out << obj}
295
+ output_width + @width
296
+ end
297
+
298
+ def add(obj, width)
299
+ @objs << obj
300
+ @width += width
301
+ end
302
+ end
303
+
304
+ class Breakable
305
+ def initialize(sep, width, q)
306
+ @obj = sep
307
+ @width = width
308
+ @pp = q
309
+ @indent = q.indent
310
+ @group = q.current_group
311
+ @group.breakables.push self
312
+ end
313
+ attr_reader :obj, :width, :indent
314
+
315
+ def output(out, output_width)
316
+ @group.breakables.shift
317
+ if @group.break?
318
+ out << @pp.newline
319
+ out << @pp.genspace.call(@indent)
320
+ @indent
321
+ else
322
+ @pp.group_queue.delete @group if @group.breakables.empty?
323
+ out << @obj
324
+ output_width + @width
325
+ end
326
+ end
327
+ end
328
+
329
+ class Group
330
+ def initialize(depth)
331
+ @depth = depth
332
+ @breakables = []
333
+ @break = false
334
+ end
335
+ attr_reader :depth, :breakables
336
+
337
+ def break
338
+ @break = true
339
+ end
340
+
341
+ def break?
342
+ @break
343
+ end
344
+
345
+ def first?
346
+ if defined? @first
347
+ false
348
+ else
349
+ @first = false
350
+ true
351
+ end
352
+ end
353
+ end
354
+
355
+ class GroupQueue
356
+ def initialize(*groups)
357
+ @queue = []
358
+ groups.each {|g| enq g}
359
+ end
360
+
361
+ def enq(group)
362
+ depth = group.depth
363
+ @queue << [] until depth < @queue.length
364
+ @queue[depth] << group
365
+ end
366
+
367
+ def deq
368
+ @queue.each {|gs|
369
+ (gs.length-1).downto(0) {|i|
370
+ unless gs[i].breakables.empty?
371
+ group = gs.slice!(i, 1).first
372
+ group.break
373
+ return group
374
+ end
375
+ }
376
+ gs.each {|group| group.break}
377
+ gs.clear
378
+ }
379
+ return nil
380
+ end
381
+
382
+ def delete(group)
383
+ @queue[group.depth].delete(group)
384
+ end
385
+ end
386
+
387
+ class SingleLine
388
+ def initialize(output, maxwidth=nil, newline=nil)
389
+ @output = output
390
+ @first = [true]
391
+ end
392
+
393
+ def text(obj, width=nil)
394
+ @output << obj
395
+ end
396
+
397
+ def breakable(sep=' ', width=nil)
398
+ @output << sep
399
+ end
400
+
401
+ def nest(indent)
402
+ yield
403
+ end
404
+
405
+ def group(indent=nil, open_obj='', close_obj='', open_width=nil, close_width=nil)
406
+ @first.push true
407
+ @output << open_obj
408
+ yield
409
+ @output << close_obj
410
+ @first.pop
411
+ end
412
+
413
+ def flush
414
+ end
415
+
416
+ def first?
417
+ result = @first[-1]
418
+ @first[-1] = false
419
+ result
420
+ end
421
+ end
422
+ end
423
+
424
+ module Kernel
425
+ # returns a pretty printed object as a string.
426
+ def pretty_inspect
427
+ PP.pp(self, '')
428
+ end
429
+
430
+ private
431
+ # prints arguments in pretty form.
432
+ #
433
+ # pp returns nil.
434
+ def pp(*objs) # :doc:
435
+ objs.each {|obj|
436
+ PP.pp(obj)
437
+ }
438
+ nil
439
+ end
440
+ module_function :pp
441
+ end
442
+
443
+ class PP < PrettyPrint
444
+ # Outputs +obj+ to +out+ in pretty printed format of
445
+ # +width+ columns in width.
446
+ #
447
+ # If +out+ is omitted, +$>+ is assumed.
448
+ # If +width+ is omitted, 79 is assumed.
449
+ #
450
+ # PP.pp returns +out+.
451
+ def PP.pp(obj, out=$>, width=79)
452
+ q = PP.new(out, width)
453
+ q.guard_inspect_key {q.pp obj}
454
+ q.flush
455
+ #$pp = q
456
+ out << "\n"
457
+ end
458
+
459
+ # Outputs +obj+ to +out+ like PP.pp but with no indent and
460
+ # newline.
461
+ #
462
+ # PP.singleline_pp returns +out+.
463
+ def PP.singleline_pp(obj, out=$>)
464
+ q = SingleLine.new(out)
465
+ q.guard_inspect_key {q.pp obj}
466
+ q.flush
467
+ out
468
+ end
469
+
470
+ # :stopdoc:
471
+ def PP.mcall(obj, mod, meth, *args, &block)
472
+ mod.instance_method(meth).bind(obj).call(*args, &block)
473
+ end
474
+ # :startdoc:
475
+
476
+ @sharing_detection = false
477
+ class << self
478
+ # Returns the sharing detection flag as a boolean value.
479
+ # It is false by default.
480
+ attr_accessor :sharing_detection
481
+ end
482
+
483
+ module PPMethods
484
+ def guard_inspect_key
485
+ if Thread.current[:__recursive_key__] == nil
486
+ Thread.current[:__recursive_key__] = {}
487
+ end
488
+
489
+ if Thread.current[:__recursive_key__][:inspect] == nil
490
+ Thread.current[:__recursive_key__][:inspect] = {}
491
+ end
492
+
493
+ save = Thread.current[:__recursive_key__][:inspect]
494
+
495
+ begin
496
+ Thread.current[:__recursive_key__][:inspect] = {}
497
+ yield
498
+ ensure
499
+ Thread.current[:__recursive_key__][:inspect] = save
500
+ end
501
+ end
502
+
503
+ def check_inspect_key(id)
504
+ Thread.current[:__recursive_key__] &&
505
+ Thread.current[:__recursive_key__][:inspect] &&
506
+ Thread.current[:__recursive_key__][:inspect].include?(id)
507
+ end
508
+ def push_inspect_key(id)
509
+ Thread.current[:__recursive_key__][:inspect][id] = true
510
+ end
511
+ def pop_inspect_key(id)
512
+ Thread.current[:__recursive_key__][:inspect].delete id
513
+ end
514
+
515
+ # Adds +obj+ to the pretty printing buffer
516
+ # using Object#pretty_print or Object#pretty_print_cycle.
517
+ #
518
+ # Object#pretty_print_cycle is used when +obj+ is already
519
+ # printed, a.k.a the object reference chain has a cycle.
520
+ def pp(obj)
521
+ id = obj.object_id
522
+
523
+ if check_inspect_key(id)
524
+ group {obj.pretty_print_cycle self}
525
+ return
526
+ end
527
+
528
+ begin
529
+ push_inspect_key(id)
530
+ group {obj.pretty_print self}
531
+ ensure
532
+ pop_inspect_key(id) unless PP.sharing_detection
533
+ end
534
+ end
535
+
536
+ # A convenience method which is same as follows:
537
+ #
538
+ # group(1, '#<' + obj.class.name, '>') { ... }
539
+ def object_group(obj, &block) # :yield:
540
+ group(1, '#<' + obj.class.name, '>', &block)
541
+ end
542
+
543
+ if 0x100000000.class == Bignum
544
+ # 32bit
545
+ PointerMask = 0xffffffff
546
+ else
547
+ # 64bit
548
+ PointerMask = 0xffffffffffffffff
549
+ end
550
+
551
+ case Object.new.inspect
552
+ when /\A\#<Object:0x([0-9a-f]+)>\z/
553
+ PointerFormat = "%0#{$1.length}x"
554
+ else
555
+ PointerFormat = "%x"
556
+ end
557
+
558
+ def object_address_group(obj, &block)
559
+ id = PointerFormat % (obj.object_id * 2 & PointerMask)
560
+ group(1, "\#<#{obj.class}:0x#{id}", '>', &block)
561
+ end
562
+
563
+ # A convenience method which is same as follows:
564
+ #
565
+ # text ','
566
+ # breakable
567
+ def comma_breakable
568
+ text ','
569
+ breakable
570
+ end
571
+
572
+ # Adds a separated list.
573
+ # The list is separated by comma with breakable space, by default.
574
+ #
575
+ # #seplist iterates the +list+ using +iter_method+.
576
+ # It yields each object to the block given for #seplist.
577
+ # The procedure +separator_proc+ is called between each yields.
578
+ #
579
+ # If the iteration is zero times, +separator_proc+ is not called at all.
580
+ #
581
+ # If +separator_proc+ is nil or not given,
582
+ # +lambda { comma_breakable }+ is used.
583
+ # If +iter_method+ is not given, :each is used.
584
+ #
585
+ # For example, following 3 code fragments has similar effect.
586
+ #
587
+ # q.seplist([1,2,3]) {|v| xxx v }
588
+ #
589
+ # q.seplist([1,2,3], lambda { q.comma_breakable }, :each) {|v| xxx v }
590
+ #
591
+ # xxx 1
592
+ # q.comma_breakable
593
+ # xxx 2
594
+ # q.comma_breakable
595
+ # xxx 3
596
+ def seplist(list, sep=nil, iter_method=:each) # :yield: element
597
+ sep ||= lambda { comma_breakable }
598
+ first = true
599
+ list.__send__(iter_method) {|*v|
600
+ if first
601
+ first = false
602
+ else
603
+ sep.call
604
+ end
605
+ yield(*v)
606
+ }
607
+ end
608
+
609
+ def pp_object(obj)
610
+ object_address_group(obj) {
611
+ seplist(obj.pretty_print_instance_variables, lambda { text ',' }) {|v|
612
+ breakable
613
+ v = v.to_s if Symbol === v
614
+ text v
615
+ text '='
616
+ group(1) {
617
+ breakable ''
618
+ pp(obj.instance_eval(v))
619
+ }
620
+ }
621
+ }
622
+ end
623
+
624
+ def pp_hash(obj)
625
+ group(1, '{', '}') {
626
+ seplist(obj, nil, :each_pair) {|k, v|
627
+ group {
628
+ pp k
629
+ text '=>'
630
+ group(1) {
631
+ breakable ''
632
+ pp v
633
+ }
634
+ }
635
+ }
636
+ }
637
+ end
638
+ end
639
+
640
+ include PPMethods
641
+
642
+ class SingleLine < PrettyPrint::SingleLine
643
+ include PPMethods
644
+ end
645
+
646
+ module ObjectMixin
647
+ # 1. specific pretty_print
648
+ # 2. specific inspect
649
+ # 3. specific to_s if instance variable is empty
650
+ # 4. generic pretty_print
651
+
652
+ # A default pretty printing method for general objects.
653
+ # It calls #pretty_print_instance_variables to list instance variables.
654
+ #
655
+ # If +self+ has a customized (redefined) #inspect method,
656
+ # the result of self.inspect is used but it obviously has no
657
+ # line break hints.
658
+ #
659
+ # This module provides predefined #pretty_print methods for some of
660
+ # the most commonly used built-in classes for convenience.
661
+ def pretty_print(q)
662
+ if /\(Kernel\)#/ !~ Object.instance_method(:method).bind(self).call(:inspect).inspect
663
+ q.text self.inspect
664
+ elsif /\(Kernel\)#/ !~ Object.instance_method(:method).bind(self).call(:to_s).inspect && instance_variables.empty?
665
+ q.text self.to_s
666
+ else
667
+ q.pp_object(self)
668
+ end
669
+ end
670
+
671
+ # A default pretty printing method for general objects that are
672
+ # detected as part of a cycle.
673
+ def pretty_print_cycle(q)
674
+ q.object_address_group(self) {
675
+ q.breakable
676
+ q.text '...'
677
+ }
678
+ end
679
+
680
+ # Returns a sorted array of instance variable names.
681
+ #
682
+ # This method should return an array of names of instance variables as symbols or strings as:
683
+ # +[:@a, :@b]+.
684
+ def pretty_print_instance_variables
685
+ instance_variables.sort
686
+ end
687
+
688
+ # Is #inspect implementation using #pretty_print.
689
+ # If you implement #pretty_print, it can be used as follows.
690
+ #
691
+ # alias inspect pretty_print_inspect
692
+ #
693
+ # However, doing this requires that every class that #inspect is called on
694
+ # implement #pretty_print, or a RuntimeError will be raised.
695
+ def pretty_print_inspect
696
+ if /\(PP::ObjectMixin\)#/ =~ Object.instance_method(:method).bind(self).call(:pretty_print).inspect
697
+ raise "pretty_print is not overridden for #{self.class}"
698
+ end
699
+ PP.singleline_pp(self, '')
700
+ end
701
+ end
702
+ end
703
+
704
+ class Array
705
+ def pretty_print(q)
706
+ q.group(1, '[', ']') {
707
+ q.seplist(self) {|v|
708
+ q.pp v
709
+ }
710
+ }
711
+ end
712
+
713
+ def pretty_print_cycle(q)
714
+ q.text(empty? ? '[]' : '[...]')
715
+ end
716
+ end
717
+
718
+ class Hash
719
+ def pretty_print(q)
720
+ q.pp_hash self
721
+ end
722
+
723
+ def pretty_print_cycle(q)
724
+ q.text(empty? ? '{}' : '{...}')
725
+ end
726
+ end
727
+
728
+ class << ENV
729
+ def pretty_print(q)
730
+ h = {}
731
+ ENV.keys.sort.each {|k|
732
+ h[k] = ENV[k]
733
+ }
734
+ q.pp_hash h
735
+ end
736
+ end
737
+
738
+ class Struct
739
+ def pretty_print(q)
740
+ q.group(1, '#<struct ' + PP.mcall(self, Kernel, :class).name, '>') {
741
+ q.seplist(PP.mcall(self, Struct, :members), lambda { q.text "," }) {|member|
742
+ q.breakable
743
+ q.text member.to_s
744
+ q.text '='
745
+ q.group(1) {
746
+ q.breakable ''
747
+ q.pp self[member]
748
+ }
749
+ }
750
+ }
751
+ end
752
+
753
+ def pretty_print_cycle(q)
754
+ q.text sprintf("#<struct %s:...>", PP.mcall(self, Kernel, :class).name)
755
+ end
756
+ end
757
+
758
+ class Range
759
+ def pretty_print(q)
760
+ q.pp self.begin
761
+ q.breakable ''
762
+ q.text(self.exclude_end? ? '...' : '..')
763
+ q.breakable ''
764
+ q.pp self.end
765
+ end
766
+ end
767
+
768
+ class File
769
+ class Stat
770
+ def pretty_print(q)
771
+ require 'etc.so'
772
+ q.object_group(self) {
773
+ q.breakable
774
+ q.text sprintf("dev=0x%x", self.dev); q.comma_breakable
775
+ q.text "ino="; q.pp self.ino; q.comma_breakable
776
+ q.group {
777
+ m = self.mode
778
+ q.text sprintf("mode=0%o", m)
779
+ q.breakable
780
+ q.text sprintf("(%s %c%c%c%c%c%c%c%c%c)",
781
+ self.ftype,
782
+ (m & 0400 == 0 ? ?- : ?r),
783
+ (m & 0200 == 0 ? ?- : ?w),
784
+ (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
785
+ (m & 04000 == 0 ? ?x : ?s)),
786
+ (m & 0040 == 0 ? ?- : ?r),
787
+ (m & 0020 == 0 ? ?- : ?w),
788
+ (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
789
+ (m & 02000 == 0 ? ?x : ?s)),
790
+ (m & 0004 == 0 ? ?- : ?r),
791
+ (m & 0002 == 0 ? ?- : ?w),
792
+ (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
793
+ (m & 01000 == 0 ? ?x : ?t)))
794
+ }
795
+ q.comma_breakable
796
+ q.text "nlink="; q.pp self.nlink; q.comma_breakable
797
+ q.group {
798
+ q.text "uid="; q.pp self.uid
799
+ begin
800
+ pw = Etc.getpwuid(self.uid)
801
+ rescue ArgumentError
802
+ end
803
+ if pw
804
+ q.breakable; q.text "(#{pw.name})"
805
+ end
806
+ }
807
+ q.comma_breakable
808
+ q.group {
809
+ q.text "gid="; q.pp self.gid
810
+ begin
811
+ gr = Etc.getgrgid(self.gid)
812
+ rescue ArgumentError
813
+ end
814
+ if gr
815
+ q.breakable; q.text "(#{gr.name})"
816
+ end
817
+ }
818
+ q.comma_breakable
819
+ q.group {
820
+ q.text sprintf("rdev=0x%x", self.rdev)
821
+ q.breakable
822
+ q.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor)
823
+ }
824
+ q.comma_breakable
825
+ q.text "size="; q.pp self.size; q.comma_breakable
826
+ q.text "blksize="; q.pp self.blksize; q.comma_breakable
827
+ q.text "blocks="; q.pp self.blocks; q.comma_breakable
828
+ q.group {
829
+ t = self.atime
830
+ q.text "atime="; q.pp t
831
+ q.breakable; q.text "(#{t.tv_sec})"
832
+ }
833
+ q.comma_breakable
834
+ q.group {
835
+ t = self.mtime
836
+ q.text "mtime="; q.pp t
837
+ q.breakable; q.text "(#{t.tv_sec})"
838
+ }
839
+ q.comma_breakable
840
+ q.group {
841
+ t = self.ctime
842
+ q.text "ctime="; q.pp t
843
+ q.breakable; q.text "(#{t.tv_sec})"
844
+ }
845
+ }
846
+ end
847
+ end
848
+ end
849
+
850
+ class MatchData
851
+ def pretty_print(q)
852
+ nc = []
853
+ self.regexp.named_captures.each {|name, indexes|
854
+ indexes.each {|i| nc[i] = name }
855
+ }
856
+ q.object_group(self) {
857
+ q.breakable
858
+ q.seplist(0...self.size, lambda { q.breakable }) {|i|
859
+ if i == 0
860
+ q.pp self[i]
861
+ else
862
+ if nc[i]
863
+ q.text nc[i]
864
+ else
865
+ q.pp i
866
+ end
867
+ q.text ':'
868
+ q.pp self[i]
869
+ end
870
+ }
871
+ }
872
+ end
873
+ end
874
+
875
+ class Object
876
+ include PP::ObjectMixin
877
+ end
878
+
879
+ [Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c|
880
+ c.class_eval {
881
+ def pretty_print_cycle(q)
882
+ q.text inspect
883
+ end
884
+ }
885
+ }
886
+
887
+ [Numeric, FalseClass, TrueClass, Module].each {|c|
888
+ c.class_eval {
889
+ def pretty_print(q)
890
+ q.text inspect
891
+ end
892
+ }
893
+ }