railz_lite 0.2.3 → 0.2.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 582dcdab7ea03cf05ecf37d4ead94d5a2f3cc95ad1b89db6b9e93b98d7732e67
4
- data.tar.gz: 507d329e1fc346cf7ede9892afb1d81a85a1bb1bc14d5c8f8eb6632a57936ae0
3
+ metadata.gz: aff7d4d6f62b5eee1669eef62895d5170f55a5d3241db179191a690bb9dc26f1
4
+ data.tar.gz: 2ab955ed52a300dcf3c614e1a0c4bcbf9f0e8ef81613e2bae9aa7324f08980db
5
5
  SHA512:
6
- metadata.gz: 4d480c89f27783d171aea9fe67862080b108a8e9b2c8320e99f687119a6b5b72b189a6dede703349152c843ad5bd6a0c03fa1bc4dac3377b454b5bb3b3c2e40c
7
- data.tar.gz: b6927c1850fe7bf1e4db6b29fbd13a9105eed8c4b03d0383a15f0bc09ef6fa1691b0a8483413a09c4147aaf08dd37b6af5262d37d88a15adba4556091806d415
6
+ metadata.gz: 0b6b915059bf4ed2ce158b1b6eef235fb742cac8fa3a416bdd88df3d4183510c632021181cec277551e86b3142253bf09732d6aed1954cf1c92b08c820c02b2b
7
+ data.tar.gz: 7118f1462ba3e8d772d4e64bfea0304f8cae53cebd073d9b38c97e318a3129f42bf5560369c96f7edc7b916b9a1e77e22d7d713c18ed31526a189f080f00c25b
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')
@@ -1,11 +1,11 @@
1
1
  require 'rack'
2
2
 
3
3
  class Static
4
- attr_reader :app, :file_server, :root
4
+ attr_reader :app, :file_server, :root_paths
5
5
 
6
6
  def initialize(app)
7
7
  @app = app
8
- @root = 'public'
8
+ @root_paths = ['public', 'assets']
9
9
  @file_server = FileServer.new
10
10
  end
11
11
 
@@ -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
- 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
 
@@ -32,12 +36,14 @@ class FileServer
32
36
  MIME_TYPES = {
33
37
  '.txt' => 'text/plain',
34
38
  '.jpg' => 'image/jpeg',
35
- '.zip' => 'application/zip'
39
+ '.zip' => 'application/zip',
40
+ '.css' => 'text/css',
41
+ '.js' => 'text/javascript'
36
42
  }
37
43
 
38
- def call(env)
44
+ def call(env, asset_dir)
39
45
  res = Rack::Response.new
40
- file_name = requested_file_name(env)
46
+ file_name = requested_file_name(env, asset_dir)
41
47
 
42
48
  if File.exist?(file_name)
43
49
  serve_file(file_name, res)
@@ -58,10 +64,11 @@ class FileServer
58
64
  res.write(file)
59
65
  end
60
66
 
61
- def requested_file_name(env)
67
+ def requested_file_name(env, asset_dir)
62
68
  req = Rack::Request.new(env)
63
- path = req.path
64
69
  dir = Dir.pwd
65
- File.join(dir, path)
70
+ path = req.path
71
+ filename = File.basename(path)
72
+ File.join(dir, asset_dir, filename)
66
73
  end
67
74
  end
@@ -29,7 +29,10 @@ module RailzLite
29
29
  def setup_views
30
30
  template('welcome_view.index.html.erb', "#{project_name}/views/welcome/index.html.erb")
31
31
  template('application.html.erb', "#{project_name}/views/application/application.html.erb")
32
- create_file("#{project_name}/views/application/application.css")
32
+ end
33
+
34
+ def add_assets
35
+ create_file("#{project_name}/assets/application.css")
33
36
  end
34
37
 
35
38
  def add_public
@@ -2,7 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>App Title</title>
5
- <link rel="stylesheet" href="application.css">
5
+ <link rel="stylesheet" href="assets/application.css">
6
6
  </head>
7
7
  <body>
8
8
  <%= yield %>
@@ -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
 
@@ -6,7 +6,7 @@
6
6
  ">
7
7
  <h1>Welcome To Railz Lite!</h1>
8
8
 
9
- <img src="../public/winter_fox_large.jpg" />
9
+ <img src="public/winter_fox_large.jpg" />
10
10
 
11
11
  <h3>For guidelines on how to build an app with this framework see <a href="https://github.com/bryanqb07/railz_lite">here</a>.</h3>
12
12
  </div>
@@ -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)
@@ -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
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
@@ -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.3"
4
+ VERSION = "0.2.8"
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.3
4
+ version: 0.2.8
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