gdonald-railsdb 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. data/LICENSE.txt +674 -0
  2. data/README.txt +40 -0
  3. data/README_RAILS.txt +204 -0
  4. data/Rakefile +31 -0
  5. data/app/controllers/application.rb +24 -0
  6. data/app/controllers/database_controller.rb +274 -0
  7. data/app/controllers/home_controller.rb +366 -0
  8. data/app/helpers/application_helper.rb +43 -0
  9. data/app/helpers/home_helper.rb +2 -0
  10. data/app/models/database.rb +113 -0
  11. data/app/models/driver.rb +8 -0
  12. data/app/models/field.rb +117 -0
  13. data/app/models/group.rb +12 -0
  14. data/app/models/group_permission.rb +6 -0
  15. data/app/models/permission.rb +9 -0
  16. data/app/models/row.rb +26 -0
  17. data/app/models/switch.rb +85 -0
  18. data/app/models/table.rb +155 -0
  19. data/app/models/user.rb +98 -0
  20. data/app/models/user_group.rb +6 -0
  21. data/app/views/database/_field.html.erb +20 -0
  22. data/app/views/database/_field_form.html.erb +28 -0
  23. data/app/views/database/_insert_form.html.erb +37 -0
  24. data/app/views/database/_new_field.html.erb +43 -0
  25. data/app/views/database/_new_field_header.html.erb +12 -0
  26. data/app/views/database/_pagination.html.erb +14 -0
  27. data/app/views/database/_row.html.erb +16 -0
  28. data/app/views/database/_table.html.erb +22 -0
  29. data/app/views/database/_table_form.html.erb +45 -0
  30. data/app/views/database/add_fields.html.erb +3 -0
  31. data/app/views/database/add_table.html.erb +3 -0
  32. data/app/views/database/blank_field.rjs +6 -0
  33. data/app/views/database/browse.html.erb +30 -0
  34. data/app/views/database/del_field.html.erb +31 -0
  35. data/app/views/database/del_table.html.erb +31 -0
  36. data/app/views/database/edit_field.html.erb +23 -0
  37. data/app/views/database/edit_row.html.erb +22 -0
  38. data/app/views/database/index.html.erb +23 -0
  39. data/app/views/database/insert.html.erb +3 -0
  40. data/app/views/database/table.html.erb +30 -0
  41. data/app/views/home/_database.html.erb +8 -0
  42. data/app/views/home/_database_form.html.erb +68 -0
  43. data/app/views/home/_driver.html.erb +5 -0
  44. data/app/views/home/_driver_form.html.erb +25 -0
  45. data/app/views/home/_group.html.erb +10 -0
  46. data/app/views/home/_group_form.html.erb +25 -0
  47. data/app/views/home/_group_permission.html.erb +4 -0
  48. data/app/views/home/_permission.html.erb +6 -0
  49. data/app/views/home/_permission_form.html.erb +32 -0
  50. data/app/views/home/_user.html.erb +10 -0
  51. data/app/views/home/_user_form.html.erb +64 -0
  52. data/app/views/home/_user_group.html.erb +4 -0
  53. data/app/views/home/add_database.html.erb +3 -0
  54. data/app/views/home/add_driver.html.erb +3 -0
  55. data/app/views/home/add_group.html.erb +4 -0
  56. data/app/views/home/add_permission.html.erb +3 -0
  57. data/app/views/home/add_user.html.erb +62 -0
  58. data/app/views/home/bottom.html.erb +17 -0
  59. data/app/views/home/databases.html.erb +22 -0
  60. data/app/views/home/del_database.html.erb +31 -0
  61. data/app/views/home/del_driver.html.erb +32 -0
  62. data/app/views/home/del_group.html.erb +32 -0
  63. data/app/views/home/del_group_permission.html.erb +32 -0
  64. data/app/views/home/del_permission.html.erb +32 -0
  65. data/app/views/home/del_user.html.erb +33 -0
  66. data/app/views/home/del_user_group.html.erb +32 -0
  67. data/app/views/home/drivers.html.erb +20 -0
  68. data/app/views/home/edit_database.html.erb +3 -0
  69. data/app/views/home/edit_driver.html.erb +3 -0
  70. data/app/views/home/edit_group.html.erb +4 -0
  71. data/app/views/home/edit_permission.html.erb +4 -0
  72. data/app/views/home/edit_user.html.erb +84 -0
  73. data/app/views/home/group_permissions.html.erb +38 -0
  74. data/app/views/home/groups.html.erb +20 -0
  75. data/app/views/home/index.html.erb +21 -0
  76. data/app/views/home/login.html.erb +42 -0
  77. data/app/views/home/menu.html.erb +33 -0
  78. data/app/views/home/permissions.html.erb +21 -0
  79. data/app/views/home/top.html.erb +22 -0
  80. data/app/views/home/user_groups.html.erb +44 -0
  81. data/app/views/home/users.html.erb +27 -0
  82. data/app/views/layouts/application.html.erb +18 -0
  83. data/config/boot.rb +109 -0
  84. data/config/database.yml +14 -0
  85. data/config/environment.rb +114 -0
  86. data/config/environments/development.rb +18 -0
  87. data/config/environments/production.rb +19 -0
  88. data/config/environments/test.rb +22 -0
  89. data/config/initializers/inflections.rb +10 -0
  90. data/config/initializers/mime_types.rb +5 -0
  91. data/config/routes.rb +135 -0
  92. data/db/migrate/001_create_sessions.rb +17 -0
  93. data/db/migrate/002_create_users.rb +26 -0
  94. data/db/migrate/003_create_groups.rb +17 -0
  95. data/db/migrate/004_create_permissions.rb +18 -0
  96. data/db/migrate/005_create_group_permissions.rb +22 -0
  97. data/db/migrate/006_create_user_groups.rb +19 -0
  98. data/db/migrate/007_create_drivers.rb +17 -0
  99. data/db/migrate/008_create_databases.rb +43 -0
  100. data/db/schema.rb +91 -0
  101. data/lib/ignore +0 -0
  102. data/log/ignore +0 -0
  103. data/public/404.html +30 -0
  104. data/public/422.html +30 -0
  105. data/public/500.html +30 -0
  106. data/public/dispatch.cgi +10 -0
  107. data/public/dispatch.fcgi +24 -0
  108. data/public/dispatch.rb +10 -0
  109. data/public/favicon.ico +0 -0
  110. data/public/images/rails.png +0 -0
  111. data/public/images/ss1.png +0 -0
  112. data/public/images/ss2.png +0 -0
  113. data/public/images/ss3.png +0 -0
  114. data/public/images/ss4.png +0 -0
  115. data/public/images/ss5.png +0 -0
  116. data/public/images/ss6.png +0 -0
  117. data/public/javascripts/application.js +25 -0
  118. data/public/javascripts/controls.js +963 -0
  119. data/public/javascripts/dragdrop.js +972 -0
  120. data/public/javascripts/effects.js +1120 -0
  121. data/public/javascripts/prototype.js +4225 -0
  122. data/public/robots.txt +4 -0
  123. data/public/stylesheets/railsdb.css +227 -0
  124. data/script/about +3 -0
  125. data/script/console +3 -0
  126. data/script/destroy +3 -0
  127. data/script/generate +3 -0
  128. data/script/performance/benchmarker +3 -0
  129. data/script/performance/profiler +3 -0
  130. data/script/performance/request +3 -0
  131. data/script/plugin +3 -0
  132. data/script/process/inspector +3 -0
  133. data/script/process/reaper +3 -0
  134. data/script/process/spawner +3 -0
  135. data/script/runner +3 -0
  136. data/script/server +3 -0
  137. data/test/fixtures/databases.yml +7 -0
  138. data/test/fixtures/drivers.yml +7 -0
  139. data/test/fixtures/group_permissions.yml +7 -0
  140. data/test/fixtures/groups.yml +7 -0
  141. data/test/fixtures/permissions.yml +7 -0
  142. data/test/fixtures/user_groups.yml +7 -0
  143. data/test/fixtures/users.yml +18 -0
  144. data/test/functional/home_controller_test.rb +8 -0
  145. data/test/test_helper.rb +38 -0
  146. data/test/unit/database_test.rb +8 -0
  147. data/test/unit/driver_test.rb +8 -0
  148. data/test/unit/group_permission_test.rb +8 -0
  149. data/test/unit/group_test.rb +8 -0
  150. data/test/unit/permission_test.rb +8 -0
  151. data/test/unit/user_group_test.rb +8 -0
  152. data/test/unit/user_test.rb +9 -0
  153. data/tmp/pids/ignore +0 -0
  154. data/tmp/sessions/ignore +0 -0
  155. data/tmp/sockets/ignore +0 -0
  156. metadata +287 -0
@@ -0,0 +1,8 @@
1
+ class Driver < ActiveRecord::Base
2
+
3
+ has_many :databases
4
+
5
+ validates_presence_of :name,
6
+ :message => 'name required'
7
+
8
+ end
@@ -0,0 +1,117 @@
1
+ class Field
2
+
3
+ include Comparable
4
+ include Switch
5
+
6
+ attr_accessor :database
7
+ attr_accessor :table
8
+ attr_accessor :attributes
9
+
10
+ #
11
+ # This method assigns the database and table this field appears in,
12
+ # along with all the attributes, no matter how useless they may be.
13
+ #
14
+ def initialize( table, attributes )
15
+ self.table = table
16
+ self.attributes = attributes
17
+ self.database = self.table.database
18
+ end
19
+
20
+ #
21
+ # This is our obligation to Comparable. Order by column id
22
+ # (which is faked for some databases).
23
+ #
24
+ def <=>( other )
25
+ self.cid <=> other.cid
26
+ end
27
+
28
+ #
29
+ # Update the field using the parameters passed. This first
30
+ # checks the name for an update then operates on the actual
31
+ # field attributes.
32
+ #
33
+ def update( params )
34
+ switch( self.database ) do
35
+ if params['field'] != params['fields']['1']['name']
36
+ ActiveRecord::Base.connection.rename_column( self.table.name.to_sym,
37
+ params['field'].to_sym,
38
+ params['fields']['1']['name'].to_sym )
39
+ end
40
+ ActiveRecord::Base.connection.change_column( self.table.name.to_sym,
41
+ params['fields']['1']['name'].to_sym,
42
+ params['fields']['1']['type'].to_sym,
43
+ mangle_column_options( params, '1' ) )
44
+ end
45
+ end
46
+
47
+ #
48
+ # name mapping
49
+ #
50
+ def name
51
+ self.attributes[:name]
52
+ end
53
+
54
+ #
55
+ # column id mapping
56
+ #
57
+ def cid
58
+ self.attributes[:cid]
59
+ end
60
+
61
+ #
62
+ # not all databases send this
63
+ #
64
+ def sql_type
65
+ self.attributes[:sql_type]
66
+ end
67
+
68
+ #
69
+ # field type mapping
70
+ #
71
+ def type
72
+ self.attributes[:type]
73
+ end
74
+
75
+ #
76
+ # limit mapping
77
+ #
78
+ def limit
79
+ self.attributes[:limit]
80
+ end
81
+
82
+ #
83
+ # precision mapping
84
+ #
85
+ def precision
86
+ self.attributes[:precision]
87
+ end
88
+
89
+ #
90
+ # scale mapping
91
+ #
92
+ def scale
93
+ self.attributes[:scale]
94
+ end
95
+
96
+ #
97
+ # inconsistant on some databases
98
+ #
99
+ def primary
100
+ self.attributes[:primary]
101
+ end
102
+
103
+ #
104
+ # is null mapping
105
+ #
106
+ def null
107
+ self.attributes[:null]
108
+ end
109
+
110
+ #
111
+ # default value mapping
112
+ #
113
+ def default
114
+ self.attributes[:default]
115
+ end
116
+
117
+ end
@@ -0,0 +1,12 @@
1
+ class Group < ActiveRecord::Base
2
+
3
+ has_many :user_groups
4
+ has_many :users, :through => :user_groups
5
+
6
+ has_many :group_permissions
7
+ has_many :permissions, :through => :group_permissions
8
+
9
+ validates_presence_of :name,
10
+ :message => 'group name required'
11
+
12
+ end
@@ -0,0 +1,6 @@
1
+ class GroupPermission < ActiveRecord::Base
2
+
3
+ belongs_to :group
4
+ belongs_to :permission
5
+
6
+ end
@@ -0,0 +1,9 @@
1
+ class Permission < ActiveRecord::Base
2
+
3
+ has_many :group_permissions
4
+ has_many :permissions, :through => :group_permissions
5
+
6
+ validates_presence_of :name,
7
+ :message => 'permission name required'
8
+
9
+ end
@@ -0,0 +1,26 @@
1
+ class Row < ActiveRecord::Base
2
+
3
+ include Comparable
4
+ include Switch
5
+
6
+ attr_accessor :database
7
+ attr_accessor :table
8
+ attr_accessor :id
9
+
10
+ #
11
+ # This method assigns the table, id, and database attributes
12
+ #
13
+ def initialize( table, id )
14
+ self.table = table
15
+ self.id = id
16
+ self.database = self.table.database
17
+ end
18
+
19
+ #
20
+ # This is our obligation to Comparable. Order by id for now
21
+ #
22
+ def <=>( other )
23
+ self.id <=> other.id
24
+ end
25
+
26
+ end
@@ -0,0 +1,85 @@
1
+ #
2
+ # This is a mixin used by any other models that need to do
3
+ # work with non- RailsDB application databases.
4
+ #
5
+ module Switch
6
+
7
+ #
8
+ # Switch RailsDB's database connection to a different database.
9
+ #
10
+ # (Add newly discovered broken table names in config/environment.rb.)
11
+ #
12
+ # This is magic:
13
+ #
14
+ def switch_ar( database, name )
15
+ switch( database ) do
16
+ begin
17
+ c = name.singularize.camelize.constantize
18
+ rescue NameError
19
+ klass = Class.new ActiveRecord::Base
20
+ Object.const_set name.singularize.camelize, klass
21
+ klass.set_table_name name
22
+ begin
23
+ c = name.singularize.camelize.constantize
24
+ rescue NameError
25
+ raise "NameError: Cannot constantize #{ name }"
26
+ end
27
+ end
28
+ klass = Class.new ActiveRecord::Base
29
+ class_name = ALT_TABLE_NAMES.include?( name ) ? ALT_TABLE_NAMES[ name ] : name
30
+ Object.const_set class_name.singularize.camelize, klass
31
+ klass.set_table_name name
32
+ yield class_name.singularize.camelize.constantize
33
+ end
34
+ end
35
+
36
+ #
37
+ # This method wraps work done to alternate databases
38
+ #
39
+ def switch( database )
40
+ switch_db( database )
41
+ begin
42
+ yield
43
+ rescue ArgumentError
44
+ raise "Argument error: #{ $!.to_s }"
45
+ rescue ActiveRecord::StatementInvalid
46
+ raise "#{ $!.to_s }"
47
+ rescue Mysql::Error, PGError, TypeError
48
+ raise "Database Error: #{ $!.to_s }"
49
+ ensure
50
+ switch_back
51
+ end
52
+ end
53
+
54
+ #
55
+ # This method establishes a new ActiveRecord connection using
56
+ # the database passed. If you call this you need to finish by
57
+ # calling switch_back.
58
+ #
59
+ def switch_db( database )
60
+ options = { :adapter => database.driver.name }
61
+ case database.driver.name
62
+ when 'sqlite3'
63
+ options[:database] = database.path
64
+ when 'mysql',
65
+ 'postgresql',
66
+ 'oracle'
67
+ options[:database] = database.name
68
+ options[:host] = database.host
69
+ options[:username] = database.username
70
+ options[:password] = database.password
71
+ else
72
+ raise "#{ database.driver.name } driver not available"
73
+ end
74
+ ActiveRecord::Base.establish_connection( options )
75
+ end
76
+
77
+ #
78
+ # This method is used to re-establish the RailsDB application
79
+ # database. It's usually called after a call to switch_db.
80
+ #
81
+ def switch_back
82
+ ActiveRecord::Base.establish_connection( RAILS_ENV.to_sym )
83
+ end
84
+
85
+ end
@@ -0,0 +1,155 @@
1
+ class Table
2
+
3
+ include Switch
4
+
5
+ attr_accessor :database
6
+ attr_accessor :name
7
+ attr_accessor :driver
8
+
9
+ #
10
+ # Assign the database this table belongs to and it's name
11
+ #
12
+ def initialize( database, name )
13
+ self.database = database
14
+ self.name = name
15
+ self.driver = self.database.driver
16
+ end
17
+
18
+ #
19
+ # This method switches ActiveRecord's connection to the actual
20
+ # database this database model represents, deletes the field,
21
+ # then switches back to the RailsDB database.
22
+ #
23
+ def del_field( name )
24
+ switch( self.database ) do
25
+ ActiveRecord::Base.connection.remove_column( self.name.to_sym, name.to_sym )
26
+ end
27
+ end
28
+
29
+ #
30
+ # This method switches ActiveRecord's connection to the actual
31
+ # database this database model represents, adds the new field,
32
+ # then switches back to the RailsDB database.
33
+ #
34
+ def add_field( name, type, options )
35
+ switch( self.database ) do
36
+ ActiveRecord::Base.connection.add_column( self.name.to_sym, name.to_sym, type.to_sym, options )
37
+ end
38
+ end
39
+
40
+ #
41
+ # This method adds fields from the params from a form.
42
+ #
43
+ def add_fields( params )
44
+ 1.upto( params[:fields].size ) do |x|
45
+ if params[:fields][x.to_s] && !params[:fields][x.to_s][:name].empty?
46
+ unless self.has_field?( params[:fields][x.to_s][:name] )
47
+ self.add_field( params[:fields][x.to_s][:name],
48
+ params[:fields][x.to_s][:type],
49
+ mangle_column_options( params, x.to_s ) )
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ #
56
+ # This calls ActiveRecord's create method on the given model class
57
+ #
58
+ def create( args )
59
+ switch_ar( self.database, self.name ) do |c|
60
+ params = {}
61
+ args.collect { |a| { a[0].to_sym => args[ a[0] ] } }.each do |b|
62
+ params[ b.keys.first.to_sym ] = b[ b.keys.first.to_sym ]
63
+ end
64
+ o = c.create( params )
65
+ end
66
+ end
67
+
68
+ #
69
+ # This calls ActiveRecord's find method on the given model class
70
+ #
71
+ def find( *args )
72
+ switch_ar( self.database, self.name ) { |c| o = c.find( *args ).collect{ |r| r.attributes } }
73
+ end
74
+
75
+ #
76
+ # This method gets the row count for this table.
77
+ #
78
+ def row_count
79
+ switch_ar( self.database, self.name ) { |c| o = c.count }
80
+ end
81
+
82
+ #
83
+ # Just the field names, sorted
84
+ #
85
+ def field_names
86
+ self.fields.collect { |f| f.name }.sort
87
+ end
88
+
89
+ #
90
+ # Does this table contain a filed with passed name?
91
+ #
92
+ def has_field?( name )
93
+ self.field_names.each { |f| return true if f == name }
94
+ false
95
+ end
96
+
97
+ #
98
+ # How many fields in this table
99
+ #
100
+ def field_count
101
+ self.fields.size
102
+ end
103
+
104
+ #
105
+ # Returns a single field by name
106
+ #
107
+ def get_field( name )
108
+ self.fields.each { |f| return f if f.name == name }
109
+ nil
110
+ end
111
+
112
+ #
113
+ # This method maps the field attributes for a table into known named
114
+ # keys, required mostly because sqlite doesn't match the others
115
+ #
116
+ def fields
117
+ fields = []
118
+ switch( self.database ) do
119
+ cid = 0
120
+ case self.driver.name
121
+ when 'sqlite3'
122
+ # map sqlite fields to match more common names
123
+ ActiveRecord::Base.connection.table_structure( self.name.to_sym ).each do |c|
124
+ attributes = {}
125
+ attributes[:cid] = c['cid']
126
+ attributes[:name] = c['name']
127
+ attributes[:type] = c['type']
128
+ attributes[:primary] = c['pk']
129
+ attributes[:null] = c['notnull']
130
+ attributes[:default] = c['dflt_value']
131
+ fields << Field.new( self, attributes )
132
+ end
133
+ else
134
+ # this same mapping currently works for both mysql and postgresql
135
+ ActiveRecord::Base.connection.columns( self.name.to_sym ).each do |c|
136
+ attributes = {}
137
+ attributes[:cid] = cid
138
+ attributes[:name] = c.name
139
+ attributes[:type] = c.type
140
+ attributes[:sql_type] = c.sql_type
141
+ attributes[:primary] = c.primary
142
+ attributes[:null] = c.null
143
+ attributes[:default] = c.default
144
+ attributes[:scale] = c.scale
145
+ attributes[:precision] = c.precision
146
+ attributes[:limit] = c.limit
147
+ fields << Field.new( self, attributes )
148
+ cid += 1
149
+ end
150
+ end
151
+ end
152
+ fields.sort
153
+ end
154
+
155
+ end
@@ -0,0 +1,98 @@
1
+ require 'digest/sha1'
2
+
3
+ class User < ActiveRecord::Base
4
+
5
+ has_many :user_groups
6
+ has_many :groups, :through => :user_groups
7
+
8
+ attr_accessor :password_confirmation
9
+
10
+ validates_presence_of :fname,
11
+ :message => 'first name required'
12
+
13
+ validates_presence_of :lname,
14
+ :message => 'last name required'
15
+
16
+ validates_presence_of :username,
17
+ :message => 'last name required'
18
+
19
+ validates_format_of :email,
20
+ :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i,
21
+ :message => 'valid email required'
22
+
23
+ validates_uniqueness_of :email,
24
+ :message => 'unique email required'
25
+
26
+ validates_presence_of :password,
27
+ :message => 'password required'
28
+
29
+ validates_confirmation_of :password,
30
+ :message => 'password confirmation required'
31
+
32
+ # Both of them, together
33
+ def fullname
34
+ "#{ self.fname } #{ self.lname }"
35
+ end
36
+
37
+ # Find permission by iterating over group permissions
38
+ def has_perm?( perm )
39
+ permission = Permission.find_by_name( perm )
40
+ raise "#{perm} not found" if permission.nil?
41
+ groups.each do |g|
42
+ g.permissions.each do |p|
43
+ return true if p.id == permission.id
44
+ end
45
+ end
46
+ return false
47
+ end
48
+
49
+ # Authenticate against the database
50
+ def self.authenticate( username, password )
51
+ @user = User.find( :first, :conditions => [ 'username = ?', username ] )
52
+ if @user.nil?
53
+ @user = User.find( :first, :conditions => [ 'email = ?', username ] )
54
+ end
55
+ return nil if @user.nil?
56
+ return @user if User.hash_password( password, @user.passwd_salt ) == @user.passwd_hash
57
+ nil
58
+ end
59
+
60
+ # Convenience method
61
+ def password
62
+ @password
63
+ end
64
+
65
+ # Setter method assigning a new password. Re-salts automatically.
66
+ def password=( passwd )
67
+ @password = passwd
68
+ return if passwd.blank?
69
+ self.passwd_salt = User.salt
70
+ self.passwd_hash = User.hash_password( @password, self.passwd_salt )
71
+ end
72
+
73
+ private
74
+
75
+ def self.salt
76
+ Digest::SHA1.hexdigest( rand.to_s )
77
+ end
78
+
79
+ def self.hash_password( password, salt )
80
+ Digest::SHA1.hexdigest( password + salt )
81
+ end
82
+
83
+ def self.random_password
84
+ c = %w( b c d f g h j k l m n p qu r s t v w x z ) +
85
+ %w( ch cr fr nd ng nk nt ph pr rd sh sl sp st th tr )
86
+ v = %w( a e i o u y )
87
+ f, r = true, ''
88
+ 6.times do
89
+ r << ( f ? c[ rand * c.size ] : v[ rand * v.size ] )
90
+ f = !f
91
+ end
92
+ 2.times do
93
+ r << ( rand( 9 ) + 1 ).to_s
94
+ end
95
+ r
96
+ end
97
+
98
+ end