rails_lite 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +8 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +22 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +39 -0
  8. data/Rakefile +2 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/exe/railslite +4 -0
  12. data/lib/.DS_Store +0 -0
  13. data/lib/rails_lite.rb +6 -0
  14. data/lib/rails_lite/.DS_Store +0 -0
  15. data/lib/rails_lite/cli.rb +65 -0
  16. data/lib/rails_lite/version.rb +3 -0
  17. data/lib/scaffold/.DS_Store +0 -0
  18. data/lib/scaffold/Gemfile +12 -0
  19. data/lib/scaffold/app/controllers/albums_controller.rb +55 -0
  20. data/lib/scaffold/app/controllers/application_controller.rb +45 -0
  21. data/lib/scaffold/app/controllers/bands_controller.rb +62 -0
  22. data/lib/scaffold/app/controllers/notes_controller.rb +26 -0
  23. data/lib/scaffold/app/controllers/sessions_controller.rb +34 -0
  24. data/lib/scaffold/app/controllers/tracks_controller.rb +55 -0
  25. data/lib/scaffold/app/controllers/users_controller.rb +41 -0
  26. data/lib/scaffold/app/models/album.rb +14 -0
  27. data/lib/scaffold/app/models/application_model.rb +5 -0
  28. data/lib/scaffold/app/models/band.rb +7 -0
  29. data/lib/scaffold/app/models/note.rb +7 -0
  30. data/lib/scaffold/app/models/track.rb +20 -0
  31. data/lib/scaffold/app/models/user.rb +42 -0
  32. data/lib/scaffold/app/views/albums_controller/edit.html.erb +51 -0
  33. data/lib/scaffold/app/views/albums_controller/new.html.erb +50 -0
  34. data/lib/scaffold/app/views/albums_controller/show.html.erb +25 -0
  35. data/lib/scaffold/app/views/application.html.erb +41 -0
  36. data/lib/scaffold/app/views/bands_controller/edit.html.erb +16 -0
  37. data/lib/scaffold/app/views/bands_controller/index.html.erb +12 -0
  38. data/lib/scaffold/app/views/bands_controller/json.json.jbuilder +3 -0
  39. data/lib/scaffold/app/views/bands_controller/new.html.erb +15 -0
  40. data/lib/scaffold/app/views/bands_controller/show.html.erb +27 -0
  41. data/lib/scaffold/app/views/sessions_controller/new.html.erb +22 -0
  42. data/lib/scaffold/app/views/tracks_controller/edit.html.erb +60 -0
  43. data/lib/scaffold/app/views/tracks_controller/new.html.erb +59 -0
  44. data/lib/scaffold/app/views/tracks_controller/show.html.erb +56 -0
  45. data/lib/scaffold/app/views/users_controller/new.html.erb +22 -0
  46. data/lib/scaffold/app/views/users_controller/show.html.erb +4 -0
  47. data/lib/scaffold/bin/pry +13 -0
  48. data/lib/scaffold/bin/routes +10 -0
  49. data/lib/scaffold/bin/server +32 -0
  50. data/lib/scaffold/config/routes.rb +26 -0
  51. data/lib/scaffold/db/database.db +0 -0
  52. data/lib/scaffold/db/database.sql +48 -0
  53. data/lib/scaffold/lib/.DS_Store +0 -0
  54. data/lib/scaffold/lib/controller/controller_base.rb +183 -0
  55. data/lib/scaffold/lib/controller/controller_callbacks.rb +17 -0
  56. data/lib/scaffold/lib/controller/cookies/flash.rb +33 -0
  57. data/lib/scaffold/lib/controller/cookies/flash_now.rb +15 -0
  58. data/lib/scaffold/lib/controller/cookies/session.rb +29 -0
  59. data/lib/scaffold/lib/controller/strong_params.rb +40 -0
  60. data/lib/scaffold/lib/middleware/file_server.rb +42 -0
  61. data/lib/scaffold/lib/middleware/show_exceptions.rb +62 -0
  62. data/lib/scaffold/lib/middleware/static.rb +31 -0
  63. data/lib/scaffold/lib/middleware/templates/rescue.html.erb +49 -0
  64. data/lib/scaffold/lib/model/associations/assoc_options.rb +16 -0
  65. data/lib/scaffold/lib/model/associations/associatable.rb +120 -0
  66. data/lib/scaffold/lib/model/associations/belongs_to_options.rb +18 -0
  67. data/lib/scaffold/lib/model/associations/has_many_options.rb +17 -0
  68. data/lib/scaffold/lib/model/db_connection.rb +59 -0
  69. data/lib/scaffold/lib/model/model_base.rb +183 -0
  70. data/lib/scaffold/lib/model/model_callbacks.rb +46 -0
  71. data/lib/scaffold/lib/model/relations/relation.rb +151 -0
  72. data/lib/scaffold/lib/model/relations/searchable.rb +27 -0
  73. data/lib/scaffold/lib/model/validations/validations.rb +31 -0
  74. data/lib/scaffold/lib/model/validations/validator.rb +81 -0
  75. data/lib/scaffold/lib/router/route.rb +24 -0
  76. data/lib/scaffold/lib/router/router.rb +192 -0
  77. data/lib/scaffold/lib/utils/url_helpers.rb +96 -0
  78. data/lib/scaffold/public/main.css +165 -0
  79. data/rails_lite.gemspec +41 -0
  80. metadata +168 -0
@@ -0,0 +1,17 @@
1
+ module ControllerCallbacks
2
+ METHODS = [:index, :create, :new, :edit, :update, :show, :destroy]
3
+
4
+ def before_action(method, options = { only: METHODS, except: [] })
5
+ default = { only: METHODS, except: [] }
6
+ default.merge!(options)
7
+
8
+ names = default[:only] - default[:except]
9
+ names.each do |name|
10
+ m = instance_method(name)
11
+ define_method(name) do
12
+
13
+ m.bind(self).call unless send(method)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ require 'json'
2
+ require_relative 'flash_now'
3
+
4
+ class Flash
5
+ attr_reader :now
6
+
7
+ def initialize(req)
8
+ cookie = req.cookies['_rails_lite_app_flash']
9
+ if cookie
10
+ @now = FlashNow.new(JSON.parse(cookie))
11
+ else
12
+ @now = FlashNow.new
13
+ end
14
+
15
+ @cookie_data = {}
16
+ end
17
+
18
+ def [](key)
19
+ now[key.to_s] || cookie_data[key.to_s]
20
+ end
21
+
22
+ def []=(key, val)
23
+ cookie_data[key.to_s] = val
24
+ end
25
+
26
+ def store_flash(res)
27
+ cookie = { path: '/', value: JSON.generate(cookie_data) }
28
+ res.set_cookie('_rails_lite_app_flash', cookie)
29
+ end
30
+
31
+ private
32
+ attr_reader :cookie_data
33
+ end
@@ -0,0 +1,15 @@
1
+ class FlashNow
2
+ attr_reader :hash
3
+
4
+ def initialize(hash = {})
5
+ @hash = hash
6
+ end
7
+
8
+ def [](key)
9
+ hash[key.to_s]
10
+ end
11
+
12
+ def []=(key, val)
13
+ hash[key.to_s] = val
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ require 'json'
2
+
3
+ class Session
4
+
5
+ def initialize(req)
6
+ cookie = req.cookies['_rails_lite_app']
7
+ if cookie
8
+ @cookie_data = JSON.parse(cookie)
9
+ else
10
+ @cookie_data = {}
11
+ end
12
+ end
13
+
14
+ def [](key)
15
+ cookie_data[key.to_s]
16
+ end
17
+
18
+ def []=(key, val)
19
+ cookie_data[key.to_s] = val
20
+ end
21
+
22
+ def store_session(res)
23
+ cookie = { path: '/', value: JSON.generate(cookie_data) }
24
+ res.set_cookie('_rails_lite_app', cookie)
25
+ end
26
+
27
+ private
28
+ attr_reader :cookie_data
29
+ end
@@ -0,0 +1,40 @@
1
+ class StrongParams
2
+ attr_reader :params
3
+
4
+ def self.to_sym(hash)
5
+ sym_hash = {}
6
+ hash.each do |key, val|
7
+ sym_hash[key.to_sym] = (val.is_a?(Hash) ? to_sym(val) : val)
8
+ end
9
+
10
+ sym_hash
11
+ end
12
+
13
+ def self.new_syms(params)
14
+ StrongParams.new(StrongParams.to_sym(params))
15
+ end
16
+
17
+ def initialize(params)
18
+ @params = params
19
+ end
20
+
21
+ def require(class_name)
22
+ StrongParams.new(params[class_name])
23
+ end
24
+
25
+ def permit(*keys)
26
+ permited_params = {}
27
+ keys.each { |key| permited_params[key] = params[key] }
28
+ permited_params
29
+ end
30
+
31
+ def [](key)
32
+ params[key.to_sym]
33
+ end
34
+
35
+ private
36
+
37
+ def []=(key, val)
38
+ params[key.to_sym] = val
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ class FileServer
2
+
3
+ MIME_TYPES = {
4
+ '.txt' => 'text/plain',
5
+ '.jpg' => 'image/jpeg',
6
+ '.zip' => 'application/zip'
7
+ }
8
+
9
+ def initialize(root)
10
+ @root = root
11
+ end
12
+
13
+ def call(env)
14
+ res = Rack::Response.new
15
+ file_name = requested_file_name(env)
16
+
17
+ if File.exist?(file_name)
18
+ serve_file(file_name, res)
19
+ else
20
+ res.status = 404
21
+ res.write("File not found")
22
+ end
23
+ res
24
+ end
25
+
26
+ private
27
+
28
+ def serve_file(file_name, res)
29
+ extension = File.extname(file_name)
30
+ content_type = MIME_TYPES[extension]
31
+ file = File.read(file_name)
32
+ res["Content-type"] = content_type
33
+ res.write(file)
34
+ end
35
+
36
+ def requested_file_name(env)
37
+ req = Rack::Request.new(env)
38
+ path = req.path
39
+ dir = File.dirname(__FILE__)
40
+ File.join(dir, '..', '..', path)
41
+ end
42
+ end
@@ -0,0 +1,62 @@
1
+ require 'erb'
2
+
3
+ class ShowExceptions
4
+ attr_reader :app
5
+
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ app.call(env)
12
+ rescue Exception => e
13
+ render_exception(e)
14
+ end
15
+
16
+ private
17
+
18
+ def render_exception(e)
19
+ dir_path = File.dirname(__FILE__)
20
+ template_fname = File.join(dir_path, "templates", "rescue.html.erb")
21
+ template = File.read(template_fname)
22
+ body = ERB.new(template).result(binding)
23
+
24
+ ['500', {'Content-type' => 'text/html'}, [body]]
25
+ end
26
+
27
+ def error_source_file(e)
28
+ # stack_trace_top = e.backtrace.first.split(':')
29
+ stack_trace_top(e)[0]
30
+ end
31
+
32
+ def stack_trace_top(e)
33
+ e.backtrace.first.split(':')
34
+ end
35
+
36
+ def extract_formatted_source(e)
37
+ source_file_name = error_source_file(e)
38
+ source_line_num = source_line_num(e)
39
+ source_lines = extract_source(source_file_name)
40
+ format_source(source_lines, source_line_num)
41
+ end
42
+
43
+ def source_line_num(e)
44
+ stack_trace_top(e)[1].to_i
45
+ end
46
+
47
+ def formatted_source(file, source_line_num)
48
+ source_lines = extract_source(file)
49
+ format_source(source_lines, source_line_num)
50
+ end
51
+
52
+ def extract_source(file)
53
+ source_file = File.open(file, 'r')
54
+ source_file.readlines
55
+ end
56
+
57
+ def format_source(source_lines, source_line_num)
58
+ start = [0, source_line_num - 3].max
59
+ lines = source_lines[start..(start + 5)]
60
+ Hash[*(start+1..(lines.count + start)).zip(lines).flatten]
61
+ end
62
+ end
@@ -0,0 +1,31 @@
1
+ require_relative 'file_server'
2
+
3
+ class Static
4
+ attr_reader :app, :root, :file_server
5
+
6
+ def initialize(app)
7
+ @app = app
8
+ @root = :public
9
+ @file_server = FileServer.new(root)
10
+ end
11
+
12
+ def call(env)
13
+ req = Rack::Request.new(env)
14
+ path = req.path
15
+
16
+ if can_serve?(path)
17
+ res = file_server.call(env)
18
+ else
19
+ res = app.call(env)
20
+ end
21
+
22
+ res
23
+ end
24
+
25
+ private
26
+
27
+ def can_serve?(path)
28
+ path.index("/#{root}")
29
+ end
30
+ end
31
+
@@ -0,0 +1,49 @@
1
+ <head>
2
+ <style>
3
+ .header {
4
+ background: red;
5
+ }
6
+ .source-view {
7
+ border: 10px solid lightgray;
8
+ width: 800px;
9
+ }
10
+ .line {
11
+ white-space: pre-wrap;
12
+ }
13
+ .line-num {
14
+ background: lightgray
15
+ }
16
+ .line.error {
17
+ background: red;
18
+ }
19
+ </style>
20
+ </head>
21
+
22
+ <body>
23
+ <h2 class='header'><%=e.class%>: <%=e.message%></h2>
24
+
25
+ <h4>Extracted source (around line <b><%=source_line_num(e)%></b>):</h4>
26
+
27
+ <div class='source-view'>
28
+ <table cellpadding="0" cellspacing="0">
29
+ <% extract_formatted_source(e).each do |line_num, line| %>
30
+ <tr>
31
+ <td>
32
+ <pre class='line-num'><%=line_num %></pre>
33
+ </td>
34
+ <td>
35
+ <pre class='line
36
+ <%= 'error' if line_num == source_line_num(e)%>'><%= line %></pre>
37
+ </td>
38
+ </tr>
39
+ <% end %>
40
+ </table>
41
+ </div>
42
+ <h5><%= File.expand_path(error_source_file(e)) %></h5>
43
+
44
+ <h3>Stack trace</h3>
45
+ <% e.backtrace.each do |stack_line| %>
46
+ <%= stack_line %>
47
+ <br>
48
+ <% end %>
49
+ </body>
@@ -0,0 +1,16 @@
1
+ class AssocOptions
2
+ attr_accessor(
3
+ :foreign_key,
4
+ :class_name,
5
+ :primary_key,
6
+ :optional
7
+ )
8
+
9
+ def model_class
10
+ class_name.to_s.constantize
11
+ end
12
+
13
+ def table_name
14
+ model_class.table_name
15
+ end
16
+ end
@@ -0,0 +1,120 @@
1
+ require_relative 'has_many_options'
2
+ require_relative 'belongs_to_options'
3
+
4
+ module Associatable
5
+
6
+ def belongs_to(name, options = {})
7
+ options = BelongsToOptions.new(name, options)
8
+ assoc_options[name] = options
9
+
10
+ self.define_method(name) do
11
+ validations = self.class.validators
12
+ .map { |validator| [validator.attribute, validator.options[:presence]] }
13
+
14
+ unless options.optional || validations.include?([options.foreign_key, true])
15
+ self.class.validates options.foreign_key, presence: true
16
+ end
17
+
18
+ options.model_class.find(self.send(options.foreign_key))
19
+ end
20
+ end
21
+
22
+ def has_many(name, options = {})
23
+ if options[:through] && options[:source]
24
+ has_many_through(name, options[:through], options[:source])
25
+ else
26
+ options = HasManyOptions.new(name, self.name, options)
27
+ assoc_options[name] = options
28
+
29
+ self.define_method(name) do
30
+ options.model_class.where(options.foreign_key => self.send(options.primary_key)).parsed_query
31
+ end
32
+ end
33
+ end
34
+
35
+ def assoc_options
36
+ @assoc_options ||= {}
37
+ @assoc_options
38
+ end
39
+
40
+ def has_one(name, options)
41
+ through_name = options[:through]
42
+ source_name = options[:source]
43
+
44
+ define_method(name) do
45
+ through_options = self.class.assoc_options[through_name]
46
+ source_options = through_options.model_class.assoc_options[source_name]
47
+
48
+ through_table = through_options.table_name
49
+ through_pk = through_options.primary_key
50
+ through_fk = through_options.foreign_key
51
+
52
+ source_table = source_options.table_name
53
+ source_pk = source_options.primary_key
54
+ source_fk = source_options.foreign_key
55
+
56
+ key_val = self.send(through_fk)
57
+ results = DBConnection.execute(<<-SQL, key_val)
58
+ SELECT
59
+ #{source_table}.*
60
+ FROM
61
+ #{through_table}
62
+ JOIN
63
+ #{source_table}
64
+ ON
65
+ #{through_table}.#{source_fk} = #{source_table}.#{source_pk}
66
+ WHERE
67
+ #{through_table}.#{through_pk} = ?
68
+ SQL
69
+
70
+ source_options.model_class.parse_all(results).first
71
+ end
72
+ end
73
+
74
+ def has_many_through(name, through_name, source_name)
75
+ define_method(name) do
76
+ through_options = self.class.assoc_options[through_name]
77
+ source_options = through_options.model_class.assoc_options[source_name]
78
+
79
+ if through_options.is_a?(BelongsToOptions)
80
+ through_self_id = through_options.primary_key
81
+ self_through_id = through_options.foreign_key
82
+ else
83
+ through_self_id = through_options.foreign_key
84
+ self_through_id = through_options.primary_key
85
+ end
86
+
87
+ if source_options.is_a?(BelongsToOptions)
88
+ through_source_id = source_options.foreign_key
89
+ source_through_id = source_options.primary_key
90
+ else
91
+ through_source_id = source_options.primary_key
92
+ source_through_id = source_options.foreign_key
93
+ end
94
+
95
+ through_table = through_options.table_name
96
+ source_table = source_options.table_name
97
+
98
+ key_val = self.send(:id)
99
+ results = DBConnection.execute(<<-SQL, key_val)
100
+ SELECT
101
+ #{source_table}.*
102
+ FROM
103
+ #{through_table}
104
+ JOIN
105
+ #{source_table}
106
+ ON
107
+ #{through_table}.#{through_source_id} = #{source_table}.#{source_through_id}
108
+ JOIN
109
+ #{self.class.table_name}
110
+ ON
111
+ #{self.class.table_name}.#{self_through_id} = #{through_table}.#{through_self_id}
112
+ WHERE
113
+ #{self.class.table_name}.id = ?
114
+ SQL
115
+
116
+ source_options.model_class.parse_all(results)
117
+ end
118
+ end
119
+
120
+ end