railz_lite 0.2.4 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 432d92521ed230f18f0aa428cba72c1b37450b1ac9c8313bd12e9d908e204d45
4
- data.tar.gz: 48d357cfd3f24cd5ea0f264ce8ccf8eb67d562e8b2cf040270b1f81ef8391cb6
3
+ metadata.gz: 9da64c4f69092de235e6a5c3602a2e6f8fc6be5ac6e3fb88b6621246e8dc3bc4
4
+ data.tar.gz: 8c44980e47498b3be67ce7d894d3fe2a7eb8c18335e7fe68b56b74f198fe0cd8
5
5
  SHA512:
6
- metadata.gz: a2f65cd17bbe02aa69cb5a27414c4d8e00ead1cc21a77266947f3d7ffb36387fc2f0728957eea01f433a624d8d74eb9ae9d5c90a12b9545b71fb5e1821ab649d
7
- data.tar.gz: 3a3e3ce5b8a38e77f5aaff03c978b7ca38fe16019f0653e495c844dc03c74e28205a276da3629a31f3c25aa92ca073a3387d00c038324b8e9a349c700eee6452
6
+ metadata.gz: e3b0ffdd0f01715fadc5b5d1a9d1c9cefc5b01295d5b46a1b05c9ff4794b0dc8cd789b96adec8a556602835a7a7cbac57fa38bff8ae18ceef4220afe6c7c3802
7
+ data.tar.gz: 56f7965f2bd969c47924b173933566a147d5718d0495d32dc9041afb5dd4c6b44a33d480faee63c972e4ebd03336f4731591d167b6e13bbaf56d054dc1f19dce
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+ Gemfile.lock
data/README.md CHANGED
@@ -1,8 +1,30 @@
1
1
  # RailzLite
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/railz_lite`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ A lite-weight Ruby gem inspired by Ruby on Rails.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ To get started with a new project, execute the command
6
+
7
+ ```
8
+ $ railz_lite new MyWebApp
9
+ ```
10
+
11
+ This will generate controllers/, views/, models/, and other necessary directories.
12
+
13
+ After changing into the root project directory, run the server command to get the Rack server running
14
+
15
+ ```
16
+ $ cd MyWebApp && railz_lite server
17
+ ```
18
+
19
+ To create a new sqlite3 database file, write your desired SQL code in app.sql then execute
20
+
21
+ ```
22
+ $ railz_lite reset_db
23
+ ```
24
+
25
+ An example web app can be found at the following repo:
26
+
27
+ https://github.com/bryanqb07/trash_films
6
28
 
7
29
  ## Installation
8
30
 
@@ -22,7 +44,86 @@ Or install it yourself as:
22
44
 
23
45
  ## Usage
24
46
 
25
- TODO: Write usage instructions here
47
+ ### Controllers
48
+
49
+ All controllers should inherit from the RailzLite::ControllerBase class. An example is as follows:
50
+
51
+ ```ruby
52
+ class FilmsController < RailzLite::ControllerBase
53
+ def index
54
+ @films = Film.all
55
+ end
56
+
57
+ def show
58
+ @film = Film.find(params['id'])
59
+ end
60
+ end
61
+ ```
62
+
63
+ ### Models
64
+
65
+ Models inherit from RailzLite::SQLObject. Please note that any class declaration must be ended with a finalize! statement
66
+
67
+ ```ruby
68
+ class Film < RailzLite::SQLObject
69
+ has_many :reviews, foreign_key: :film_id
70
+ def average_rating
71
+ ratings = reviews.map(&:rating)
72
+ return 0 if ratings.empty?
73
+
74
+ ratings.reduce(:+) / reviews.length
75
+ end
76
+ finalize!
77
+ end
78
+ ```
79
+
80
+ ### Views
81
+
82
+ Views use the rails convention for path-resolution. Also, they are required to end in a .erb extension, even if no embedded ruby is used.
83
+
84
+ ex. FilmsController#index corresponds to /views/films/index.html.erb
85
+
86
+ ### Server + Routes
87
+
88
+ The server file can be found in config/server.
89
+
90
+ Routes are configured in the following structure
91
+
92
+ ```
93
+ http_method, regex to match route, controller, action
94
+ ```
95
+
96
+ Below is an example route config:
97
+ ```ruby
98
+ router.draw do
99
+ # add routes here
100
+ get Regexp.new('^/films$'), FilmsController, :index
101
+ get Regexp.new("^/films/(?<id>\\d+)$"), FilmsController, :show
102
+
103
+ get Regexp.new("^/films/(?<film_id>\\d+)/reviews/new$"), ReviewsController, :new
104
+ post Regexp.new("^/films/(?<film_id>\\d+)/reviews$"), ReviewsController, :create
105
+ end
106
+ ```
107
+
108
+ ### Assets
109
+
110
+ Assets are served via the static middleware included within the framework. The default load paths for assets are "/public" and "/assets".
111
+ Any file within these two folders can be accessed in the app by specifying the root folder + the file name.
112
+
113
+ ex.) MyProject/public/balloons.jpg
114
+
115
+ ```html
116
+ <h1>Congratulations!!! <img src="public/balloons.jpg" /></h1>
117
+ ```
118
+
119
+ ex.) MyProject/assets/home.css
120
+
121
+ ```html
122
+ <head>
123
+ <title>My Project</title>
124
+ <link rel="stylesheet" href="assets/application.css">
125
+ </head>
126
+ ```
26
127
 
27
128
  ## Development
28
129
 
@@ -1,6 +1,7 @@
1
1
  require 'active_support'
2
2
  require 'active_support/core_ext'
3
3
  require 'erb'
4
+ require 'loofah'
4
5
  require_relative './session'
5
6
  require_relative './flash'
6
7
 
@@ -62,7 +63,8 @@ module RailzLite
62
63
  layout.def_method(LayoutRenderer, 'render') # dummy method used so that blocks can be passed to ERB result
63
64
 
64
65
  result = LayoutRenderer.new.render do
65
- inner.result(binding)
66
+ inner_html = inner.result(binding)
67
+ Loofah.fragment(inner_html).scrub!(:prune).to_s # prevent non-safe html from being executed
66
68
  end
67
69
 
68
70
  render_content(result, 'text/html')
@@ -13,17 +13,21 @@ class Static
13
13
  req = Rack::Request.new(env)
14
14
  path = req.path
15
15
 
16
- if can_match?(path)
17
- file_server.call(env)
18
- else
16
+ asset_dir = get_asset_dir(path)
17
+
18
+ if asset_dir.nil?
19
19
  app.call(env)
20
+ else
21
+ file_server.call(env, asset_dir)
20
22
  end
21
23
  end
22
24
 
23
25
  private
24
26
 
25
- def can_match?(path)
26
- root_paths.any? { |root| path.index("/#{root}") }
27
+ # return the root directory of asset request ex => 'films/assets/app.css' would return 'assets'
28
+ def get_asset_dir(path)
29
+ root_paths.each { |root| return root if path.include?(root) }
30
+ nil
27
31
  end
28
32
  end
29
33
 
@@ -37,9 +41,9 @@ class FileServer
37
41
  '.js' => 'text/javascript'
38
42
  }
39
43
 
40
- def call(env)
44
+ def call(env, asset_dir)
41
45
  res = Rack::Response.new
42
- file_name = requested_file_name(env)
46
+ file_name = requested_file_name(env, asset_dir)
43
47
 
44
48
  if File.exist?(file_name)
45
49
  serve_file(file_name, res)
@@ -60,10 +64,11 @@ class FileServer
60
64
  res.write(file)
61
65
  end
62
66
 
63
- def requested_file_name(env)
67
+ def requested_file_name(env, asset_dir)
64
68
  req = Rack::Request.new(env)
65
- path = req.path
66
69
  dir = Dir.pwd
67
- File.join(dir, path)
70
+ path = req.path
71
+ filename = File.basename(path)
72
+ File.join(dir, asset_dir, filename)
68
73
  end
69
74
  end
@@ -24,7 +24,7 @@ end
24
24
 
25
25
  app = Rack::Builder.new do
26
26
  use ShowExceptions # generates helpful error messages
27
- use Static # serves static assets from /public
27
+ use Static # serves static assets from /public and /assets
28
28
  run app
29
29
  end.to_app
30
30
 
@@ -1,4 +1,5 @@
1
- require 'sqlite3'
1
+ require_relative 'wrappers/pg_wrapper'
2
+ require_relative 'wrappers/sqlite3_wrapper'
2
3
 
3
4
  class DBConnection
4
5
  PRINT_QUERIES = ENV['PRINT_QUERIES'] == 'true'
@@ -10,22 +11,27 @@ class DBConnection
10
11
  DBConnection.open(DB_FILE)
11
12
  end
12
13
 
13
- def self.open(db_file_name)
14
- @db = SQLite3::Database.new(db_file_name)
15
- @db.results_as_hash = true
16
- @db.type_translation = true
14
+ def self.open(db_name) # for sqlite3 we need file.db, for postgresql we need database name
15
+ db_uri = ENV['DATABASE_URL']
16
+
17
+ @db = db_uri.nil? ? SQLite3Wrapper.new(db_name) : PGWrapper.new(db_uri)
17
18
 
18
19
  @db
19
20
  end
20
21
 
21
22
  def self.reset
22
- commands = [
23
- "rm '#{DB_FILE}'",
24
- "cat '#{SQL_FILE}' | sqlite3 '#{DB_FILE}'"
25
- ]
23
+ db_uri = ENV['DATABASE_URL']
26
24
 
27
- commands.each { |command| `#{command}` }
28
- DBConnection.open(DB_FILE)
25
+ if db_uri.nil? # sqlite
26
+ commands = ["rm '#{DB_FILE}'", "cat '#{SQL_FILE}' | sqlite3 '#{DB_FILE}'"]
27
+
28
+ commands.each { |command| `#{command}` }
29
+ DBConnection.open(DB_FILE)
30
+ else # postgres
31
+ DBConnection.open(db_uri)
32
+ sql = File.read(SQL_FILE)
33
+ instance.execute(sql)
34
+ end
29
35
  end
30
36
 
31
37
  def self.instance
@@ -34,20 +40,25 @@ class DBConnection
34
40
  @db
35
41
  end
36
42
 
43
+ # results hash of results [{id: 1, name: 'Bob'}...]
37
44
  def self.execute(*args)
38
45
  print_query(*args)
39
46
  instance.execute(*args)
40
47
  end
41
48
 
49
+ # returns result with header fields [['id', 'name'...], { id: 1, name: 'Bob' ... }
42
50
  def self.execute2(*args)
43
51
  print_query(*args)
44
52
  instance.execute2(*args)
45
53
  end
46
54
 
47
- def self.last_insert_row_id
48
- instance.last_insert_row_id
55
+ # used to insert data into tables
56
+ def self.insert(*args)
57
+ print_query(*args)
58
+ instance.insert(*args)
49
59
  end
50
60
 
61
+
51
62
  private
52
63
 
53
64
  def self.print_query(query, *interpolation_args)
@@ -13,7 +13,7 @@ module RailzLite
13
13
  SELECT
14
14
  *
15
15
  FROM
16
- #{table_name}
16
+ #{table_name};
17
17
  SQL
18
18
  @columns.first.map(&:to_sym)
19
19
  end
@@ -42,7 +42,7 @@ module RailzLite
42
42
  SELECT
43
43
  *
44
44
  FROM
45
- #{table_name}
45
+ #{table_name};
46
46
  SQL
47
47
  parse_all(results)
48
48
  end
@@ -58,7 +58,7 @@ module RailzLite
58
58
  FROM
59
59
  #{table_name}
60
60
  WHERE
61
- ID = ?
61
+ ID = ?;
62
62
  SQL
63
63
 
64
64
  return nil if target.empty?
@@ -82,13 +82,13 @@ module RailzLite
82
82
  end
83
83
 
84
84
  def insert
85
- DBConnection.execute(<<-SQL, *attribute_values)
85
+ last_row_id = DBConnection.insert(<<-SQL, *attribute_values)
86
86
  INSERT INTO
87
87
  #{self.class.table_name}(#{self.class.columns.join(',')})
88
88
  VALUES
89
- (#{(["?"] * attribute_values.length).join(',')})
89
+ (#{(["?"] * attribute_values.length).join(',')});
90
90
  SQL
91
- self.id = DBConnection.last_insert_row_id
91
+ self.id = last_row_id
92
92
  end
93
93
 
94
94
  def update
@@ -98,7 +98,7 @@ module RailzLite
98
98
  SET
99
99
  #{self.class.columns.map { |attr_name| "#{attr_name}=?"}.join(',')}
100
100
  WHERE
101
- id = ?
101
+ id = ?;
102
102
  SQL
103
103
  end
104
104
 
@@ -0,0 +1,46 @@
1
+ require 'pg'
2
+
3
+ class PGWrapper
4
+ def initialize(db_uri)
5
+ @db = PG::Connection.new(db_uri)
6
+ @db.type_map_for_results = PG::BasicTypeMapForResults.new(@db) # converts pgsql strings to ruby type
7
+ @db
8
+ end
9
+
10
+ def execute(sql, *args)
11
+ converted_sql = convert_escaped_question_marks(sql)
12
+ @db.exec_params(converted_sql, args).to_a
13
+ end
14
+
15
+ def execute2(sql, *args)
16
+ converted_sql = convert_escaped_question_marks(sql)
17
+ result = @db.exec_params(converted_sql, args)
18
+ [result.fields, result.to_a]
19
+ end
20
+
21
+ def insert(sql, *args)
22
+ sql.insert(sql.index(';'), ' RETURNING ID')
23
+ converted_sql = convert_escaped_question_marks(sql)
24
+ result = @db.exec_params(converted_sql, *args)
25
+ result.to_a.first['id']
26
+ end
27
+
28
+
29
+ private
30
+
31
+ # for pgsql, SELECT * FROM films WHERE x = ?; must be converted to SELECT * FROM films WHERE x = $1;
32
+ def convert_escaped_question_marks(sql)
33
+ converted_sql = ""
34
+ index = 1
35
+ sql.each_char do |char|
36
+ if char == '?'
37
+ converted_sql += "$#{index}"
38
+ index += 1
39
+ else
40
+ converted_sql += char
41
+ end
42
+ end
43
+ converted_sql
44
+ end
45
+
46
+ end
@@ -0,0 +1,30 @@
1
+ require 'sqlite3'
2
+
3
+ class SQLite3Wrapper
4
+ def initialize(db_file_name)
5
+ @db = SQLite3::Database.new(db_file_name)
6
+ @db.results_as_hash = true
7
+ @db.type_translation = true
8
+
9
+ @db
10
+ end
11
+
12
+ def execute(*args)
13
+ @db.execute(*args)
14
+ end
15
+
16
+ def execute2(*args)
17
+ @db.execute2(*args)
18
+ end
19
+
20
+ def insert(*args)
21
+ execute(*args)
22
+ last_insert_row_id
23
+ end
24
+
25
+ private
26
+
27
+ def self.last_insert_row_id
28
+ @db.last_insert_row_id
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailzLite
4
- VERSION = "0.2.4"
4
+ VERSION = "0.2.9"
5
5
  end
data/railz_lite.gemspec CHANGED
@@ -29,11 +29,13 @@ Gem::Specification.new do |spec|
29
29
  spec.require_paths = ["lib"]
30
30
 
31
31
  # Uncomment to register a new dependency of your gem
32
- spec.add_dependency "rack"
33
- spec.add_dependency "activesupport"
34
- spec.add_dependency "puma"
35
- spec.add_dependency "sqlite3"
36
- spec.add_dependency "thor"
32
+ spec.add_dependency "rack", '~> 2.2.3'
33
+ spec.add_dependency "activesupport", '~> 6.1.4'
34
+ spec.add_dependency "puma", '~> 5.3.2'
35
+ spec.add_dependency "sqlite3", '~> 1.4.2'
36
+ spec.add_dependency "pg", '~> 1.2.3'
37
+ spec.add_dependency "thor", '~> 1.1.0'
38
+ spec.add_dependency "loofah", '~> 2.10.0'
37
39
 
38
40
 
39
41
  spec.add_development_dependency "byebug"
metadata CHANGED
@@ -1,85 +1,113 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: railz_lite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - bryan lynch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-03 00:00:00.000000000 Z
11
+ date: 2021-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 2.2.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 2.2.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 6.1.4
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 6.1.4
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: puma
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 5.3.2
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 5.3.2
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: sqlite3
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 1.4.2
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 1.4.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: pg
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.2.3
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.2.3
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: thor
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - ">="
87
+ - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '0'
89
+ version: 1.1.0
76
90
  type: :runtime
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">="
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '0'
96
+ version: 1.1.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: loofah
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 2.10.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 2.10.0
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: byebug
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +206,8 @@ files:
178
206
  - lib/railz_lite/models/searchable.rb
179
207
  - lib/railz_lite/models/sql_object.rb
180
208
  - lib/railz_lite/models/validatable.rb
209
+ - lib/railz_lite/models/wrappers/pg_wrapper.rb
210
+ - lib/railz_lite/models/wrappers/sqlite3_wrapper.rb
181
211
  - lib/railz_lite/version.rb
182
212
  - railz_lite.gemspec
183
213
  homepage: https://github.com/bryanqb07/my_rails