testoscope 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/testoscope.iml +11 -0
- data/.idea/workspace.xml +269 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +130 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/testoscope/version.rb +3 -0
- data/lib/testoscope.rb +134 -0
- data/results.png +0 -0
- data/testoscope.gemspec +41 -0
- metadata +136 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 617847638870b0e1393458cc325f0f2ec9f0ec00
|
|
4
|
+
data.tar.gz: 400016aae971293b20d7241fd8618ef560d25087
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 57f2654d2b92a7e7c8e8e6edb2f5f8be748cb0aac1bb006867b6547284b8ca09b40f1185f8f942029e86cc822e11c9667441cae2026250202a57a8e612a0a5cc
|
|
7
|
+
data.tar.gz: d72ad93aaf2f07f3e3da160a0177038e16a6f1cd8b314025f20f33b5c1f362ae6bda8e2c59799ef243acdb2cd1b7a33604648cb8c175571efd82900c2cec1338
|
data/.gitignore
ADDED
data/.idea/misc.xml
ADDED
data/.idea/modules.xml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/testoscope.iml" filepath="$PROJECT_DIR$/.idea/testoscope.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="RUBY_MODULE" version="4">
|
|
3
|
+
<component name="ModuleRunConfigurationManager">
|
|
4
|
+
<shared />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="NewModuleRootManager">
|
|
7
|
+
<content url="file://$MODULE_DIR$" />
|
|
8
|
+
<orderEntry type="jdk" jdkName="RVM: ruby-2.4.1 [apivore]" jdkType="RUBY_SDK" />
|
|
9
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
10
|
+
</component>
|
|
11
|
+
</module>
|
data/.idea/workspace.xml
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ChangeListManager">
|
|
4
|
+
<list default="true" id="2c89485b-c647-4006-8e76-d4552e0de6c3" name="Default" comment="">
|
|
5
|
+
<change beforePath="" afterPath="$PROJECT_DIR$/.idea/misc.xml" />
|
|
6
|
+
<change beforePath="" afterPath="$PROJECT_DIR$/.idea/modules.xml" />
|
|
7
|
+
<change beforePath="" afterPath="$PROJECT_DIR$/.idea/testoscope.iml" />
|
|
8
|
+
<change beforePath="" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
|
|
9
|
+
<change beforePath="" afterPath="$PROJECT_DIR$/.travis.yml" />
|
|
10
|
+
<change beforePath="$PROJECT_DIR$/README.md" afterPath="$PROJECT_DIR$/README.md" />
|
|
11
|
+
</list>
|
|
12
|
+
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
|
13
|
+
<option name="TRACKING_ENABLED" value="true" />
|
|
14
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
15
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
16
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
17
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
18
|
+
</component>
|
|
19
|
+
<component name="FileEditorManager">
|
|
20
|
+
<leaf>
|
|
21
|
+
<file leaf-file-name="testoscope.gemspec" pinned="false" current-in-tab="false">
|
|
22
|
+
<entry file="file://$PROJECT_DIR$/testoscope.gemspec">
|
|
23
|
+
<provider selected="true" editor-type-id="text-editor">
|
|
24
|
+
<state relative-caret-position="403">
|
|
25
|
+
<caret line="39" column="42" lean-forward="false" selection-start-line="39" selection-start-column="42" selection-end-line="39" selection-end-column="42" />
|
|
26
|
+
<folding />
|
|
27
|
+
</state>
|
|
28
|
+
</provider>
|
|
29
|
+
</entry>
|
|
30
|
+
</file>
|
|
31
|
+
<file leaf-file-name="testoscope.rb" pinned="false" current-in-tab="false">
|
|
32
|
+
<entry file="file://$PROJECT_DIR$/lib/testoscope.rb">
|
|
33
|
+
<provider selected="true" editor-type-id="text-editor">
|
|
34
|
+
<state relative-caret-position="120">
|
|
35
|
+
<caret line="8" column="0" lean-forward="false" selection-start-line="8" selection-start-column="0" selection-end-line="13" selection-end-column="24" />
|
|
36
|
+
<folding />
|
|
37
|
+
</state>
|
|
38
|
+
</provider>
|
|
39
|
+
</entry>
|
|
40
|
+
</file>
|
|
41
|
+
<file leaf-file-name="README.md" pinned="false" current-in-tab="true">
|
|
42
|
+
<entry file="file://$PROJECT_DIR$/README.md">
|
|
43
|
+
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
|
|
44
|
+
<state split_layout="SPLIT">
|
|
45
|
+
<first_editor relative-caret-position="327">
|
|
46
|
+
<caret line="121" column="0" lean-forward="true" selection-start-line="121" selection-start-column="0" selection-end-line="121" selection-end-column="0" />
|
|
47
|
+
<folding />
|
|
48
|
+
</first_editor>
|
|
49
|
+
<second_editor />
|
|
50
|
+
</state>
|
|
51
|
+
</provider>
|
|
52
|
+
</entry>
|
|
53
|
+
</file>
|
|
54
|
+
<file leaf-file-name=".gitignore" pinned="false" current-in-tab="false">
|
|
55
|
+
<entry file="file://$PROJECT_DIR$/.gitignore">
|
|
56
|
+
<provider selected="true" editor-type-id="text-editor">
|
|
57
|
+
<state relative-caret-position="0">
|
|
58
|
+
<caret line="0" column="7" lean-forward="false" selection-start-line="0" selection-start-column="7" selection-end-line="0" selection-end-column="7" />
|
|
59
|
+
<folding />
|
|
60
|
+
</state>
|
|
61
|
+
</provider>
|
|
62
|
+
</entry>
|
|
63
|
+
</file>
|
|
64
|
+
</leaf>
|
|
65
|
+
</component>
|
|
66
|
+
<component name="Git.Settings">
|
|
67
|
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
68
|
+
</component>
|
|
69
|
+
<component name="IdeDocumentHistory">
|
|
70
|
+
<option name="CHANGED_PATHS">
|
|
71
|
+
<list>
|
|
72
|
+
<option value="$PROJECT_DIR$/lib/testoscope.rb" />
|
|
73
|
+
<option value="$PROJECT_DIR$/.gitignore" />
|
|
74
|
+
<option value="$PROJECT_DIR$/testoscope.gemspec" />
|
|
75
|
+
<option value="$PROJECT_DIR$/README.md" />
|
|
76
|
+
</list>
|
|
77
|
+
</option>
|
|
78
|
+
</component>
|
|
79
|
+
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
|
|
80
|
+
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
|
|
81
|
+
<component name="JsGulpfileManager">
|
|
82
|
+
<detection-done>true</detection-done>
|
|
83
|
+
<sorting>DEFINITION_ORDER</sorting>
|
|
84
|
+
</component>
|
|
85
|
+
<component name="ProjectFrameBounds" fullScreen="true">
|
|
86
|
+
<option name="width" value="1440" />
|
|
87
|
+
<option name="height" value="900" />
|
|
88
|
+
</component>
|
|
89
|
+
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
|
90
|
+
<component name="ProjectView">
|
|
91
|
+
<navigator currentView="ProjectPane" proportions="" version="1">
|
|
92
|
+
<flattenPackages />
|
|
93
|
+
<showMembers />
|
|
94
|
+
<showModules />
|
|
95
|
+
<showLibraryContents />
|
|
96
|
+
<hideEmptyPackages />
|
|
97
|
+
<abbreviatePackageNames />
|
|
98
|
+
<autoscrollToSource />
|
|
99
|
+
<autoscrollFromSource />
|
|
100
|
+
<sortByType />
|
|
101
|
+
<manualOrder />
|
|
102
|
+
<foldersAlwaysOnTop value="true" />
|
|
103
|
+
</navigator>
|
|
104
|
+
<panes>
|
|
105
|
+
<pane id="Scope" />
|
|
106
|
+
<pane id="Scratches" />
|
|
107
|
+
<pane id="ProjectPane">
|
|
108
|
+
<subPane>
|
|
109
|
+
<expand>
|
|
110
|
+
<path>
|
|
111
|
+
<item name="testoscope" type="b2602c69:ProjectViewProjectNode" />
|
|
112
|
+
<item name="testoscope" type="462c0819:PsiDirectoryNode" />
|
|
113
|
+
</path>
|
|
114
|
+
<path>
|
|
115
|
+
<item name="testoscope" type="b2602c69:ProjectViewProjectNode" />
|
|
116
|
+
<item name="testoscope" type="462c0819:PsiDirectoryNode" />
|
|
117
|
+
<item name="lib" type="462c0819:PsiDirectoryNode" />
|
|
118
|
+
</path>
|
|
119
|
+
</expand>
|
|
120
|
+
<select />
|
|
121
|
+
</subPane>
|
|
122
|
+
</pane>
|
|
123
|
+
</panes>
|
|
124
|
+
</component>
|
|
125
|
+
<component name="PropertiesComponent">
|
|
126
|
+
<property name="settings.editor.selected.configurable" value="org.jetbrains.plugins.ruby.settings.RubyActiveModuleSdkConfigurable" />
|
|
127
|
+
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
|
|
128
|
+
<property name="WebServerToolWindowFactoryState" value="false" />
|
|
129
|
+
</component>
|
|
130
|
+
<component name="RecentsManager">
|
|
131
|
+
<key name="CopyFile.RECENT_KEYS">
|
|
132
|
+
<recent name="$PROJECT_DIR$" />
|
|
133
|
+
</key>
|
|
134
|
+
</component>
|
|
135
|
+
<component name="RunDashboard">
|
|
136
|
+
<option name="ruleStates">
|
|
137
|
+
<list>
|
|
138
|
+
<RuleState>
|
|
139
|
+
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
|
|
140
|
+
</RuleState>
|
|
141
|
+
<RuleState>
|
|
142
|
+
<option name="name" value="StatusDashboardGroupingRule" />
|
|
143
|
+
</RuleState>
|
|
144
|
+
</list>
|
|
145
|
+
</option>
|
|
146
|
+
</component>
|
|
147
|
+
<component name="ShelveChangesManager" show_recycled="false">
|
|
148
|
+
<option name="remove_strategy" value="false" />
|
|
149
|
+
</component>
|
|
150
|
+
<component name="SpringUtil" SPRING_PRE_LOADER_OPTION="true" />
|
|
151
|
+
<component name="SvnConfiguration">
|
|
152
|
+
<configuration />
|
|
153
|
+
</component>
|
|
154
|
+
<component name="TaskManager">
|
|
155
|
+
<task active="true" id="Default" summary="Default task">
|
|
156
|
+
<changelist id="2c89485b-c647-4006-8e76-d4552e0de6c3" name="Default" comment="" />
|
|
157
|
+
<created>1521465490978</created>
|
|
158
|
+
<option name="number" value="Default" />
|
|
159
|
+
<option name="presentableId" value="Default" />
|
|
160
|
+
<updated>1521465490978</updated>
|
|
161
|
+
<workItem from="1521465492548" duration="13819000" />
|
|
162
|
+
</task>
|
|
163
|
+
<task id="LOCAL-00001" summary="initial commit">
|
|
164
|
+
<created>1521545104500</created>
|
|
165
|
+
<option name="number" value="00001" />
|
|
166
|
+
<option name="presentableId" value="LOCAL-00001" />
|
|
167
|
+
<option name="project" value="LOCAL" />
|
|
168
|
+
<updated>1521545104500</updated>
|
|
169
|
+
</task>
|
|
170
|
+
<task id="LOCAL-00002" summary="read me chngs">
|
|
171
|
+
<created>1521545270044</created>
|
|
172
|
+
<option name="number" value="00002" />
|
|
173
|
+
<option name="presentableId" value="LOCAL-00002" />
|
|
174
|
+
<option name="project" value="LOCAL" />
|
|
175
|
+
<updated>1521545270044</updated>
|
|
176
|
+
</task>
|
|
177
|
+
<task id="LOCAL-00003" summary="more bold">
|
|
178
|
+
<created>1521545367711</created>
|
|
179
|
+
<option name="number" value="00003" />
|
|
180
|
+
<option name="presentableId" value="LOCAL-00003" />
|
|
181
|
+
<option name="project" value="LOCAL" />
|
|
182
|
+
<updated>1521545367711</updated>
|
|
183
|
+
</task>
|
|
184
|
+
<option name="localTasksCounter" value="4" />
|
|
185
|
+
<servers />
|
|
186
|
+
</component>
|
|
187
|
+
<component name="TimeTrackingManager">
|
|
188
|
+
<option name="totallyTimeSpent" value="13819000" />
|
|
189
|
+
</component>
|
|
190
|
+
<component name="ToolWindowManager">
|
|
191
|
+
<frame x="0" y="0" width="1440" height="900" extended-state="0" />
|
|
192
|
+
<editor active="true" />
|
|
193
|
+
<layout>
|
|
194
|
+
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.14663805" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
|
|
195
|
+
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
|
|
196
|
+
<window_info id="Docker" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
|
197
|
+
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
|
|
198
|
+
<window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
|
199
|
+
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.32889965" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
|
|
200
|
+
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
|
201
|
+
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
|
202
|
+
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
|
203
|
+
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
|
|
204
|
+
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
|
|
205
|
+
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
|
|
206
|
+
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
|
|
207
|
+
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
|
|
208
|
+
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
|
|
209
|
+
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
|
210
|
+
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
|
|
211
|
+
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
|
212
|
+
</layout>
|
|
213
|
+
</component>
|
|
214
|
+
<component name="TypeScriptGeneratedFilesManager">
|
|
215
|
+
<option name="version" value="1" />
|
|
216
|
+
</component>
|
|
217
|
+
<component name="VcsContentAnnotationSettings">
|
|
218
|
+
<option name="myLimit" value="2678400000" />
|
|
219
|
+
</component>
|
|
220
|
+
<component name="VcsManagerConfiguration">
|
|
221
|
+
<MESSAGE value="initial commit" />
|
|
222
|
+
<MESSAGE value="read me chngs" />
|
|
223
|
+
<MESSAGE value="more bold" />
|
|
224
|
+
<option name="LAST_COMMIT_MESSAGE" value="more bold" />
|
|
225
|
+
</component>
|
|
226
|
+
<component name="XDebuggerManager">
|
|
227
|
+
<breakpoint-manager>
|
|
228
|
+
<option name="time" value="1" />
|
|
229
|
+
</breakpoint-manager>
|
|
230
|
+
<watches-manager />
|
|
231
|
+
</component>
|
|
232
|
+
<component name="editorHistoryManager">
|
|
233
|
+
<entry file="file://$PROJECT_DIR$/lib/testoscope.rb">
|
|
234
|
+
<provider selected="true" editor-type-id="text-editor">
|
|
235
|
+
<state relative-caret-position="120">
|
|
236
|
+
<caret line="8" column="0" lean-forward="false" selection-start-line="8" selection-start-column="0" selection-end-line="13" selection-end-column="24" />
|
|
237
|
+
<folding />
|
|
238
|
+
</state>
|
|
239
|
+
</provider>
|
|
240
|
+
</entry>
|
|
241
|
+
<entry file="file://$PROJECT_DIR$/.gitignore">
|
|
242
|
+
<provider selected="true" editor-type-id="text-editor">
|
|
243
|
+
<state relative-caret-position="0">
|
|
244
|
+
<caret line="0" column="7" lean-forward="false" selection-start-line="0" selection-start-column="7" selection-end-line="0" selection-end-column="7" />
|
|
245
|
+
<folding />
|
|
246
|
+
</state>
|
|
247
|
+
</provider>
|
|
248
|
+
</entry>
|
|
249
|
+
<entry file="file://$PROJECT_DIR$/testoscope.gemspec">
|
|
250
|
+
<provider selected="true" editor-type-id="text-editor">
|
|
251
|
+
<state relative-caret-position="403">
|
|
252
|
+
<caret line="39" column="42" lean-forward="false" selection-start-line="39" selection-start-column="42" selection-end-line="39" selection-end-column="42" />
|
|
253
|
+
<folding />
|
|
254
|
+
</state>
|
|
255
|
+
</provider>
|
|
256
|
+
</entry>
|
|
257
|
+
<entry file="file://$PROJECT_DIR$/README.md">
|
|
258
|
+
<provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
|
|
259
|
+
<state split_layout="SPLIT">
|
|
260
|
+
<first_editor relative-caret-position="327">
|
|
261
|
+
<caret line="121" column="0" lean-forward="true" selection-start-line="121" selection-start-column="0" selection-end-line="121" selection-end-column="0" />
|
|
262
|
+
<folding />
|
|
263
|
+
</first_editor>
|
|
264
|
+
<second_editor />
|
|
265
|
+
</state>
|
|
266
|
+
</provider>
|
|
267
|
+
</entry>
|
|
268
|
+
</component>
|
|
269
|
+
</project>
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 alekseyl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Testoscope
|
|
2
|
+
|
|
3
|
+
Inspect in practice how well is your data organized while testing your application!
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
**Finds out of the box:** sequential scans, dummy One-Timer calls to DB, unused indexes.
|
|
7
|
+
|
|
8
|
+
**Highly customizable:** you can define your own unintended markers, inspect only part of you tables set and so.
|
|
9
|
+
|
|
10
|
+
May work in a **error mode** raising exception on unintended behaviour,
|
|
11
|
+
that way you can protect from perfomance break-out in production.
|
|
12
|
+
|
|
13
|
+
Best suits with high-level testing: **controller tests, integration tests, api tests** and so.
|
|
14
|
+
|
|
15
|
+
Output example:
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
## Out of the box inspections
|
|
19
|
+
Sequential scans, dummy One-Timer calls to DB, unused indexes
|
|
20
|
+
|
|
21
|
+
### Sequential scans
|
|
22
|
+
It can happend when you are:
|
|
23
|
+
* truly missing an index
|
|
24
|
+
* when you are intend to use a partial index but unintentionally miss index condition in a query
|
|
25
|
+
|
|
26
|
+
### One-Timers
|
|
27
|
+
Some times ORM can produce dummy query, in Postgres Query Plan they look like this:
|
|
28
|
+
|
|
29
|
+
QUERY PLAN
|
|
30
|
+
--------------------------------------------
|
|
31
|
+
Result (cost=0.00..0.00 rows=0 width=194)
|
|
32
|
+
One-Time Filter: false
|
|
33
|
+
(2 rows)
|
|
34
|
+
|
|
35
|
+
in SQL query you are looking for some WHERE false:
|
|
36
|
+
|
|
37
|
+
SELECT "tags".*
|
|
38
|
+
FROM "tags"
|
|
39
|
+
WHERE "tags"."parent_id" = $1 AND 1=0
|
|
40
|
+
|
|
41
|
+
and in ORM it doesn't look alarming:
|
|
42
|
+
|
|
43
|
+
sub_tags.where( name: names )
|
|
44
|
+
|
|
45
|
+
So when names is empty, we get a dummy request.
|
|
46
|
+
|
|
47
|
+
They are not a big deal from a performance perspective,
|
|
48
|
+
but you are occupying DB connection pool and cluttering your channel with empty noise.
|
|
49
|
+
|
|
50
|
+
So it's better to change the underlying logic other than be OK with it.
|
|
51
|
+
|
|
52
|
+
### Unused index
|
|
53
|
+
|
|
54
|
+
Testoscope can find and warn you about unused index. Possible reasons
|
|
55
|
+
for them are:
|
|
56
|
+
* you forgot to remove index after code refactoring, and now you have redundant unused index
|
|
57
|
+
* you already have another index more suitable which is preferred by the planner
|
|
58
|
+
* your tests doesn't cover all use-cases
|
|
59
|
+
|
|
60
|
+
In either cases you may have a problem, but also may not.
|
|
61
|
+
|
|
62
|
+
### How it works?
|
|
63
|
+
Testoscope hooks to exec_query of a connection adapter,
|
|
64
|
+
for all queries runs them two times:
|
|
65
|
+
first with EXPLAIN and analyze it,
|
|
66
|
+
and the second - is for original a caller purpose.
|
|
67
|
+
|
|
68
|
+
After achieving explain result, it simply search for configured unintended behaviour markers,
|
|
69
|
+
like a Seq Scan substring in Postgres QUERY PLAN explained, and also collects indexes used by all queries for a final summary.
|
|
70
|
+
|
|
71
|
+
### Unintended Behaviours
|
|
72
|
+
By default unintended behaviours are preconfigured for PosgtreSQL and all tables:
|
|
73
|
+
|
|
74
|
+
config.unintened_key_words = ['Seq Scan', 'One-Time Filter']
|
|
75
|
+
config.tables = :all
|
|
76
|
+
|
|
77
|
+
But you can set any regexp you want to track inside EXPLAIN results,
|
|
78
|
+
and also you can track not all tables, but only specified ones:
|
|
79
|
+
|
|
80
|
+
config.tables = ['cards', 'users']
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
Add this line to your application's Gemfile:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
gem 'testoscope'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
And then execute:
|
|
92
|
+
|
|
93
|
+
$ bundle
|
|
94
|
+
|
|
95
|
+
Or install it yourself as:
|
|
96
|
+
|
|
97
|
+
$ gem install testoscope
|
|
98
|
+
|
|
99
|
+
## Usage
|
|
100
|
+
|
|
101
|
+
In test_helper.rb:
|
|
102
|
+
|
|
103
|
+
require 'testoscope'
|
|
104
|
+
|
|
105
|
+
#since doubling all requests to DB is not free,
|
|
106
|
+
#you may use ENV variable to run on demand
|
|
107
|
+
if ENV[:RUN_TESTOSCOPE]
|
|
108
|
+
Testoscope.configure { |c|
|
|
109
|
+
c.back_trace_paths = [Rails.root.to_s]
|
|
110
|
+
c.back_trace_exclude_paths = ["#{Rails.root.to_s}/test", "#{Rails.root.to_s}/spec"]
|
|
111
|
+
c.unintened_key_words = ['Seq Scan', 'One-Time Filter']
|
|
112
|
+
c.raise_when_unintended = false
|
|
113
|
+
c.analyze = true
|
|
114
|
+
c.tables = :all
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
MiniTest::Unit.after_tests {
|
|
118
|
+
Testoscope.print_results
|
|
119
|
+
}
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## Contributing
|
|
125
|
+
|
|
126
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/alekseyl/testoscope.
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "testoscope"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/testoscope.rb
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
require "testoscope/version"
|
|
2
|
+
|
|
3
|
+
module Testoscope
|
|
4
|
+
class Config
|
|
5
|
+
attr_accessor :analyze, :back_trace_paths, :back_trace_exclude_paths,
|
|
6
|
+
:unintened_key_words, :analyze, :tables, :raise_when_unintended
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
self.back_trace_paths = [Rails.root.to_s]
|
|
10
|
+
self.back_trace_exclude_paths = ["#{Rails.root.to_s}/test", "#{Rails.root.to_s}/spec"]
|
|
11
|
+
self.unintened_key_words = ['Seq Scan', 'One-Time Filter']
|
|
12
|
+
self.raise_when_unintended = false
|
|
13
|
+
self.analyze = true
|
|
14
|
+
self.tables = :all
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
def self.config; @@config ||= Config.new end
|
|
18
|
+
|
|
19
|
+
def self.configure
|
|
20
|
+
yield(config) if block_given?
|
|
21
|
+
|
|
22
|
+
::ActiveRecord::Base.connection.class.include(AdapterUpgrade)
|
|
23
|
+
|
|
24
|
+
# since test table is small planner may want to just deal with Seq scans and skip all the index fuss
|
|
25
|
+
::ActiveRecord::Base.connection.execute( 'SET enable_seqscan=off;' ) if ::ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
config.tables.map!{|table| /.*[ "]#{table}[ "].*/ } if config.tables != :all
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.results
|
|
32
|
+
@results ||= {
|
|
33
|
+
unintended_behaviour: {},
|
|
34
|
+
indexes: {},
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.add_unintended_behaviour( sql, explain, backtrace )
|
|
39
|
+
results[:unintended_behaviour][sql] ||= {}
|
|
40
|
+
results[:unintended_behaviour][sql][:explain] = explain
|
|
41
|
+
results[:unintended_behaviour][sql][:backtrace] ||= []
|
|
42
|
+
results[:unintended_behaviour][sql][:backtrace] << backtrace.to_a unless results[:unintended_behaviour][sql][:backtrace].include?(backtrace)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def self.add_index_used( sql, explain, index_used )
|
|
47
|
+
results[:indexes][index_used] ||= []
|
|
48
|
+
results[:indexes][index_used] << { explain: explain, sql: sql }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Alternative way to get index names "without" ActiveRecord
|
|
53
|
+
#"SELECT i.relname as indname FROM pg_index as idx JOIN pg_class as i ON i.oid = idx.indexrelid
|
|
54
|
+
# WHERE idx.indrelid::regclass = ANY( ARRAY['#{(ActiveRecord::Base.connection.tables).join("','")}']::regclass[] )")
|
|
55
|
+
# .to_a.map(&:values).flatten
|
|
56
|
+
def self.get_all_indexes
|
|
57
|
+
ActiveRecord::Base.connection.tables.map { |table|
|
|
58
|
+
[table, ActiveRecord::Base.connection.indexes(table).map(&:name)]
|
|
59
|
+
}.to_h
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.print_results
|
|
63
|
+
return yield(results) if block_given?
|
|
64
|
+
|
|
65
|
+
puts "\n<UNINTENDED BEHAVIOUR>\n" unless results[:unintended_behaviour].blank?
|
|
66
|
+
|
|
67
|
+
results[:unintended_behaviour].each do |sql, values|
|
|
68
|
+
puts "\nSQL:\n\n"
|
|
69
|
+
puts Niceql::Prettifier.prettify_sql( sql )
|
|
70
|
+
puts "\n\nAPP BACKTRACE:\n\n"
|
|
71
|
+
puts values[:backtrace].map{|arr| arr.join("\n")}.uniq.join("\n _____________________\n\n")
|
|
72
|
+
puts ''
|
|
73
|
+
puts values[:explain]
|
|
74
|
+
puts "\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
filtered_unused_indexes = []
|
|
78
|
+
|
|
79
|
+
get_all_indexes.each do |table, indexes|
|
|
80
|
+
unused_indexes = indexes - results[:indexes].keys
|
|
81
|
+
next if unused_indexes.blank? || !sql_has_analyzing_tables?(" #{table} ")
|
|
82
|
+
filtered_unused_indexes << "\n-----#{table}------\n"
|
|
83
|
+
filtered_unused_indexes << unused_indexes
|
|
84
|
+
end
|
|
85
|
+
if !filtered_unused_indexes.blank?
|
|
86
|
+
puts "\n<UNUSED INDEXES>\n"
|
|
87
|
+
puts filtered_unused_indexes.join("\n")
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.sql_has_analyzing_tables?(sql)
|
|
92
|
+
config.tables == :all || config.tables.any?{ |table| sql[table] }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def self.analyze( sql )
|
|
96
|
+
if config.analyze && sql_has_analyzing_tables?(sql)
|
|
97
|
+
|
|
98
|
+
explain = yield
|
|
99
|
+
|
|
100
|
+
app_trace = caller_locations( 2 ).map(&:to_s).select { |st|
|
|
101
|
+
self.config.back_trace_paths.any?{|pth| st[pth]} && !self.config.back_trace_exclude_paths.any?{|epth| st[epth]}
|
|
102
|
+
}
|
|
103
|
+
# this is the case when we for example making query from test files,
|
|
104
|
+
# we may omit some params for where clause and so.
|
|
105
|
+
return if app_trace.length == 0
|
|
106
|
+
|
|
107
|
+
unintended_found = self.config.unintened_key_words.select{|ukw| explain[ukw] }
|
|
108
|
+
|
|
109
|
+
if unintended_found.length > 0
|
|
110
|
+
raise StandardError.new("#{unintended_found.join(', ')} found!\n #{explain}") if config.raise_when_unintended
|
|
111
|
+
self.add_unintended_behaviour( sql, explain, app_trace )
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
explain.scan(/Index Scan using \w+/).each{|found| add_index_used(sql, explain, found[17..-1]) }
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.suspend_global_analyze( analyze )
|
|
119
|
+
was, config.analyze = config.analyze, analyze
|
|
120
|
+
yield
|
|
121
|
+
config.analyze = was
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
module AdapterUpgrade
|
|
125
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
|
126
|
+
PGAnalyzer.analyze(sql) {
|
|
127
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new
|
|
128
|
+
.pp(super( 'EXPLAIN ' + sql, "EXPLAIN", binds, prepare: false) )
|
|
129
|
+
}
|
|
130
|
+
super( sql, name, binds, prepare: prepare )
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
end
|
data/results.png
ADDED
|
Binary file
|
data/testoscope.gemspec
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
|
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require "testoscope/version"
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "testoscope"
|
|
8
|
+
spec.version = Testoscope::VERSION
|
|
9
|
+
spec.authors = ["alekseyl"]
|
|
10
|
+
spec.email = ["leshchuk@gmail.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{ This is simple and nice tool to inspect how application operates with current DB structure while testing app,
|
|
13
|
+
inspecting for redundant indexes, sequential scans, dummy requests and any other unintended behaviour customized by user. }
|
|
14
|
+
spec.description = %q{This is simple and nice tool to inspect how application operates with current DB structure while testing app,
|
|
15
|
+
meaning redundant indexes, sequential scans, dummy requests and any other unintended behaviour customized by user. }
|
|
16
|
+
spec.homepage = "https://github.com/alekseyl/testoscope"
|
|
17
|
+
spec.license = "MIT"
|
|
18
|
+
|
|
19
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
|
20
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
|
21
|
+
if spec.respond_to?(:metadata)
|
|
22
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
23
|
+
else
|
|
24
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
|
25
|
+
"public gem pushes."
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
29
|
+
f.match(%r{^(test|spec|features)/})
|
|
30
|
+
end
|
|
31
|
+
spec.bindir = "exe"
|
|
32
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
33
|
+
spec.require_paths = ["lib"]
|
|
34
|
+
|
|
35
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
|
36
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
37
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
|
38
|
+
|
|
39
|
+
spec.add_dependency "activerecord", ">= 4"
|
|
40
|
+
spec.add_dependency "niceql", ">= 0.1.14"
|
|
41
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: testoscope
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- alekseyl
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-03-20 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.16'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.16'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '10.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '10.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: minitest
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '5.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '5.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: activerecord
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '4'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '4'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: niceql
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 0.1.14
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 0.1.14
|
|
83
|
+
description: "This is simple and nice tool to inspect how application operates with
|
|
84
|
+
current DB structure while testing app,\n meaning redundant
|
|
85
|
+
indexes, sequential scans, dummy requests and any other unintended behaviour customized
|
|
86
|
+
by user. "
|
|
87
|
+
email:
|
|
88
|
+
- leshchuk@gmail.com
|
|
89
|
+
executables: []
|
|
90
|
+
extensions: []
|
|
91
|
+
extra_rdoc_files: []
|
|
92
|
+
files:
|
|
93
|
+
- ".gitignore"
|
|
94
|
+
- ".idea/misc.xml"
|
|
95
|
+
- ".idea/modules.xml"
|
|
96
|
+
- ".idea/testoscope.iml"
|
|
97
|
+
- ".idea/workspace.xml"
|
|
98
|
+
- ".travis.yml"
|
|
99
|
+
- Gemfile
|
|
100
|
+
- LICENSE.txt
|
|
101
|
+
- README.md
|
|
102
|
+
- Rakefile
|
|
103
|
+
- bin/console
|
|
104
|
+
- bin/setup
|
|
105
|
+
- lib/testoscope.rb
|
|
106
|
+
- lib/testoscope/version.rb
|
|
107
|
+
- results.png
|
|
108
|
+
- testoscope.gemspec
|
|
109
|
+
homepage: https://github.com/alekseyl/testoscope
|
|
110
|
+
licenses:
|
|
111
|
+
- MIT
|
|
112
|
+
metadata:
|
|
113
|
+
allowed_push_host: https://rubygems.org
|
|
114
|
+
post_install_message:
|
|
115
|
+
rdoc_options: []
|
|
116
|
+
require_paths:
|
|
117
|
+
- lib
|
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - ">="
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '0'
|
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
|
+
requirements:
|
|
125
|
+
- - ">="
|
|
126
|
+
- !ruby/object:Gem::Version
|
|
127
|
+
version: '0'
|
|
128
|
+
requirements: []
|
|
129
|
+
rubyforge_project:
|
|
130
|
+
rubygems_version: 2.6.13
|
|
131
|
+
signing_key:
|
|
132
|
+
specification_version: 4
|
|
133
|
+
summary: This is simple and nice tool to inspect how application operates with current
|
|
134
|
+
DB structure while testing app, inspecting for redundant indexes, sequential scans,
|
|
135
|
+
dummy requests and any other unintended behaviour customized by user.
|
|
136
|
+
test_files: []
|