mr 0.35.2

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.
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