dm-cutie-extras 0.0.3

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.
File without changes
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2009, Cory ODaniel
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a
4
+ copy of this software and associated documentation files (the "Software"),
5
+ to deal in the Software without restriction, including without limitation
6
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the
8
+ Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,62 @@
1
+ Description
2
+ =============
3
+ dm-cutie-extras is a pack of extra stuff for dm-cutie and dm-cutie-ui.
4
+
5
+
6
+ Usage
7
+ ============
8
+ require 'dm-core'
9
+ require 'dm-cutie'
10
+ require 'dm-cutie-extras'
11
+
12
+ DataMapper::Cutie::Extras.load :mysql_query_plan
13
+
14
+ DataMapper::Cutie.start(true)
15
+
16
+
17
+ Creating Views
18
+ ==============
19
+ If your hook includes a DataMapper model and you are writing a view, the view name should
20
+ be the plural of your model name
21
+
22
+
23
+ Writing Tracker Hooks
24
+ =====================
25
+ See documentation @ http://github.com/coryodaniel/dm-cutie/
26
+
27
+
28
+ Adding Custom Columns to Views
29
+ ==============================
30
+ The partial 'grid' takes three keys to create its local variables: :columns, :records, :adhoc_columns (discussed next)
31
+
32
+ Columns should be respond to #each, it will generally be a DataMapper PropertySet. Each element in columns will be used in a #send method call on each of the elements in records. That means instead of doing the standard
33
+ partial :grid, :locals => { :columns => MyModel.properties, :records => @my_collection}
34
+
35
+ You can add any other columns you want as long as there is a method that a 'record' will respond to. For example, every object has a 'class' method, so you could do
36
+ partial :grid,
37
+ :locals => {
38
+ :columns => MyModel.properties.to_a + [:class],
39
+ :records => @my_collection
40
+ }
41
+
42
+ This would cause the class name to be output in a column (pretty useless), but you can add any method you want to the class and simply add its name to the set of elements being passed to :columns
43
+
44
+
45
+ As an alternative method you can pass a column name to a proc in the :adhoc_columns parameter
46
+ partial :grid,
47
+ :locals => {
48
+ :columns => MyModel.properties,
49
+ :records => @my_collection,
50
+ :adhoc_columns => {
51
+ :my_cool_class_name => lambda{|my_record| my_record.class }
52
+ }
53
+ }
54
+
55
+ This would result in a column named 'my_cool_class_name' with a value of 'MyModel'. Obviously you can be more creative :)
56
+
57
+ Note: the proc should accept one parameter, the specific record from the collection of items passed to :records
58
+
59
+ IMPORTANT NOTE:
60
+ Remember, if you are adding additional columns to the grid, make sure you pass the column names to the 'menu' partial if you want the ability to hide that column.
61
+
62
+
@@ -0,0 +1,94 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+ require "extlib"
6
+
7
+ require 'rake'
8
+ require 'rake/clean'
9
+ require 'spec/rake/spectask'
10
+ require "spec"
11
+ require 'rake/rdoctask'
12
+ require 'rake/gempackagetask'
13
+
14
+ ROOT = Pathname(__FILE__).dirname.expand_path
15
+ require ROOT + 'lib/dm-cutie-extras/version'
16
+
17
+ @spec = Gem::Specification.new do |s|
18
+ s.name = %q{dm-cutie-extras}
19
+ s.version = DataMapper::Cutie::Extras::VERSION
20
+
21
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
22
+ s.authors = ["Cory ODaniel"]
23
+ s.date = %q{2009-10-15}
24
+ s.summary = %q{DM Cutie's Extras}
25
+ s.description = %q{Cool additional hooks for DM Cutie.}
26
+
27
+ s.email = %q{cutie@coryodaniel.com}
28
+
29
+ s.extra_rdoc_files = ["README.markdown", "LICENSE", "History.txt", "TODO.markdown"]
30
+ s.files = ["LICENSE", "README.markdown", "Rakefile", "TODO.markdown",
31
+ "lib/dm-cutie-extras.rb",
32
+ "lib/dm-cutie-extras/version.rb",
33
+ ]
34
+ s.files += Dir[ "lib/dm-cutie-extras/views/*"]
35
+ s.files += Dir[ "lib/dm-cutie-extras/hooks/*"]
36
+
37
+ s.add_dependency "extlib",">=0.9.12"
38
+ s.add_dependency "dm-core", '>=0.10.0'
39
+ s.add_dependency "dm-types", '>=0.10.0'
40
+
41
+ s.has_rdoc = true
42
+ s.homepage = "http://github.com/coryodaniel/dm-cutie-extras"
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.2.0}
45
+ end
46
+
47
+ [ ROOT, ROOT.parent ].each do |dir|
48
+ Pathname.glob(dir.join('tasks/**/*.rb').to_s).each { |f| require f }
49
+ end
50
+
51
+ NAME = @spec.name
52
+ GEM_VERSION = DataMapper::Cutie::Extras::VERSION
53
+
54
+ Rake::GemPackageTask.new(@spec) do |pkg|
55
+ pkg.gem_spec = @spec
56
+ end
57
+
58
+ desc "install the plugin locally"
59
+ task :install => [:package] do
60
+ sh %{sudo gem install #{install_home} pkg/#{NAME}-#{GEM_VERSION} --no-update-sources}
61
+ end
62
+
63
+ desc "create a gemspec file"
64
+ task :make_spec do
65
+ File.open("#{NAME}.gemspec", "w") do |file|
66
+ file.puts @spec.to_ruby
67
+ end
68
+ end
69
+
70
+ Rake::RDocTask.new do |rdoc|
71
+ files = ["README.markdown", "History.txt", "LICENSE", "lib/**/*.rb"]
72
+ rdoc.rdoc_files.add(files)
73
+ rdoc.main = "README.markdown"
74
+ rdoc.title = "DM Cutie Extras"
75
+
76
+ rdoc.rdoc_dir = "doc/rdoc"
77
+ rdoc.options << "--line-numbers" << "--inline-source"
78
+ end
79
+
80
+ Spec::Rake::SpecTask.new do |t|
81
+ t.spec_files = Dir["./spec/**/*_spec.rb"]
82
+ t.spec_files.unshift './spec/spec_helper.rb'
83
+
84
+ t.libs = ['lib']
85
+ t.spec_opts << "--color" << "--format" << "specdoc" #"progress"
86
+
87
+ if ENV['RCOV']
88
+ t.rcov = true
89
+ t.rcov_opts << '--exclude' << 'pkg,spec,interactive.rb,install_test_suite.rb,lib/gems,' + Gem.path.join(',')
90
+ t.rcov_opts << '--text-summary'
91
+ t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
92
+ t.rcov_opts << '--only-uncovered'
93
+ end
94
+ end
@@ -0,0 +1,60 @@
1
+ MysqlIndex
2
+ =============
3
+ * Percent of times used (Need a way to set dependencies between hooks)
4
+ * mysql_index count times used queryplan.keys.split(',').member? index.key_name
5
+ * mysql_index count times used queryplan.possible_keys_.split(',').member? index.key_name
6
+
7
+ * link MysqlIndex to executed OR generalized query ?
8
+ * Offer DROP INDEX on mysql_index
9
+ * redundant index detection http://www.paragon-cs.com/presentations/querytuning.pdf
10
+
11
+
12
+ MysqlOptimizations (Probably highly correlated to MysqlExecutionStep...)
13
+ ===================
14
+ * Offer ADD INDEX on generalized_query (Optimize Queries like this)
15
+
16
+
17
+ Amazon SimpleDB
18
+ ===============
19
+ * Cost calculation
20
+
21
+
22
+ MysqlExecutionStep
23
+ ==================
24
+ * full table scans causes:
25
+ * no WHERE clause
26
+ * no index on any field in WHERE clause
27
+ * poor selectivity on an indexed field
28
+ * too many records meet WHERE conditions
29
+ * MySQL version less than 5.0 and using OR in a WHERE clause
30
+ * using SELECT * FROM
31
+ * select_type optimizers
32
+ * if MysqlExecutionStep.select_type =~ /DEPENDENT/i # Correlated subquery, consider rewriting as join
33
+ * type optimizers
34
+ * extra optimizers
35
+ * distinct
36
+ * full scan on NULL key
37
+ * Impossible WHERE noticed after reading const table
38
+ * No tables
39
+ * Not exists
40
+ * Range checked for each record (index map: N)
41
+ * Select tables optimized away
42
+ * Using filesort
43
+ * Using index
44
+ * Using index for group-by
45
+ * Using sort_union(...), Using union(...), Using intersect(...)
46
+ * Using temporary
47
+ * Using where
48
+ * Using where with pushed condition
49
+ * covering index useful
50
+ * When you have large tables
51
+ * When you have long rows (BLOBS for example)
52
+ * When extra columns do not increase key length significantly
53
+ * When you have a large join with a number of secondary table lookups
54
+ * When a lot of rows match the same key value
55
+
56
+ Analyze joins
57
+ =============
58
+ * Repository Storage 'strongest' relationships (storage that a given storage is most frequently joined with)
59
+ * Repository Storage 'weakest' relationship
60
+ * Repository Storage w/ no relationship
@@ -0,0 +1,28 @@
1
+ module DataMapper
2
+ module Cutie
3
+ class Extras
4
+ class << self
5
+
6
+ def view_path
7
+ @_view_paths ||={}
8
+ end
9
+
10
+ def root
11
+ @_root_path ||= File.expand_path(File.dirname(__FILE__)) / 'dm-cutie-extras'
12
+ end
13
+
14
+ def load(*_hooks)
15
+ _hooks.each do |_hook|
16
+ require DataMapper::Cutie::Extras.root / 'hooks' / _hook
17
+ hook_view_path = DataMapper::Cutie::Extras.root / 'views' / "#{_hook.to_s.pluralize}.erb"
18
+
19
+ if File.exist? hook_view_path
20
+ DataMapper::Cutie::Extras.view_path[_hook] = hook_view_path
21
+ end
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,63 @@
1
+ # Tracker Hook for recording mysql query plans
2
+ #
3
+ class MysqlExecutionStep
4
+ include DataMapper::Cutie::Tracker::Hook::Abstract
5
+ include DataMapper::Resource
6
+
7
+ STATEMENT = "EXPLAIN EXTENDED %s"
8
+
9
+ belongs_to :executed_query
10
+ property :id, Serial
11
+
12
+ property :order, Integer, :nullable => false #the order the step was executed in
13
+ property :type, String
14
+ property :select_type, String
15
+ property :table, String #denormalized, duplicate, you can get this through self.generalized_query.query_storage_links.first(:primary_storage => true).repository_storage.table_name
16
+ property :possible_keys,String
17
+ property :used_key, String # maps to: OpenStruct#key
18
+ property :used_key_len, String # maps to: OpenStruct#key_len
19
+ property :ref, String
20
+ property :rows, Integer
21
+ property :filtered, Float
22
+ property :extra, String
23
+
24
+ def self.hook_name; "MySQL Execution Steps" end;
25
+ def self.supported_statements; [:select]; end;
26
+ def self.supported_adapters; [:mysql]; end;
27
+
28
+ def self.execute(target_repo, query_str)
29
+ repository(target_repo.to_sym).adapter.query MysqlExecutionStep::STATEMENT % query_str
30
+ end
31
+
32
+ def self.track( executed_query )
33
+ # Perform the EXPLAIN against the original target repo
34
+ raw_query_plan = MysqlExecutionStep.execute(
35
+ executed_query.generalized_query.primary_storage.repo_name,
36
+ executed_query.statement
37
+ )
38
+
39
+ #Store the MysqlExecutionSteps in the DM Cutie repo
40
+ DataMapper::Cutie.repo do
41
+ raw_query_plan.each_with_index do |raw_query_step, idx|
42
+ attribs = raw_query_step.attributes
43
+ attribs.delete(:id) #get rid of the rowID from MySQL
44
+ attribs[:used_key] = attribs.delete(:key)
45
+ attribs[:used_key_len] = attribs.delete(:key_len)
46
+ query_step = MysqlExecutionStep.new(attribs)
47
+ query_step.order = idx + 1
48
+ query_step.executed_query = executed_query
49
+
50
+ query_step.save
51
+ end
52
+ end
53
+ end #end track
54
+ end
55
+
56
+ # Let executed query know it just got some mysql_execution_steps
57
+ ExecutedQuery.send :has, (1/0.to_f), :mysql_execution_steps
58
+
59
+ # Let Cutie know she needs to treat this as an internal model
60
+ DataMapper::Cutie.add_internal_model :mysql_execution_step
61
+
62
+ # Let cutie know she has a new hook
63
+ DataMapper::Cutie.add_tracker_hook :mysql_execution_step
@@ -0,0 +1,132 @@
1
+ # This isn't a Tracker::Hook, its actually and After Create hook for RepositoryStorage and ExecutedQuery
2
+ #
3
+ # In calculating the selectivity, since the number of records can change, I do the average selectivity
4
+ # and the final selectivity (on stopping Dm Cutie)
5
+ #
6
+ class MysqlIndex
7
+ include DataMapper::Resource
8
+
9
+ STATEMENT = "SHOW INDEX FROM %s"
10
+
11
+ belongs_to :repository_storage
12
+ property :id, Serial
13
+ property :non_unique, Boolean
14
+ property :key_name, String
15
+ property :sequence_in_index, Integer # maps to: OpenStruct#Seq_in_index
16
+ property :table, String #denormalized, accessible through repository_storage
17
+ property :column_name, String
18
+ property :collation, String
19
+ property :cardinality, Integer
20
+ property :sub_part, Integer
21
+ property :packed, String
22
+ property :null, String
23
+ property :index_type, String
24
+
25
+ ######### SELECTIVITY #########
26
+ # The distinctness of the values in the column
27
+ property :distinct_count, Integer, :default => 0, :nullable => false
28
+
29
+ # denormalized, this is the count of 'executed_queries' that are related to this index
30
+ # @note there is no direction relationship (belongs_to/has) to executed_query in this model
31
+ property :execution_count, Integer, :default => 0, :nullable => false
32
+
33
+ # methods for selectivity
34
+ # selectivity is calculated by count(*) / count(distinct(COLUMN_NAME)) the closer to 1.0
35
+ # the better the index
36
+ def selectivity
37
+ ( distinct_count.to_f / self.repository_storage.record_count.to_f )
38
+ end
39
+
40
+ # Shortcut method to R:W of repo
41
+ def table_read_to_write_ratio
42
+ self.repository_storage.read_to_write_ratio
43
+ end
44
+
45
+ # TODO Row length could be a useful addition to calculating this
46
+ # TODO Potential Uses vs Actual Uses could be a useful addition to calculating this
47
+ #
48
+ def usefulness
49
+ # Number of records, read to write ratio, selectivity
50
+ num_records = self.repository_storage.record_count
51
+ num_reads = self.repository_storage.total_selects
52
+ num_writes = self.repository_storage.total_inserts +
53
+ self.repository_storage.total_deletes +
54
+ self.repository_storage.total_updates
55
+
56
+ records_processed = num_records * execution_count
57
+ total_operations = (num_reads + num_writes).to_f
58
+ read_percentage = (num_reads / total_operations)
59
+ # write_percentage = (num_writes / total_operations)
60
+ return(
61
+ (total_operations * read_percentage * selectivity) / total_operations
62
+ )
63
+ end
64
+
65
+ # detect duplicate indexes
66
+ def duplicate
67
+ MysqlIndex.all(
68
+ :id.not => self.id,
69
+ :table => self.table,
70
+ :index_type => self.index_type,
71
+ :column_name => self.column_name
72
+ ).count != 0
73
+ end
74
+
75
+ ######### CLASS METHODS #########
76
+ def self.execute( target_repo, storage_name )
77
+ repository(target_repo.to_sym).adapter.query MysqlIndex::STATEMENT % storage_name
78
+ end
79
+
80
+ # Track the selectivity of an index
81
+ def self.process_executed_query( executed_query )
82
+ executing_repo = executed_query.generalized_query.primary_storage
83
+
84
+ MysqlIndex.all(MysqlIndex.repository_storage.id => executing_repo.id).each do |mysql_index|
85
+ # Get to the repo BEING tracked for distinct count
86
+ distinct_column_count = DataMapper.repository(executing_repo.repo_name.to_sym) do |exec_repo|
87
+ exec_repo.adapter.query(
88
+ "SELECT COUNT(DISTINCT(`#{mysql_index.column_name}`)) FROM `#{mysql_index.table}`"
89
+ ).first
90
+ end
91
+
92
+ DataMapper::Transaction.new.link do
93
+ mysql_index.distinct_count = distinct_column_count
94
+ mysql_index.execution_count += 1
95
+ mysql_index.save
96
+ end
97
+ end
98
+ end
99
+
100
+
101
+
102
+ # Track the indexes on a Repository Storage
103
+ def self.process_repository_storage( repository_storage )
104
+ # Perform the SHOW index on the repos storage
105
+ raw_index_list = MysqlIndex.execute( repository_storage.repo_name, repository_storage.storage_name )
106
+
107
+ #Store the MysqlIndex in the DM Cutie repo
108
+ DataMapper::Cutie.repo do
109
+ raw_index_list.each_with_index do |shown_index, idx|
110
+ attribs = shown_index.attributes
111
+ attribs.delete(:comment)
112
+ attribs[:sequence_in_index] = attribs.delete(:seq_in_index)
113
+ mysql_index = MysqlIndex.new(attribs)
114
+ mysql_index.repository_storage = repository_storage
115
+
116
+ mysql_index.save
117
+ end
118
+ end
119
+ end #end process_repository_storage
120
+
121
+ end
122
+
123
+ # Let executed query know it just got some mysql_execution_steps
124
+ RepositoryStorage.send :has, (1/0.to_f), :mysql_indexes
125
+
126
+ # Let Cutie know she needs to treat this as an internal model
127
+ DataMapper::Cutie.add_internal_model :mysql_index
128
+
129
+ # Add this to the after(:create) for RepositoryStorage
130
+ RepositoryStorage.send(:after,:create){ MysqlIndex.process_repository_storage self }
131
+
132
+ ExecutedQuery.send(:after, :create){ MysqlIndex.process_executed_query self }
@@ -0,0 +1,48 @@
1
+ # Tracker Hook for recording mysql warnings
2
+ #
3
+ class MysqlWarning
4
+ include DataMapper::Cutie::Tracker::Hook::Abstract
5
+ include DataMapper::Resource
6
+
7
+ STATEMENT = "SHOW WARNINGS"
8
+
9
+ belongs_to :executed_query
10
+ property :id, Serial
11
+
12
+ property :level, String
13
+ property :code, Integer
14
+ property :message, Text
15
+
16
+ def self.hook_name; "MySQL Warnings" end;
17
+ def self.supported_statements; [:select, :insert, :update, :delete]; end;
18
+ def self.supported_adapters; [:mysql]; end;
19
+
20
+ def self.track( executed_query )
21
+ # Perform the show warings
22
+ raw_warnings = repository(
23
+ executed_query.generalized_query.primary_storage.repo_name.to_sym
24
+ ).adapter.query MysqlWarning::STATEMENT % executed_query.statement
25
+
26
+ #Store the MysqlWarning in the DM Cutie repo
27
+ DataMapper::Cutie.repo do
28
+ raw_warnings.each_with_index do |raw_warning, idx|
29
+ next if raw_warning.attributes[:level] =~ /^note$/i
30
+ attribs = raw_warning.attributes
31
+ mysql_warning = MysqlWarning.new(attribs)
32
+ mysql_warning.executed_query = executed_query
33
+ mysql_warning.save
34
+ end
35
+ end
36
+ end #end track
37
+
38
+
39
+ end
40
+
41
+ # Let executed query know it just got some mysql_warnings
42
+ ExecutedQuery.send :has, (1/0.to_f), :mysql_warnings
43
+
44
+ # Let Cutie know she needs to treat this as an internal model
45
+ DataMapper::Cutie.add_internal_model :mysql_warning
46
+
47
+ # Let cutie know she has a new hook
48
+ DataMapper::Cutie.add_tracker_hook :mysql_warning
@@ -0,0 +1,55 @@
1
+ # Tracker Hook for recording sqlite3 query plans
2
+ #
3
+ class Sqlite3ExecutionStep
4
+ include DataMapper::Cutie::Tracker::Hook::Abstract
5
+ include DataMapper::Resource
6
+
7
+ STATEMENT = "EXPLAIN QUERY PLAN %s"
8
+
9
+ belongs_to :executed_query
10
+ property :id, Serial
11
+
12
+ property :order, Integer, :nullable => false #the order the step was executed in
13
+ property :from, Integer, :nullable => false #the table the step was performed against
14
+ property :detail, Text # "TABLE people WITH INDEX index_people_person_name_idx"
15
+
16
+ def self.hook_name
17
+ "Sqlite3 Execution Steps"
18
+ end
19
+ def self.supported_statements
20
+ [:select, :insert, :update, :delete]
21
+ end
22
+ def self.supported_adapters
23
+ [:sqlite3]
24
+ end
25
+
26
+ def self.track( executed_query )
27
+ # Perform the EXPLAIN against the original target repo
28
+ raw_query_plan = repository(
29
+ executed_query.generalized_query.primary_storage.repo_name.to_sym
30
+ ).adapter.query Sqlite3ExecutionStep::STATEMENT % executed_query.statement
31
+
32
+ #Store the Sqlite3ExecutionStep in the DM Cutie repo
33
+ DataMapper::Cutie.repo do
34
+ raw_query_plan.each_with_index do |raw_query_step, idx|
35
+ attribs = raw_query_step.attributes
36
+ query_step = Sqlite3ExecutionStep.new(attribs)
37
+ query_step.order = attribs[:order]
38
+ query_step.from = attribs[:from]
39
+ query_step.detail = attribs[:detail]
40
+ query_step.executed_query = executed_query
41
+ query_step.save
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ # Tell executed query it has sqlite3 execution steps
49
+ ExecutedQuery.send :has, (1/0.to_f), :sqlite3_execution_steps
50
+
51
+ # Let Cutie know she needs to treat this as an internal model
52
+ DataMapper::Cutie.add_internal_model :sqlite3_execution_step
53
+
54
+ # Let cutie know she has a new hook
55
+ DataMapper::Cutie.add_tracker_hook :sqlite3_execution_step
@@ -0,0 +1,7 @@
1
+ module DataMapper
2
+ module Cutie
3
+ module Extras
4
+ VERSION = '0.0.3'.freeze
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,47 @@
1
+ <%
2
+ # This should be an array of DM Properties or symbols, essentially things
3
+ # that can be 'sent' to each of your records
4
+ # @my_record.send(_element_in_columns_array_)
5
+
6
+ @columns = YOUR_MODEL.properties
7
+ %>
8
+
9
+ <%= partial :menu, :locals => {:columns => @columns} %>
10
+
11
+ <%
12
+ query = {}
13
+
14
+ # @example: ExecutedQuery.generalized_query.query_storage_links.repository_storage
15
+ # @note: Yours will likeley by YOUR_MODEL.executed_query.generalized_query.query_storage_links.repository_storage
16
+ #
17
+ # The point in this is that DM CutieUI allows you to filter result sets by Repository, Adapter and Model
18
+ # This builds the join statement for narrowing that down. You just have to set up how to find the relationshps
19
+ # between this model and the RepositoryStorage model
20
+ relationship_path_to_repo_storage_model = YOUR_MODEL.relationship.path.to.repository_storage
21
+
22
+ unless ram_filters[:repository].blank?
23
+ query[relationship_path_to_repo_storage_model.repo_name]= ram_filters[:repository]
24
+ end
25
+
26
+ unless ram_filters[:adapter].blank?
27
+ query[relationship_path_to_repo_storage_model.adapter_name]= ram_filters[:adapter]
28
+ end
29
+
30
+ unless ram_filters[:model].blank?
31
+ query[relationship_path_to_repo_storage_model.model_name]= ram_filters[:model]
32
+ end
33
+
34
+ # Additionally parameters may come in with specific values to filter from the result set
35
+ # query = parse_params(YOUR_MODEL).merge(query)
36
+
37
+ @records = YOUR_MODEL.all(query)
38
+ %>
39
+
40
+ <%= partial :grid,
41
+ :locals => {
42
+ :columns => @columns,
43
+ :records => @records,
44
+ :adhoc_columns => {},
45
+ :relationships => YOUR_MODEL.relationships
46
+ }
47
+ %>
@@ -0,0 +1,71 @@
1
+ <% help_link %>
2
+ <% @columns = MysqlExecutionStep.properties %>
3
+ <%= partial :menu, :locals => {:columns => @columns } %>
4
+
5
+ <%
6
+ query = {}
7
+ path_to_repo_storage = MysqlExecutionStep.executed_query.generalized_query.query_storage_links.repository_storage
8
+ unless ram_filters[:repository].blank?
9
+ query[path_to_repo_storage.repo_name]= ram_filters[:repository]
10
+ end
11
+
12
+ unless ram_filters[:adapter].blank?
13
+ query[path_to_repo_storage.adapter_name]= ram_filters[:adapter]
14
+ end
15
+
16
+ unless ram_filters[:model].blank?
17
+ query[path_to_repo_storage.model_name]= ram_filters[:model]
18
+ end
19
+
20
+ query = parse_params(MysqlExecutionStep).merge(query)
21
+ @records = MysqlExecutionStep.all(query)
22
+ %>
23
+
24
+ <%= partial :grid,
25
+ :locals => {
26
+ :columns => @columns,
27
+ :records => @records,
28
+ :relationships => MysqlExecutionStep.relationships
29
+ }
30
+ %>
31
+
32
+ <a name="help">
33
+ <div class="help_container">
34
+ <p>
35
+ <a href="http://dev.mysql.com/doc/refman/5.0/en/using-explain.html">
36
+ Details on MySQL's explain statement can be read up on here.
37
+ </a>
38
+ </p>
39
+ <p>
40
+ Below is a list of column explanations
41
+ <ul>
42
+ <li>select_type - type of SELECT</li>
43
+ <li>table - the name of the table or alias</li>
44
+ <li>type - the type of join used for the query</li>
45
+ <li>possible_keys - indexes MySQL could possible use</li>
46
+ <li>used_key - indexes MySQL actually used</li>
47
+ <li>used_key_len - the total length of keys used</li>
48
+ <li>ref - any columns used with key to retrieve results</li>
49
+ <li>rows - estimated number of rows returned</li>
50
+ <li>extra - additional details</li>
51
+ </ul>
52
+
53
+ Below is a list of the MySQL Execution Plan step 'types'. They are listed in order from best to worst.
54
+ <ul>
55
+ <li>system</li>
56
+ <li>const</li>
57
+ <li>eq_ref</li>
58
+ <li>ref</li>
59
+ <li>fulltext</li>
60
+ <li>ref_or_null</li>
61
+ <li>index_merge</li>
62
+ <li>unique_subquery</li>
63
+ <li>index_subquery</li>
64
+ <li>range</li>
65
+ <li>index</li>
66
+ <li>all</li>
67
+ </ul>
68
+ </p>
69
+
70
+ </div>
71
+ </a>
@@ -0,0 +1,45 @@
1
+ <% @columns = MysqlIndex.properties.to_a + [:usefulness, :selectivity, :table_read_to_write_ratio, :usefulness, :duplicate] %>
2
+
3
+ <%= partial :menu, :locals => {:columns => @columns} %>
4
+
5
+ <%
6
+ query = {}
7
+
8
+ unless ram_filters[:repository].blank?
9
+ query[MysqlIndex.repository_storage.repo_name]= ram_filters[:repository]
10
+ end
11
+
12
+ unless ram_filters[:adapter].blank?
13
+ query[MysqlIndex.repository_storage.adapter_name]= ram_filters[:adapter]
14
+ end
15
+
16
+ unless ram_filters[:model].blank?
17
+ query[MysqlIndex.repository_storage.model_name]= ram_filters[:model]
18
+ end
19
+ query = parse_params(MysqlIndex).merge(query)
20
+ @records = MysqlIndex.all(query)
21
+ %>
22
+
23
+ <%= partial :grid,
24
+ :locals => {
25
+ :columns => @columns,
26
+ :records => @records,
27
+ :adhoc_columns => {},
28
+ :relationships => MysqlIndex.relationships
29
+ }
30
+ %>
31
+
32
+ <a name="help">
33
+ <div class="help_container">
34
+ <p>
35
+ <em>Selectivity</em> measures the selectiveness of an index. The closer to '1' the better, the closer it is to '0' the MySQL optimizer will probably not use it.
36
+ Unused indexes should be dropped because they still slow down write performance.
37
+ </p>
38
+ <p>
39
+ <em>Duplicate</em> determines whether a duplicate (same index_type) index is on the table/column. Duplicate indexes require <em>n</em> times the index management, and should be avoided
40
+ </p>
41
+ <p>
42
+ <em>Usefulness</em> is just a weight to compare indexes on the same table to determine how useful an index may be. This calculation will change over time as this mysql_index plugin gets more refined. From 0 to 1, the closer to 1 the better.
43
+ </p>
44
+ </div>
45
+ </a>
@@ -0,0 +1,35 @@
1
+ <%
2
+ @columns = MysqlWarning.properties
3
+ %>
4
+
5
+ <%= partial :menu, :locals => {:columns => @columns} %>
6
+
7
+ <%
8
+ query = {}
9
+
10
+ relationship_path_to_repo_storage_model = MysqlWarning.executed_query.generalized_query.query_storage_links.repository_storage
11
+
12
+ unless ram_filters[:repository].blank?
13
+ query[relationship_path_to_repo_storage_model.repo_name]= ram_filters[:repository]
14
+ end
15
+
16
+ unless ram_filters[:adapter].blank?
17
+ query[relationship_path_to_repo_storage_model.adapter_name]= ram_filters[:adapter]
18
+ end
19
+
20
+ unless ram_filters[:model].blank?
21
+ query[relationship_path_to_repo_storage_model.model_name]= ram_filters[:model]
22
+ end
23
+
24
+ query = parse_params(MysqlWarning).merge(query)
25
+ @records = MysqlWarning.all(query)
26
+ %>
27
+
28
+ <%= partial :grid,
29
+ :locals => {
30
+ :columns => @columns,
31
+ :records => @records,
32
+ :adhoc_columns => {},
33
+ :relationships => MysqlWarning.relationships
34
+ }
35
+ %>
@@ -0,0 +1,29 @@
1
+ <% @columns = Sqlite3ExecutionStep.properties %>
2
+ <%= partial :menu, :locals => {:columns => @columns} %>
3
+
4
+ <%
5
+ query = {}
6
+ path_to_repo_storage = Sqlite3ExecutionStep.executed_query.generalized_query.query_storage_links.repository_storage
7
+ unless ram_filters[:repository].blank?
8
+ query[path_to_repo_storage.repo_name]= ram_filters[:repository]
9
+ end
10
+
11
+ unless ram_filters[:adapter].blank?
12
+ query[path_to_repo_storage.adapter_name]= ram_filters[:adapter]
13
+ end
14
+
15
+ unless ram_filters[:model].blank?
16
+ query[path_to_repo_storage.model_name]= ram_filters[:model]
17
+ end
18
+
19
+ query = parse_params(Sqlite3ExecutionStep).merge(query)
20
+ @records = Sqlite3ExecutionStep.all(query)
21
+ %>
22
+
23
+ <%= partial :grid,
24
+ :locals => {
25
+ :columns => @columns,
26
+ :records => @records,
27
+ :relationships => Sqlite3ExecutionStep.relationships
28
+ }
29
+ %>
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-cutie-extras
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Cory ODaniel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-15 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: extlib
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.12
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: dm-core
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.10.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: dm-types
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.10.0
44
+ version:
45
+ description: Cool additional hooks for DM Cutie.
46
+ email: cutie@coryodaniel.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - README.markdown
53
+ - LICENSE
54
+ - History.txt
55
+ - TODO.markdown
56
+ files:
57
+ - LICENSE
58
+ - README.markdown
59
+ - Rakefile
60
+ - TODO.markdown
61
+ - lib/dm-cutie-extras.rb
62
+ - lib/dm-cutie-extras/version.rb
63
+ - History.txt
64
+ - lib/dm-cutie-extras/views/_template_.erb
65
+ - lib/dm-cutie-extras/views/mysql_execution_steps.erb
66
+ - lib/dm-cutie-extras/views/mysql_indexes.erb
67
+ - lib/dm-cutie-extras/views/mysql_warnings.erb
68
+ - lib/dm-cutie-extras/views/sqlite3_execution_steps.erb
69
+ - lib/dm-cutie-extras/hooks/mysql_execution_step.rb
70
+ - lib/dm-cutie-extras/hooks/mysql_index.rb
71
+ - lib/dm-cutie-extras/hooks/mysql_warning.rb
72
+ - lib/dm-cutie-extras/hooks/sqlite3_execution_step.rb
73
+ has_rdoc: true
74
+ homepage: http://github.com/coryodaniel/dm-cutie-extras
75
+ licenses: []
76
+
77
+ post_install_message:
78
+ rdoc_options: []
79
+
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "0"
87
+ version:
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: "0"
93
+ version:
94
+ requirements: []
95
+
96
+ rubyforge_project:
97
+ rubygems_version: 1.3.5
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: DM Cutie's Extras
101
+ test_files: []
102
+