rails_notebook 1.0.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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +22 -0
  4. data/app/assets/javascripts/rails_notebook/application.js +13 -0
  5. data/app/assets/stylesheets/rails_notebook/application.css +15 -0
  6. data/app/controllers/rails_notebook/application_controller.rb +4 -0
  7. data/app/helpers/rails_notebook/application_helper.rb +4 -0
  8. data/app/views/layouts/rails_notebook/application.html.erb +14 -0
  9. data/config/routes.rb +2 -0
  10. data/lib/rails_notebook.rb +14 -0
  11. data/lib/rails_notebook/assets/d3.js +9553 -0
  12. data/lib/rails_notebook/assets/dagre-d3.js +17572 -0
  13. data/lib/rails_notebook/assets/jquery.jsonview.css +52 -0
  14. data/lib/rails_notebook/assets/jquery.jsonview.js +284 -0
  15. data/lib/rails_notebook/assets/jquery.qtip.min.css +2 -0
  16. data/lib/rails_notebook/assets/jquery.qtip.min.js +3 -0
  17. data/lib/rails_notebook/assets/jquery.tipsy.js +288 -0
  18. data/lib/rails_notebook/assets/kernel.css +7 -0
  19. data/lib/rails_notebook/assets/kernel.js +18 -0
  20. data/lib/rails_notebook/assets/kernel.json +1 -0
  21. data/lib/rails_notebook/assets/logo-32x32.png +0 -0
  22. data/lib/rails_notebook/assets/logo-64x64.png +0 -0
  23. data/lib/rails_notebook/assets/nv.d3.css +641 -0
  24. data/lib/rails_notebook/assets/nv.d3.js +13298 -0
  25. data/lib/rails_notebook/assets/rails_notebook.css +136 -0
  26. data/lib/rails_notebook/assets/rails_notebook.js +548 -0
  27. data/lib/rails_notebook/assets/tipsy.css +25 -0
  28. data/lib/rails_notebook/command.rb +104 -0
  29. data/lib/rails_notebook/engine.rb +9 -0
  30. data/lib/rails_notebook/profiler.rb +111 -0
  31. data/lib/rails_notebook/renderers.rb +121 -0
  32. data/lib/rails_notebook/route.rb +121 -0
  33. data/lib/rails_notebook/schemaTable.rb +27 -0
  34. data/lib/rails_notebook/serializers.rb +92 -0
  35. data/lib/rails_notebook/table.rb +28 -0
  36. data/lib/rails_notebook/version.rb +3 -0
  37. data/lib/tasks/rails_notebook_tasks.rake +8 -0
  38. metadata +196 -0
@@ -0,0 +1,25 @@
1
+ .tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; }
2
+ .tipsy-inner { background-color: #000; color: #FFF; max-width: 600px; padding: 5px 8px 4px 8px; text-align: center; }
3
+
4
+ /* Rounded corners */
5
+ .tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
6
+
7
+ /* Uncomment for shadow */
8
+ /*.tipsy-inner { box-shadow: 0 0 5px #000000; -webkit-box-shadow: 0 0 5px #000000; -moz-box-shadow: 0 0 5px #000000; }*/
9
+
10
+ .tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; }
11
+
12
+ /* Rules to colour arrows */
13
+ .tipsy-arrow-n { border-bottom-color: #000; }
14
+ .tipsy-arrow-s { border-top-color: #000; }
15
+ .tipsy-arrow-e { border-left-color: #000; }
16
+ .tipsy-arrow-w { border-right-color: #000; }
17
+
18
+ .tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
19
+ .tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
20
+ .tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
21
+ .tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
22
+ .tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
23
+ .tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
24
+ .tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
25
+ .tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }
@@ -0,0 +1,104 @@
1
+ require 'fileutils'
2
+ require 'multi_json'
3
+
4
+ module RailsNotebook
5
+
6
+ class Command
7
+
8
+ def initialize
9
+ ipython_dir = ENV['IPYTHONDIR'] || '~/.ipython'
10
+ @kernel_dir = File.join(File.expand_path(ipython_dir), 'kernels', 'rails_notebook')
11
+ @kernel_file = File.join(@kernel_dir, 'kernel.json')
12
+ puts @kernel_file
13
+ @iruby_path = File.expand_path $0
14
+ puts @iruby_path
15
+ end
16
+
17
+ def run_kernel
18
+ require 'iruby/logger'
19
+ IRuby.logger = Rails.logger
20
+
21
+ connection_file = ENV['connection_file']
22
+ change_working_dir
23
+
24
+ IRuby::Kernel.new(connection_file).run
25
+ rescue Exception => e
26
+ IRuby.logger.fatal "Kernel died: #{e.message}\n#{e.backtrace.join("\n")}"
27
+ raise
28
+ end
29
+
30
+ def run_ipython
31
+ check_version
32
+ check_registered_kernel
33
+ change_working_dir
34
+ #create_static_symlink!
35
+ Kernel.exec('ipython', 'notebook')
36
+ end
37
+
38
+ private
39
+
40
+ #================================= For Symlink
41
+ def create_static_symlink!
42
+ src, dst = static_path, File.join(profile_path, '/kernels/rails_notebook')
43
+ puts src
44
+ puts dst
45
+ FileUtils.rm_r dst
46
+ File.symlink src, dst
47
+ end
48
+
49
+ def profile_path
50
+ `ipython locate rails_notebook`.strip
51
+ #File.expand_path(".." , `ipython locate rails_notebook`.strip)
52
+ end
53
+
54
+ def static_path
55
+ File.join(File.dirname(__FILE__), "assets")
56
+ end
57
+ #================================= For Symlink
58
+
59
+
60
+ def change_working_dir
61
+ working_dir = File.join(Rails.root, "notebooks")
62
+ Dir.mkdir(working_dir) unless Dir.exist?(working_dir)
63
+ Dir.chdir(working_dir) if working_dir
64
+ end
65
+
66
+ def check_version
67
+ required = '3.0.0'
68
+ version = `ipython --version`.chomp
69
+ if version < required
70
+ STDERR.puts "Your IPython version #{version} is too old, at least #{required} is required"
71
+ exit 1
72
+ end
73
+ end
74
+
75
+ def check_registered_kernel
76
+ if (kernel = registered_iruby_path)
77
+ STDERR.puts "#{@iruby_path} differs from registered path #{registered_iruby_path}.
78
+ This might not work. Run 'iruby register --force' to fix it." if @iruby_path != kernel
79
+ else
80
+ register_kernel
81
+ end
82
+ end
83
+
84
+ def register_kernel
85
+ FileUtils.mkpath(@kernel_dir)
86
+
87
+ if RUBY_PLATFORM =~ /mswin(?!ce)|mingw|cygwin/
88
+ ruby_path, iruby_path = [RbConfig.ruby, @iruby_path].map { |path| path.gsub('/', '\\\\') }
89
+ File.write(@kernel_file, MultiJson.dump(argv: [ruby_path, iruby_path, 'rails_notebook_kernel', 'connection_file={connection_file}'],
90
+ display_name: "Rails #{Rails.application.class.to_s}", language: 'ruby'))
91
+ else
92
+ File.write(@kernel_file, MultiJson.dump(argv: [@iruby_path, 'rails_notebook_kernel', 'connection_file={connection_file}'],
93
+ display_name: "Rails #{Rails.application.class.to_s}", language: 'ruby'))
94
+ end
95
+
96
+ FileUtils.copy(Dir[File.join(__dir__, 'assets', '*')], @kernel_dir) rescue nil
97
+ end
98
+
99
+ def registered_iruby_path
100
+ File.exist?(@kernel_file) && MultiJson.load(File.read(@kernel_file))['argv'].first
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,9 @@
1
+ module RailsNotebook
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsNotebook
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,111 @@
1
+ #https://github.com/SamSaffron/flamegraph/
2
+ #Modified from Sam Saffron's original code for flamegraph
3
+ require "json"
4
+ require "stackprof"
5
+
6
+ module Profiler
7
+ def self.profile(filename=nil, opts = {})
8
+ backtraces = nil;
9
+ fidelity = opts[:fidelity] || 0.05
10
+ backtraces = StackProfSampler.collect( fidelity ) do yield end
11
+ Profiler::Profile.new( Profiler::generate_data( backtraces ) , fidelity )
12
+ end
13
+
14
+ def self.generate_data( stacks )
15
+ table = []
16
+ prev = []
17
+
18
+ # a 2d array makes collapsing easy
19
+ stacks.each_with_index do |stack, pos|
20
+ next unless stack
21
+ col = []
22
+
23
+ stack.reverse.map{|r| r.to_s}.each_with_index do |frame, i|
24
+ if !prev[i].nil?
25
+ last_col = prev[i]
26
+ if last_col[0] == frame
27
+ last_col[1] += 1
28
+ col << nil
29
+ next
30
+ end
31
+ end
32
+
33
+ prev[i] = [frame, 1]
34
+ col << prev[i]
35
+ end
36
+ prev = prev[0..col.length-1].to_a
37
+ table << col
38
+ end
39
+
40
+ data = []
41
+ # a 1d array makes rendering easy
42
+ table.each_with_index do |col, col_num|
43
+ col.each_with_index do |row, row_num|
44
+ next unless row && row.length == 2
45
+ data << {
46
+ :x => col_num + 1,
47
+ :y => row_num + 1,
48
+ :width => row[1],
49
+ :frame => row[0]
50
+ }
51
+ end
52
+ end
53
+ data
54
+ end
55
+
56
+ class Profile
57
+ def initialize( data, _sampleTime )
58
+ @data = data
59
+ @sampleTime = _sampleTime
60
+ end
61
+ end
62
+
63
+ class StackProfSampler
64
+ def self.collect(fidelity=0.5)
65
+ result = StackProf.run(mode: :wall,
66
+ raw: true,
67
+ aggregate: false,
68
+ interval: (fidelity * 1000).to_i) do
69
+ yield
70
+ end
71
+
72
+ stacks = []
73
+ stack = []
74
+
75
+ return [] unless result[:raw]
76
+
77
+ length = nil
78
+ result[:raw].each do |i|
79
+ if length.nil?
80
+ length = i
81
+ next
82
+ end
83
+
84
+ if length > 0
85
+ frame = result[:frames][i]
86
+ frame = "#{frame[:file]}:#{frame[:line]}:in `#{frame[:name]}'"
87
+ # puts frame
88
+ #if frame.to_s =~ /iruby/ && !( frame.to_s =~ /IRuby::/ )
89
+ s = frame.to_s
90
+ if !( s =~ /application.rb/ ) &&
91
+ !( s =~ /Rake::Application/) &&
92
+ !( s =~ /task.rb/) &&
93
+ !( s =~ /kernel.rb/) &&
94
+ !( s =~ /profiler.rb/)
95
+ stack << frame.to_s
96
+ end
97
+ length -= 1
98
+ next
99
+ end
100
+
101
+ i.times do
102
+ stacks << stack.reverse
103
+ end
104
+
105
+ stack = []
106
+ length = nil
107
+ end
108
+ stacks
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,121 @@
1
+ require 'json'
2
+
3
+ module RailsNotebook
4
+
5
+ module Renderers
6
+
7
+ def self.pretty_print_json( json )
8
+ <<-HTML
9
+ <div id="json-#{json.object_id}"></div>
10
+ <script>
11
+ require(["jquery", "/kernelspecs/rails_notebook/jquery.jsonview.js"], function ($, jsonview) {
12
+ $("#json-#{json.object_id}").JSONView( #{json} );
13
+ });
14
+ </script>
15
+ HTML
16
+ end
17
+
18
+ def self.render_html( object , functionName )
19
+ <<-HTML
20
+ <div id="railsnb-#{object.object_id}"></div>
21
+ <script>
22
+ require(["/kernelspecs/rails_notebook/rails_notebook.js"] , function ( railsNB ) {
23
+ railsNB.#{functionName}( #{MultiJson.dump(object)} , document.getElementById( "railsnb-#{object.object_id}" ) );
24
+ });
25
+ </script>
26
+ HTML
27
+ end
28
+ end
29
+
30
+ IRuby::Display::Registry.type { String }
31
+ IRuby::Display::Registry.format("text/html") do |string|
32
+ begin JSON.parse( string )
33
+ Renderers.pretty_print_json( string )
34
+ rescue
35
+ string
36
+ end
37
+ end
38
+
39
+ IRuby::Display::Registry.type { Rails.application }
40
+ IRuby::Display::Registry.format("text/html") do |obj|
41
+ Renderers.json_view( Serializers.serialize(obj) )
42
+ end
43
+
44
+ IRuby::Display::Registry.type { Profiler::Profile }
45
+ IRuby::Display::Registry.format("text/html") do |profiledData|
46
+ Renderers.render_html( profiledData, "renderFlamechart" )
47
+ end
48
+
49
+ IRuby::Display::Registry.type { ActionDispatch::Routing::RouteSet }
50
+ IRuby::Display::Registry.format("text/html") do |route_set|
51
+ all_routes = route_set.routes.to_a
52
+ all_routes.reject! { |route| route.verb.nil? || route.path.spec.to_s == '/assets' || ( route.path.spec.to_s =~ /^\/rails(.*)/ ) || route.path.spec.to_s =~ /^\/pages(.*)/ }
53
+ routeTree = RouteTree.new( RouteNode.new( "/" , nil, nil, nil, "" ) )
54
+ all_routes.group_by { |route| route.defaults[:controller] }.each_value do |group|
55
+ group.each do |r|
56
+ routeNode = RouteNode.new( (r.path.spec.to_s.sub '(.:format)','') ,
57
+ r.verb.inspect.gsub(/^.{2}|.{2}$/, "") ,
58
+ r.name.to_s , r.defaults[:action].to_s ,
59
+ (r.path.spec.to_s.sub '(.:format)','').split("/")[-1]
60
+ )
61
+ routeTree.insertNode( routeNode )
62
+ end
63
+ end
64
+ #routeTree.printTree() # for debugging
65
+ Renderers.render_html( routeTree, "renderRoutes" )
66
+ end
67
+
68
+ IRuby::Display::Registry.type { ActiveRecord::Base.connection.tables }
69
+ IRuby::Display::Registry.format("text/html") do |obj|
70
+ tables = [] # List of Table objects
71
+ tableNames = [] # List of table names
72
+ obj.each do |table_name|
73
+ tableNames.push(table_name.singularize.foreign_key)
74
+ end # Populates table names
75
+
76
+ obj.each do |table_name| # Finding foreign keys and populating the array of Table objects
77
+ columnsTemp = []
78
+ arrowsTo = []
79
+ ActiveRecord::Base.connection.columns(table_name).each do |c|
80
+ columnTemp = []
81
+ if (tableNames.include? c.name)
82
+ arrowsTo.push(c.name[0..-4].pluralize.downcase) # [0..-4] chops off the _id suffix for foreign keys. .humanize renmoves existing underscores, which causes bugs.
83
+ columnTemp.push("* " + c.name)
84
+ columnTemp.push(c.type.to_s)
85
+ else
86
+ columnTemp.push("- " + c.name)
87
+ columnTemp.push(c.type.to_s)
88
+ end
89
+ columnsTemp.push(columnTemp)
90
+ end # end iterating through columns in tables
91
+ tables.push(Table.new(table_name , columnsTemp , arrowsTo ))
92
+ end # end iterating through tables
93
+ tables = tables.sort_by { |x| x.arrowsTo.length }
94
+ Renderers.render_html( tables, "renderSchema" ) # Parses the array of tables to Javascript for rendering
95
+ end
96
+
97
+ IRuby::Display::Registry.type { ActiveRecord::Relation}
98
+ IRuby::Display::Registry.format("text/html") do |obj|
99
+ columnNames = []
100
+ tableData = []
101
+ tableData.push(obj.name)
102
+ obj.columns.each do |column|
103
+ columnNames.push(column.name)
104
+ end
105
+ columnNames.each do |index|
106
+ tempValues = []
107
+ tempValues.push(index)
108
+ obj[0,10].each do |row| # Display only the first ten rows
109
+ tempValues.push(row[index])
110
+ end
111
+ tableData.push(tempValues)
112
+ end
113
+ Renderers.render_html( tableData, "renderTableData" )
114
+ end # DatabaseQueries
115
+
116
+ IRuby::Display::Registry.type { SchemaTable::ChartRenderer }
117
+ IRuby::Display::Registry.format("text/html") do |obj|
118
+ Renderers.render_html( obj.data , "renderBarChart" )
119
+ end
120
+
121
+ end
@@ -0,0 +1,121 @@
1
+ class RouteNode
2
+
3
+ def initialize( _uriPattern , _verb , _controller , _action, _nodeUri )
4
+ @uriPattern = _uriPattern
5
+ _verb ? @verbs = [_verb] : @verbs = []
6
+ @controller = _controller
7
+ _action ? @actions = [_action] : @actions = []
8
+ @countChildrenNodes = 0
9
+ @childrenNodes = []
10
+ @nodeUri = _nodeUri
11
+ @id = self.object_id
12
+ end
13
+ def updateParams( _verb , _controller , _action )
14
+ @verbs << _verb unless _verb.nil?
15
+ @controller = _controller
16
+ @actions << _action unless _action.nil?
17
+ end
18
+ def verbs
19
+ @verbs
20
+ end
21
+ def verb
22
+ @verbs.first
23
+ end
24
+ def controller
25
+ @controller
26
+ end
27
+ def actions
28
+ @actions
29
+ end
30
+ def action
31
+ @actions.first
32
+ end
33
+ def uriPattern
34
+ @uriPattern
35
+ end
36
+ def getChildren
37
+ @childrenNodes
38
+ end
39
+ def getCountChildrenNodes
40
+ @countChildrenNodes
41
+ end
42
+
43
+ def hasRoute( uri_mapping )
44
+ @childrenNodes.each do |childNode|
45
+ if childNode.uriPattern == uri_mapping
46
+ return childNode
47
+ end
48
+ end
49
+ return false
50
+ end
51
+
52
+ def insertChild( childNode )
53
+ @childrenNodes << childNode
54
+ end
55
+
56
+ def countChildrenNodes( count )
57
+ @childrenNodes.each do |child|
58
+ count = count + child.countChildrenNodes(0)
59
+ end
60
+ @countChildrenNodes = count
61
+ count += 1
62
+ return count
63
+ end
64
+
65
+ def printNode
66
+ puts "====================NODE===================="
67
+ puts @uriPattern
68
+ puts @verbs
69
+ puts @controller
70
+ puts @actions
71
+ puts "============================================"
72
+ @childrenNodes.each do |child|
73
+ child.printNode
74
+ end
75
+ end
76
+ end
77
+
78
+
79
+
80
+ class RouteTree
81
+ def initialize( _headNode )
82
+ @headNode = _headNode
83
+ end
84
+
85
+ def insertNode( _node )
86
+ #Get route mapping and iteratively either walk the tree or insert a node at the correct position
87
+ uri_mapping = ""
88
+ thisNode = @headNode #always insert from the headNode
89
+
90
+ _node.uriPattern.split("/").each do |uri|
91
+ uri_mapping += uri + "/"
92
+ if uri_mapping[0...-1] == _node.uriPattern || uri_mapping == _node.uriPattern # We are at node to insert or update
93
+ if thisNode.uriPattern == uri_mapping # Update the parameters to include details
94
+ thisNode.updateParams( _node.verb , _node.controller , _node.action )
95
+ elsif thisNode.hasRoute( uri_mapping )
96
+ thisNode.hasRoute( uri_mapping ).updateParams( _node.verb , _node.controller , _node.action )
97
+ else
98
+ child = RouteNode.new( uri_mapping , _node.verb , _node.controller , _node.action, uri )
99
+ thisNode.insertChild( child )
100
+ end
101
+ elsif thisNode.hasRoute( uri_mapping ) # node exists so walk the tree
102
+ thisNode = thisNode.hasRoute( uri_mapping )
103
+ elsif thisNode.uriPattern == uri_mapping # already there so do nothing
104
+ #thisNode.updateParams( _node.verb , _node.controller , _node.action )
105
+ else
106
+ child = RouteNode.new( uri_mapping , nil, nil , nil, uri )
107
+ thisNode.insertChild( child )
108
+ thisNode = child
109
+ end
110
+ end
111
+ end
112
+ def removeNode( _node )
113
+ puts "removing node"
114
+ end
115
+ def getRoot
116
+ @headNode
117
+ end
118
+ def printTree
119
+ @headNode.printNode
120
+ end
121
+ end