mr 0.35.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +22 -0
  5. data/README.md +29 -0
  6. data/bench/all.rb +4 -0
  7. data/bench/factory.rb +68 -0
  8. data/bench/fake_record.rb +174 -0
  9. data/bench/model.rb +201 -0
  10. data/bench/read_model.rb +191 -0
  11. data/bench/results/factory.txt +21 -0
  12. data/bench/results/fake_record.txt +37 -0
  13. data/bench/results/model.txt +44 -0
  14. data/bench/results/read_model.txt +46 -0
  15. data/bench/setup.rb +132 -0
  16. data/lib/mr.rb +11 -0
  17. data/lib/mr/after_commit.rb +49 -0
  18. data/lib/mr/after_commit/fake_record.rb +39 -0
  19. data/lib/mr/after_commit/record.rb +48 -0
  20. data/lib/mr/after_commit/record_procs_methods.rb +82 -0
  21. data/lib/mr/factory.rb +82 -0
  22. data/lib/mr/factory/config.rb +240 -0
  23. data/lib/mr/factory/model_factory.rb +103 -0
  24. data/lib/mr/factory/model_stack.rb +28 -0
  25. data/lib/mr/factory/read_model_factory.rb +104 -0
  26. data/lib/mr/factory/record_factory.rb +130 -0
  27. data/lib/mr/factory/record_stack.rb +219 -0
  28. data/lib/mr/fake_query.rb +53 -0
  29. data/lib/mr/fake_record.rb +58 -0
  30. data/lib/mr/fake_record/associations.rb +257 -0
  31. data/lib/mr/fake_record/attributes.rb +168 -0
  32. data/lib/mr/fake_record/persistence.rb +116 -0
  33. data/lib/mr/json_field.rb +180 -0
  34. data/lib/mr/json_field/fake_record.rb +31 -0
  35. data/lib/mr/json_field/record.rb +38 -0
  36. data/lib/mr/model.rb +67 -0
  37. data/lib/mr/model/associations.rb +161 -0
  38. data/lib/mr/model/configuration.rb +67 -0
  39. data/lib/mr/model/fields.rb +177 -0
  40. data/lib/mr/model/persistence.rb +79 -0
  41. data/lib/mr/query.rb +126 -0
  42. data/lib/mr/read_model.rb +83 -0
  43. data/lib/mr/read_model/data.rb +38 -0
  44. data/lib/mr/read_model/fields.rb +218 -0
  45. data/lib/mr/read_model/query_expression.rb +188 -0
  46. data/lib/mr/read_model/querying.rb +214 -0
  47. data/lib/mr/read_model/set_querying.rb +82 -0
  48. data/lib/mr/read_model/subquery.rb +98 -0
  49. data/lib/mr/record.rb +35 -0
  50. data/lib/mr/test_helpers.rb +229 -0
  51. data/lib/mr/type_converter.rb +85 -0
  52. data/lib/mr/version.rb +3 -0
  53. data/log/.gitkeep +0 -0
  54. data/mr.gemspec +29 -0
  55. data/test/helper.rb +21 -0
  56. data/test/support/db.rb +10 -0
  57. data/test/support/factory.rb +13 -0
  58. data/test/support/factory/area.rb +6 -0
  59. data/test/support/factory/comment.rb +14 -0
  60. data/test/support/factory/image.rb +6 -0
  61. data/test/support/factory/user.rb +6 -0
  62. data/test/support/models/area.rb +58 -0
  63. data/test/support/models/comment.rb +60 -0
  64. data/test/support/models/image.rb +53 -0
  65. data/test/support/models/user.rb +96 -0
  66. data/test/support/read_model/querying.rb +150 -0
  67. data/test/support/read_models/comment_with_user_data.rb +27 -0
  68. data/test/support/read_models/set_data.rb +49 -0
  69. data/test/support/read_models/subquery_data.rb +41 -0
  70. data/test/support/read_models/user_with_area_data.rb +15 -0
  71. data/test/support/schema.rb +39 -0
  72. data/test/support/setup_test_db.rb +10 -0
  73. data/test/system/factory/model_factory_tests.rb +87 -0
  74. data/test/system/factory/model_stack_tests.rb +30 -0
  75. data/test/system/factory/record_factory_tests.rb +84 -0
  76. data/test/system/factory/record_stack_tests.rb +51 -0
  77. data/test/system/factory_tests.rb +32 -0
  78. data/test/system/read_model_tests.rb +199 -0
  79. data/test/system/with_model_tests.rb +275 -0
  80. data/test/unit/after_commit/fake_record_tests.rb +110 -0
  81. data/test/unit/after_commit/record_procs_methods_tests.rb +177 -0
  82. data/test/unit/after_commit/record_tests.rb +134 -0
  83. data/test/unit/after_commit_tests.rb +113 -0
  84. data/test/unit/factory/config_tests.rb +651 -0
  85. data/test/unit/factory/model_factory_tests.rb +473 -0
  86. data/test/unit/factory/model_stack_tests.rb +97 -0
  87. data/test/unit/factory/read_model_factory_tests.rb +195 -0
  88. data/test/unit/factory/record_factory_tests.rb +446 -0
  89. data/test/unit/factory/record_stack_tests.rb +549 -0
  90. data/test/unit/factory_tests.rb +213 -0
  91. data/test/unit/fake_query_tests.rb +137 -0
  92. data/test/unit/fake_record/associations_tests.rb +585 -0
  93. data/test/unit/fake_record/attributes_tests.rb +265 -0
  94. data/test/unit/fake_record/persistence_tests.rb +239 -0
  95. data/test/unit/fake_record_tests.rb +106 -0
  96. data/test/unit/json_field/fake_record_tests.rb +75 -0
  97. data/test/unit/json_field/record_tests.rb +80 -0
  98. data/test/unit/json_field_tests.rb +302 -0
  99. data/test/unit/model/associations_tests.rb +346 -0
  100. data/test/unit/model/configuration_tests.rb +92 -0
  101. data/test/unit/model/fields_tests.rb +278 -0
  102. data/test/unit/model/persistence_tests.rb +114 -0
  103. data/test/unit/model_tests.rb +137 -0
  104. data/test/unit/query_tests.rb +300 -0
  105. data/test/unit/read_model/data_tests.rb +56 -0
  106. data/test/unit/read_model/fields_tests.rb +416 -0
  107. data/test/unit/read_model/query_expression_tests.rb +381 -0
  108. data/test/unit/read_model/querying_tests.rb +613 -0
  109. data/test/unit/read_model/set_querying_tests.rb +149 -0
  110. data/test/unit/read_model/subquery_tests.rb +242 -0
  111. data/test/unit/read_model_tests.rb +187 -0
  112. data/test/unit/record_tests.rb +45 -0
  113. data/test/unit/test_helpers_tests.rb +431 -0
  114. data/test/unit/type_converter_tests.rb +207 -0
  115. metadata +285 -0
@@ -0,0 +1,191 @@
1
+ $LOAD_PATH.push File.expand_path('../..', __FILE__)
2
+ require 'bench/setup'
3
+
4
+ profiler = Bench::Profiler.new("bench/results/read_model.txt")
5
+ profiler.run("MR::ReadModel") do
6
+
7
+ section "Fields" do
8
+ read_model_class = Class.new{ include MR::ReadModel }
9
+
10
+ benchmark("field") do |n|
11
+ read_model_class.field "field_#{n}", :string
12
+ end
13
+ benchmark("field with select args") do |n|
14
+ read_model_class.field "field_args_#{n}", :string, "column_#{n}"
15
+ end
16
+ benchmark("field with select block") do |n|
17
+ read_model_class.field("field_block_#{n}", :string){ |c| "table.#{c}" }
18
+ end
19
+
20
+ read_model_class = Class.new do
21
+ include MR::ReadModel
22
+ field :string, :string
23
+ field :text, :text
24
+ field :binary, :binary
25
+ field :integer, :integer
26
+ field :primary_key, :primary_key
27
+ field :float, :float
28
+ field :decimal, :decimal
29
+ field :datetime, :datetime
30
+ field :timestamp, :timestamp
31
+ field :time, :time
32
+ field :date, :date
33
+ field :boolean, :boolean
34
+ end
35
+
36
+ benchmark("typecast string") do |n|
37
+ read_model = read_model_class.new('string' => 'String')
38
+ read_model.string
39
+ end
40
+ benchmark("typecast text") do |n|
41
+ read_model = read_model_class.new('text' => 'Text')
42
+ read_model.text
43
+ end
44
+ benchmark("typecast binary") do |n|
45
+ read_model = read_model_class.new('binary' => "\000\001\002\003\004")
46
+ read_model.binary
47
+ end
48
+ benchmark("typecast integer") do |n|
49
+ read_model = read_model_class.new('integer' => '100')
50
+ read_model.integer
51
+ end
52
+ benchmark("typecast primary key") do |n|
53
+ read_model = read_model_class.new('primary_key' => '125')
54
+ read_model.primary_key
55
+ end
56
+ benchmark("typecast float") do |n|
57
+ read_model = read_model_class.new('float' => '6.1370')
58
+ read_model.float
59
+ end
60
+ benchmark("typecast decimal") do |n|
61
+ read_model = read_model_class.new('decimal' => '33.4755926134924')
62
+ read_model.decimal
63
+ end
64
+ benchmark("typecast datetime") do |n|
65
+ read_model = read_model_class.new('datetime' => '2013-11-18 21:29:10')
66
+ read_model.datetime
67
+ end
68
+ benchmark("typecast timestamp") do |n|
69
+ read_model = read_model_class.new('timestamp' => '2013-11-18 22:10:36.660846')
70
+ read_model.timestamp
71
+ end
72
+ benchmark("typecast time") do |n|
73
+ read_model = read_model_class.new('time' => '21:29:10.905011')
74
+ read_model.time
75
+ end
76
+ benchmark("typecast date") do |n|
77
+ read_model = read_model_class.new('date' => '2013-11-18')
78
+ read_model.date
79
+ end
80
+ benchmark("typecast boolean") do |n|
81
+ read_model = read_model_class.new('boolean' => 't')
82
+ read_model.boolean
83
+ end
84
+
85
+ end
86
+
87
+ section "Querying" do
88
+ read_model_class = Class.new{ include MR::ReadModel }
89
+
90
+ benchmark("select") do |n|
91
+ read_model_class.select "column_#{n}"
92
+ end
93
+ benchmark("select with block") do |n|
94
+ read_model_class.select{ |column| "table.#{column}" }
95
+ end
96
+ benchmark("from") do |n|
97
+ read_model_class.from((n % 2 == 0) ? UserRecord : AreaRecord)
98
+ end
99
+ benchmark("joins") do |n|
100
+ read_model_class.joins "table_#{n}"
101
+ end
102
+ benchmark("joins with block") do |n|
103
+ read_model_class.joins{ |table| table }
104
+ end
105
+ benchmark("where") do |n|
106
+ read_model_class.where :column => n
107
+ end
108
+ benchmark("where with block") do |n|
109
+ read_model_class.where{ |value| { :column => value } }
110
+ end
111
+ benchmark("order") do |n|
112
+ read_model_class.order "column_#{n}"
113
+ end
114
+ benchmark("order with block") do |n|
115
+ read_model_class.order{ |column| "table.#{column}" }
116
+ end
117
+ benchmark("group") do |n|
118
+ read_model_class.group "column_#{n}"
119
+ end
120
+ benchmark("group with block") do |n|
121
+ read_model_class.group{ |column| "table.#{column}" }
122
+ end
123
+ benchmark("having") do |n|
124
+ read_model_class.having "COUNT(column_#{n})"
125
+ end
126
+ benchmark("having with block") do |n|
127
+ read_model_class.having{ |column| "COUNT(table.#{column})" }
128
+ end
129
+ benchmark("limit") do |n|
130
+ read_model_class.limit n
131
+ end
132
+ benchmark("limit with block") do |n|
133
+ read_model_class.limit{ |count| count }
134
+ end
135
+ benchmark("offset") do |n|
136
+ read_model_class.offset n
137
+ end
138
+ benchmark("offset with block") do |n|
139
+ read_model_class.offset{ |count| count }
140
+ end
141
+ benchmark("merge") do |n|
142
+ read_model_class.merge((n % 2 == 0) ? UserRecord.scoped : AreaRecord.scoped)
143
+ end
144
+ benchmark("merge with block") do |n|
145
+ read_model_class.merge{ |value| AreaRecord.scoped }
146
+ end
147
+ benchmark("inner join subquery") do |n|
148
+ read_model_class.inner_join_subquery do
149
+ read_model{ from AreaRecord }
150
+ end
151
+ end
152
+ benchmark("left outer join subquery") do |n|
153
+ read_model_class.left_outer_join_subquery do
154
+ read_model{ from AreaRecord }
155
+ end
156
+ end
157
+ benchmark("right outer join subquery") do |n|
158
+ read_model_class.right_outer_join_subquery do
159
+ read_model{ from AreaRecord }
160
+ end
161
+ end
162
+ benchmark("full outer join subquery") do |n|
163
+ read_model_class.full_outer_join_subquery do
164
+ read_model{ from AreaRecord }
165
+ end
166
+ end
167
+
168
+ first_area = AreaRecord.new(:name => 'Area1').tap(&:save)
169
+ second_area = AreaRecord.new(:name => 'Area2').tap(&:save)
170
+ first_user = UserRecord.new(:name => 'User1').tap do |u|
171
+ u.area = first_area
172
+ u.save
173
+ end
174
+ second_user = UserRecord.new(:name => 'User2').tap do |u|
175
+ u.area = second_area
176
+ u.save
177
+ end
178
+ comment = CommentRecord.new(:parent => first_user).tap(&:save)
179
+
180
+ benchmark("find") do
181
+ UserWithAreaData.find(first_user.id)
182
+ end
183
+ benchmark("query") do
184
+ UserWithAreaData.query.results
185
+ end
186
+ benchmark("subquery query") do
187
+ SubqueryData.query(:started_on => first_user.started_on).results
188
+ end
189
+ end
190
+
191
+ end
@@ -0,0 +1,21 @@
1
+ MR::Factory
2
+ -----------
3
+ primary_key new provider | 62.22 ms | 1.69 MB |
4
+ primary_key existing provider | 33.6 ms | 0.0 MB |
5
+ integer | 22.66 ms | 0.0 MB |
6
+ float | 22.03 ms | 0.0 MB |
7
+ decimal | 61.53 ms | 0.35 MB |
8
+ date | 76.27 ms | 0.41 MB |
9
+ datetime | 27.89 ms | 0.0 MB |
10
+ time | 32.05 ms | 0.0 MB |
11
+ timestamp | 31.0 ms | 0.77 MB |
12
+ string | 95.36 ms | 2.93 MB |
13
+ text | 149.15 ms | 1.42 MB |
14
+ slug | 126.49 ms | 1.14 MB |
15
+ hex | 136.85 ms | 0.96 MB |
16
+ file_name | 124.49 ms | 2.59 MB |
17
+ dir_path | 159.27 ms | 0.92 MB |
18
+ file_path | 289.76 ms | 23.92 MB |
19
+ binary | 31.31 ms | 0.59 MB |
20
+ boolean | 25.62 ms | 0.0 MB |
21
+ Total | 2.43 s | 33.33 MB |
@@ -0,0 +1,37 @@
1
+ MR::FakeRecord
2
+ --------------
3
+ Initialization
4
+ initialize with no arguments | 19.78 ms | 1.27 MB |
5
+ initialize with a hash | 93.43 ms | 0.79 MB |
6
+ Comparison
7
+ == unequal | 7.12 ms | 0.02 MB |
8
+ == equal | 7.36 ms | 0.0 MB |
9
+ Attributes
10
+ attribute | 403.95 ms | 131.0 MB |
11
+ read single attribute | 1.87 ms | 0.02 MB |
12
+ write single attribute | 11.93 ms | 0.0 MB |
13
+ read single attribute was | 15.17 ms | 0.0 MB |
14
+ single attribute changed? | 22.97 ms | 0.0 MB |
15
+ attributes | 186.34 ms | 25.01 MB |
16
+ attributes= | 85.33 ms | 0.84 MB |
17
+ columns | 48.05 ms | 0.0 MB |
18
+ Associations
19
+ belongs_to | 311.59 ms | 101.88 MB |
20
+ has_many | 348.34 ms | 5.81 MB |
21
+ has_one | 376.29 ms | 112.59 MB |
22
+ polymorphic_belongs_to | 340.73 ms | 120.84 MB |
23
+ read belongs to | 23.1 ms | 0.0 MB |
24
+ write belongs to | 58.97 ms | 0.05 MB |
25
+ read has many | 24.1 ms | 0.0 MB |
26
+ write has many | 47.59 ms | 0.0 MB |
27
+ read has one | 21.69 ms | 0.0 MB |
28
+ write has one | 29.72 ms | 0.1 MB |
29
+ read polymorphic belongs to | 24.57 ms | 0.0 MB |
30
+ write polymorphic belongs to | 75.65 ms | 0.02 MB |
31
+ reflect_on_all_associations | 200.93 ms | 62.18 MB |
32
+ association | 5.24 ms | 0.0 MB |
33
+ Persistence
34
+ save! | 443.02 ms | 7.63 MB |
35
+ destroy | 17.28 ms | 0.04 MB |
36
+ valid? and errors | 142.18 ms | 0.0 MB |
37
+ Total | 8.38 s | 514.16 MB |
@@ -0,0 +1,44 @@
1
+ MR::Model
2
+ ---------
3
+ Configuration
4
+ record_class read | 6.2 ms | 0.0 MB |
5
+ record_class write | 16.31 ms | 0.0 MB |
6
+ Initialization
7
+ initialize with no arguments | 328.62 ms | 38.23 MB |
8
+ initialize with a record | 29.01 ms | 0.73 MB |
9
+ initialize with a hash | 900.97 ms | 59.46 MB |
10
+ initialize with a record and hash | 451.5 ms | 1.08 MB |
11
+ Comparison
12
+ == unequal | 58.81 ms | 0.0 MB |
13
+ == equal | 59.7 ms | 0.0 MB |
14
+ Fields
15
+ field_reader | 430.61 ms | 169.25 MB |
16
+ field_writer | 263.24 ms | 65.88 MB |
17
+ field_accessor | 672.82 ms | 250.2 MB |
18
+ read single field | 25.72 ms | 0.0 MB |
19
+ write single field | 123.32 ms | 2.1 MB |
20
+ read single field was | 35.86 ms | 0.31 MB |
21
+ single field changed | 37.95 ms | 0.0 MB |
22
+ fields | 278.65 ms | 21.94 MB |
23
+ fields= | 415.15 ms | 2.04 MB |
24
+ Associations
25
+ belongs_to | 304.7 ms | 121.5 MB |
26
+ has_many | 324.99 ms | 1.86 MB |
27
+ has_one | 334.34 ms | 0.0 MB |
28
+ polymorphic_belongs_to | 315.62 ms | 1.72 MB |
29
+ read belongs to | 198.68 ms | 0.0 MB |
30
+ write belongs to | 482.21 ms | 0.0 MB |
31
+ read has many | 218.64 ms | 0.0 MB |
32
+ write has many | 5884.43 ms | 519.03 MB |
33
+ read has one | 114.22 ms | 0.79 MB |
34
+ write has one | 12847.94 ms | 805.3 MB |
35
+ read polymorphic belongs to | 258.96 ms | 0.0 MB |
36
+ write polymorphic belongs to | 657.58 ms | 0.04 MB |
37
+ Persistence
38
+ save | 8171.23 ms | 206.38 MB |
39
+ destroy | 3550.59 ms | 0.02 MB |
40
+ valid? and errors | 6785.77 ms | 0.04 MB |
41
+ Querying
42
+ find | 4513.17 ms | 0.0 MB |
43
+ all | 3885.71 ms | 0.0 MB |
44
+ Total | 83.28 s | 1007.97 MB |
@@ -0,0 +1,46 @@
1
+ MR::ReadModel
2
+ -------------
3
+ Fields
4
+ field | 189.57 ms | 81.12 MB |
5
+ field with select args | 258.46 ms | 61.79 MB |
6
+ field with select block | 393.61 ms | 177.38 MB |
7
+ typecast string | 46.82 ms | 2.3 MB |
8
+ typecast text | 54.24 ms | 0.72 MB |
9
+ typecast binary | 39.91 ms | 0.29 MB |
10
+ typecast integer | 51.2 ms | 0.0 MB |
11
+ typecast primary key | 54.25 ms | 0.0 MB |
12
+ typecast float | 48.58 ms | 0.0 MB |
13
+ typecast decimal | 63.79 ms | 0.9 MB |
14
+ typecast datetime | 222.36 ms | 0.0 MB |
15
+ typecast timestamp | 224.93 ms | 0.0 MB |
16
+ typecast time | 225.3 ms | 0.0 MB |
17
+ typecast date | 281.33 ms | 0.0 MB |
18
+ typecast boolean | 67.83 ms | 0.0 MB |
19
+ Querying
20
+ select | 49.15 ms | 0.0 MB |
21
+ select with block | 65.79 ms | 0.16 MB |
22
+ from | 10.56 ms | 0.0 MB |
23
+ joins | 48.56 ms | 0.08 MB |
24
+ joins with block | 67.98 ms | -0.16 MB |
25
+ where | 81.42 ms | 0.07 MB |
26
+ where with block | 80.65 ms | 0.07 MB |
27
+ order | 78.45 ms | 0.07 MB |
28
+ order with block | 90.07 ms | 0.07 MB |
29
+ group | 53.91 ms | 0.07 MB |
30
+ group with block | 93.91 ms | 0.07 MB |
31
+ having | 76.13 ms | -0.75 MB |
32
+ having with block | 79.41 ms | 0.08 MB |
33
+ limit | 49.43 ms | 0.07 MB |
34
+ limit with block | 80.49 ms | 0.07 MB |
35
+ offset | 68.07 ms | 0.07 MB |
36
+ offset with block | 73.57 ms | -1.16 MB |
37
+ merge | 369.98 ms | 0.08 MB |
38
+ merge with block | 103.48 ms | 0.07 MB |
39
+ inner join subquery | 77.23 ms | 24.49 MB |
40
+ left outer join subquery | 76.27 ms | 24.32 MB |
41
+ right outer join subquery | 83.2 ms | 24.64 MB |
42
+ full outer join subquery | 70.24 ms | 23.23 MB |
43
+ find | 6798.8 ms | 833.29 MB |
44
+ query | 6976.37 ms | 404.91 MB |
45
+ subquery query | 14832.29 ms | 1193.85 MB |
46
+ Total | 43.85 s | 1071.88 MB |
@@ -0,0 +1,132 @@
1
+ $LOAD_PATH.push File.expand_path('../..', __FILE__)
2
+ require 'test/support/setup_test_db'
3
+ require 'mr'
4
+ require 'whysoslow'
5
+
6
+ require 'test/support/models/area'
7
+ require 'test/support/models/comment'
8
+ require 'test/support/models/image'
9
+ require 'test/support/models/user'
10
+ require 'test/support/read_models/subquery_data'
11
+ require 'test/support/read_models/user_with_area_data'
12
+
13
+ module Bench
14
+
15
+ class Profiler
16
+
17
+ def initialize(file_path)
18
+ @logger = Logger.new(file_path)
19
+ end
20
+
21
+ def run(title, &block)
22
+ printer = Printer.new(@logger, title)
23
+ Whysoslow::Runner.new(printer, :time_unit => 's').run do
24
+ self.instance_eval(&block)
25
+ end
26
+ end
27
+
28
+ def section(title, &block)
29
+ @logger.puts(title)
30
+ ActiveRecord::Base.transaction do
31
+ yield
32
+ raise ActiveRecord::Rollback
33
+ end
34
+ end
35
+
36
+ def benchmark(message, times = 10000, options = nil, &block)
37
+ GC.disable
38
+ iterations = [*0..(times - 1)]
39
+ printer = BenchmarkPrinter.new(@logger, message)
40
+ Whysoslow::Runner.new(printer, options || {}).run{ iterations.each(&block) }
41
+ GC.enable
42
+ GC.start
43
+ end
44
+
45
+ end
46
+
47
+ class BasePrinter
48
+
49
+ def initialize(logger, title)
50
+ @logger = logger
51
+ @title = title
52
+ @logger.sync = true
53
+ end
54
+
55
+ private
56
+
57
+ def output_columns(*columns)
58
+ @logger.puts "#{columns.join(" | ")} |"
59
+ end
60
+
61
+ def message(string)
62
+ string.ljust(40)
63
+ end
64
+
65
+ def time_taken(results)
66
+ measurement = results.measurements.find{ |(type, time)| type == :real }
67
+ time_taken = RoundedNumber.new(measurement.last).to_s.rjust(10)
68
+ unit = results.measurement_units.to_s.ljust(2)
69
+ "#{time_taken} #{unit}"
70
+ end
71
+
72
+ def memory_diff(results)
73
+ memory = results.snapshots.map(&:memory)
74
+ memory_diff = RoundedNumber.new(memory.last - memory.first).to_s.rjust(10)
75
+ unit = results.snapshot_units.to_s.ljust(2)
76
+ "#{memory_diff} #{unit}"
77
+ end
78
+
79
+ end
80
+
81
+ class Printer < BasePrinter
82
+
83
+ def print(thing)
84
+ case thing
85
+ when :title
86
+ @logger.puts(@title)
87
+ @logger.puts('-' * @title.size)
88
+ when Whysoslow::Results
89
+ output_columns message("Total"),
90
+ time_taken(thing),
91
+ memory_diff(thing)
92
+ end
93
+ end
94
+
95
+ end
96
+
97
+ class BenchmarkPrinter < BasePrinter
98
+
99
+ def print(thing)
100
+ case thing
101
+ when Whysoslow::Results
102
+ output_columns message(" #{@title}"),
103
+ time_taken(thing),
104
+ memory_diff(thing)
105
+ end
106
+ end
107
+
108
+ end
109
+
110
+ class Logger
111
+
112
+ def initialize(file_path)
113
+ @file = File.open(file_path, 'w')
114
+ @ios = [ @file, $stdout ]
115
+ end
116
+
117
+ def method_missing(method, *args, &block)
118
+ @ios.each{ |io| io.send(method, *args, &block) }
119
+ end
120
+
121
+ end
122
+
123
+ module RoundedNumber
124
+ ROUND_PRECISION = 2
125
+ ROUND_MODIFIER = 10 ** ROUND_PRECISION
126
+
127
+ def self.new(number)
128
+ rounded_number = (number * ROUND_MODIFIER).to_i / ROUND_MODIFIER.to_f
129
+ end
130
+ end
131
+
132
+ end