bezel-app 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitconfig +2 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +44 -0
- data/bezel-app.gemspec +39 -0
- data/bin/bezel-app +130 -0
- data/lib/bezel.rb +15 -0
- data/lib/bezelrecord_base/associatable.rb +114 -0
- data/lib/bezelrecord_base/bezelrecord_base.rb +122 -0
- data/lib/bezelrecord_base/searchable.rb +18 -0
- data/lib/controller_base.rb +78 -0
- data/lib/db_connection.rb +117 -0
- data/lib/flash.rb +29 -0
- data/lib/router.rb +60 -0
- data/lib/session.rb +24 -0
- data/lib/show_exceptions.rb +24 -0
- data/lib/static_assets.rb +36 -0
- data/template/app/controllers/application_controller.rb +3 -0
- data/template/config/routes.rb +9 -0
- data/template/db/seeds.rb +7 -0
- data/views/cats_controller/index.html.erb +3 -0
- data/views/cats_controller/new.html.erb +11 -0
- data/views/dogs_controller/index.html.erb +8 -0
- data/views/dogs_controller/new.html.erb +19 -0
- data/views/dogs_controller/show.html.erb +1 -0
- data/views/dummy_controller/index.html.erb +0 -0
- data/views/my_controller/counting_show.html.erb +1 -0
- data/views/my_controller/show.html.erb +1 -0
- metadata +214 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c2182514434b0eb19d341fb9ea1724aae0bbfd59
|
4
|
+
data.tar.gz: 019978d0a49e4acdf84fb2c28cddc052dc8ef04d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ba1fa4d2d752a193874a38dd91e09e507a87e438a04d49e41ff7322277b0226aca41adec8c94164d9816729b15d28ffeb75bfa265ed9db0842b595ad163afa4f
|
7
|
+
data.tar.gz: 34be517b8c9bd2c94641cb38b8ffa374991be236bb2bbc01ca970055ae8370ba2b6d4a46b27c58a2aa7a7d347806b07a41a4a0bdcae3c1369653f0a703d7fc8f
|
data/.gitconfig
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (4.2.7.1)
|
5
|
+
i18n (~> 0.7)
|
6
|
+
json (~> 1.7, >= 1.7.7)
|
7
|
+
minitest (~> 5.1)
|
8
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
9
|
+
tzinfo (~> 1.1)
|
10
|
+
byebug (9.0.5)
|
11
|
+
diff-lcs (1.2.5)
|
12
|
+
i18n (0.7.0)
|
13
|
+
json (1.8.3)
|
14
|
+
minitest (5.9.0)
|
15
|
+
rack (1.6.4)
|
16
|
+
rspec (3.1.0)
|
17
|
+
rspec-core (~> 3.1.0)
|
18
|
+
rspec-expectations (~> 3.1.0)
|
19
|
+
rspec-mocks (~> 3.1.0)
|
20
|
+
rspec-core (3.1.7)
|
21
|
+
rspec-support (~> 3.1.0)
|
22
|
+
rspec-expectations (3.1.2)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.1.0)
|
25
|
+
rspec-mocks (3.1.3)
|
26
|
+
rspec-support (~> 3.1.0)
|
27
|
+
rspec-support (3.1.2)
|
28
|
+
thor (0.19.1)
|
29
|
+
thread_safe (0.3.5)
|
30
|
+
tzinfo (1.2.2)
|
31
|
+
thread_safe (~> 0.1)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
activesupport (~> 4.2)
|
38
|
+
byebug
|
39
|
+
rack (~> 1.6.4)
|
40
|
+
rspec (~> 3.1.0)
|
41
|
+
thor
|
42
|
+
|
43
|
+
BUNDLED WITH
|
44
|
+
1.12.5
|
data/bezel-app.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'bezel-app'
|
8
|
+
spec.version = '0.0.1'
|
9
|
+
spec.authors = ['Keith Thompson']
|
10
|
+
spec.email = ['KeithM_Thompson@outlook.com']
|
11
|
+
|
12
|
+
spec.summary = %q(A simple ORM and MVC inspired by Rails.)
|
13
|
+
spec.description =
|
14
|
+
%q(Replicate much of the core functionality of rails.)
|
15
|
+
spec.homepage = 'http://github.com/keithm-thompson/Bezel'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
19
|
+
# delete this section to allow pushing this gem to any host.
|
20
|
+
if spec.respond_to?(:metadata)
|
21
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
22
|
+
else
|
23
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
24
|
+
end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
|
+
spec.executables = 'bezel-app'
|
28
|
+
|
29
|
+
spec.add_runtime_dependency 'thor', '~> 0.19'
|
30
|
+
spec.add_runtime_dependency 'pg', '~> 0.18'
|
31
|
+
spec.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.5.1'
|
32
|
+
spec.add_runtime_dependency 'pry', '~> 0.10.3'
|
33
|
+
spec.add_runtime_dependency 'rack', '~> 1.6', '>= 1.6.4'
|
34
|
+
spec.add_runtime_dependency 'fileutils', '~> 0.7'
|
35
|
+
|
36
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
37
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
38
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
39
|
+
end
|
data/bin/bezel-app
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'thor'
|
5
|
+
|
6
|
+
class Generate < Thor
|
7
|
+
desc 'model <name>', 'generate a model with the specified name.'
|
8
|
+
def model(name)
|
9
|
+
m_name = name.capitalize
|
10
|
+
|
11
|
+
# Writes model file
|
12
|
+
File.open("./app/models/#{m_name.downcase}.rb", 'w') do |f|
|
13
|
+
f.write("class #{m_name} < Bezel::BezelrecordBase\n\n")
|
14
|
+
f.write("end\n")
|
15
|
+
f.write("#{m_name}.finalize!")
|
16
|
+
end
|
17
|
+
migration("Create#{m_name}")
|
18
|
+
puts "#{m_name} model created"
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'controller <name>', 'generate a controller with the specified name.'
|
22
|
+
def controller(name)
|
23
|
+
c_name = name.capitalize
|
24
|
+
|
25
|
+
# Writes controller file
|
26
|
+
File.open("./app/controllers/#{c_name.downcase}.rb", 'w') do |f|
|
27
|
+
f.write("class #{c_name}Controller < Bezel::ControllerBase\n\n")
|
28
|
+
f.write('end')
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates empty views directory
|
32
|
+
Dir.mkdir "./app/views/#{c_name.downcase}_controller"
|
33
|
+
puts "#{c_name} controller created"
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'migration <name>', 'generates an empty sql file with a filename of the specified <name> appended to a timestamp'
|
37
|
+
def migration(name)
|
38
|
+
# Create a timestamp
|
39
|
+
ts = Time.now.to_s.split(' ').take(2).join('').split('').map(&:to_i).join
|
40
|
+
require 'active_support/inflector'
|
41
|
+
filename = "#{ts}__#{name.underscore.downcase}"
|
42
|
+
|
43
|
+
# Create the migration file
|
44
|
+
File.open("./db/migrate/#{filename}.sql", 'w') do |f|
|
45
|
+
f.write "CREATE TABLE IF NOT EXISTS #{name} (\n"
|
46
|
+
f.write "\tid SERIAL PRIMARY KEY,\n"
|
47
|
+
f.write "\tname VARCHAR(255) NOT NULL"
|
48
|
+
f.write ');'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Top-level command for manipulating the database file.
|
54
|
+
class Db < Thor
|
55
|
+
desc 'create', 'creates the DB'
|
56
|
+
def create
|
57
|
+
require_relative '../lib/db_connection'
|
58
|
+
Bezel::DBConnection.reset
|
59
|
+
puts 'db created!'
|
60
|
+
end
|
61
|
+
|
62
|
+
desc 'migrate', 'runs pending migrations'
|
63
|
+
def migrate
|
64
|
+
# Creates Version table if necessary,
|
65
|
+
# then runs needed migrations in order.
|
66
|
+
require_relative '../lib/db_connection'
|
67
|
+
Bezel::DBConnection.migrate
|
68
|
+
puts 'migrated!'
|
69
|
+
end
|
70
|
+
|
71
|
+
desc 'seed', 'seeds the DB'
|
72
|
+
def seed
|
73
|
+
require_relative '../lib/bezel'
|
74
|
+
Bezel::Seed.populate
|
75
|
+
puts 'db seeded!'
|
76
|
+
end
|
77
|
+
|
78
|
+
desc 'reset', 'resets the DB and seeds it'
|
79
|
+
def reset
|
80
|
+
create
|
81
|
+
migrate
|
82
|
+
seed
|
83
|
+
puts 'db reset!'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class CLI < Thor
|
88
|
+
register(Generate, 'generate', 'generate <command>', 'Generates a model or controller.')
|
89
|
+
register(Db, 'db', 'db <command>', 'Accesses commands for the DB.')
|
90
|
+
|
91
|
+
desc 'g', 'alias of generate subcommand'
|
92
|
+
subcommand 'g', Generate
|
93
|
+
|
94
|
+
desc 'server', 'starts the Bezel server'
|
95
|
+
def server
|
96
|
+
require_relative '../lib/bezel'
|
97
|
+
Bezel::ServerConnection.start
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'new', 'creates a new Bezel app'
|
101
|
+
def new(name)
|
102
|
+
Dir.mkdir "./#{name}"
|
103
|
+
Dir.mkdir "./#{name}/config"
|
104
|
+
|
105
|
+
File.open("./#{name}/config/database.yml", 'w') do |f|
|
106
|
+
f.write("database: #{name}")
|
107
|
+
end
|
108
|
+
|
109
|
+
Dir.mkdir "./#{name}/app"
|
110
|
+
Dir.mkdir "./#{name}/app/models"
|
111
|
+
Dir.mkdir "./#{name}/app/views"
|
112
|
+
Dir.mkdir "./#{name}/app/controllers"
|
113
|
+
File.open("./#{name}/app/controllers/application_controller.rb", 'w') do |f|
|
114
|
+
f.write File.read(File.expand_path('../../template/app/controllers/application_controller.rb', __FILE__))
|
115
|
+
end
|
116
|
+
File.open("./#{name}/config/routes.rb", 'w') do |f|
|
117
|
+
f.write File.read(File.expand_path('../../template/config/routes.rb', __FILE__))
|
118
|
+
end
|
119
|
+
Dir.mkdir "./#{name}/db"
|
120
|
+
Dir.mkdir "./#{name}/db/migrate"
|
121
|
+
File.open("./#{name}/db/seeds.rb", 'w') do |f|
|
122
|
+
f.write File.read(File.expand_path('../../template/db/seeds.rb', __FILE__))
|
123
|
+
end
|
124
|
+
File.open("./#{name}/Gemfile", 'w') do |f|
|
125
|
+
f.write File.read(File.expand_path('../../template/Gemfile', __FILE__))
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
CLI.start(ARGV)
|
data/lib/bezel.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Bezel
|
2
|
+
end
|
3
|
+
|
4
|
+
require_relative 'bezelrecord_base/bezelrecord_base'
|
5
|
+
require_relative 'controller_base'
|
6
|
+
|
7
|
+
Dir.glob('./app/models/*.rb') { |file| require file }
|
8
|
+
Dir.glob('./app/controllers/*.rb') { |file| require file }
|
9
|
+
|
10
|
+
require './db/seeds'
|
11
|
+
|
12
|
+
require_relative 'db_connection'
|
13
|
+
require_relative 'router'
|
14
|
+
require_relative 'server_connection'
|
15
|
+
require_relative 'session'
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require_relative 'searchable'
|
2
|
+
require 'active_support/inflector'
|
3
|
+
|
4
|
+
class AssocOptions
|
5
|
+
attr_accessor(
|
6
|
+
:foreign_key,
|
7
|
+
:class_name,
|
8
|
+
:primary_key
|
9
|
+
)
|
10
|
+
|
11
|
+
def model_class
|
12
|
+
@model_class = class_name.constantize
|
13
|
+
end
|
14
|
+
|
15
|
+
def table_name
|
16
|
+
@table_name = model_class.table_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class BelongsToOptions < AssocOptions
|
21
|
+
def initialize(name, options = {})
|
22
|
+
@foreign_key = options[:foreign_key] || (name.to_s + "Id").underscore.to_sym
|
23
|
+
@primary_key = options[:primary_key] || :id
|
24
|
+
@class_name = options[:class_name] || name.to_s.camelcase
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class HasManyOptions < AssocOptions
|
29
|
+
def initialize(name, self_class_name, options = {})
|
30
|
+
|
31
|
+
@foreign_key = options[:foreign_key] || (self_class_name.to_s + "Id").underscore.to_sym
|
32
|
+
@primary_key = options[:primary_key] || :id
|
33
|
+
@class_name = options[:class_name] || name.to_s.singularize.camelcase
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module Associatable
|
38
|
+
def belongs_to(name, options = {})
|
39
|
+
options = BelongsToOptions.new(name, options)
|
40
|
+
@prior_options = assoc_options
|
41
|
+
@prior_options[name] = options
|
42
|
+
|
43
|
+
define_method(options.class_name.downcase.to_sym) do
|
44
|
+
foreign_key = send(options.foreign_key)
|
45
|
+
the_class = options.model_class
|
46
|
+
the_class.where(options.primary_key => foreign_key).first
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def has_many(name, options = {})
|
51
|
+
options = HasManyOptions.new(name, self, options)
|
52
|
+
@prior_options = assoc_options
|
53
|
+
if @prior_options[name.to_s.singularize.to_sym].nil?
|
54
|
+
@prior_options[name.to_s.singularize.to_sym] = options
|
55
|
+
else
|
56
|
+
@prior_options[name.to_s.singularize.to_sym] << options
|
57
|
+
end
|
58
|
+
define_method(options.table_name.to_sym) do
|
59
|
+
primary_key = send(options.primary_key)
|
60
|
+
options.model_class.where(options.foreign_key => primary_key)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def assoc_options
|
65
|
+
@prior_options ||= {}
|
66
|
+
end
|
67
|
+
|
68
|
+
def has_one_through(name, through_name, source_name)
|
69
|
+
|
70
|
+
define_method(name) do
|
71
|
+
through_options = self.class.assoc_options[through_name]
|
72
|
+
source_options = through_options.model_class.assoc_options[source_name]
|
73
|
+
search_by = send(through_options.foreign_key)
|
74
|
+
|
75
|
+
results = DBConnection.execute(<<-SQL, search_by)
|
76
|
+
SELECT
|
77
|
+
#{source_options.table_name}.*
|
78
|
+
FROM
|
79
|
+
#{through_options.table_name}
|
80
|
+
JOIN
|
81
|
+
#{source_options.table_name} ON #{source_options.table_name}.#{source_options.primary_key.to_s} = #{through_options.table_name}.#{source_options.foreign_key.to_s}
|
82
|
+
WHERE
|
83
|
+
#{through_options.table_name}.#{through_options.primary_key} = ?
|
84
|
+
SQL
|
85
|
+
source_options.model_class.new(results.first)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def has_many_through(name, through_name, source_name)
|
90
|
+
|
91
|
+
define_method(name) do
|
92
|
+
through_options = self.class.assoc_options[through_name]
|
93
|
+
source_options = through_options.model_class.assoc_options[source_name.to_s.singularize.to_sym]
|
94
|
+
search_classes = through_options.model_class.where(through_options.foreign_key => send(source_options.primary_key))
|
95
|
+
|
96
|
+
search_by = []
|
97
|
+
search_classes.each do |classes|
|
98
|
+
search_by << classes.send(source_options.primary_key)
|
99
|
+
end
|
100
|
+
|
101
|
+
results = DBConnection.execute(<<-SQL, *search_by)
|
102
|
+
SELECT
|
103
|
+
#{source_options.table_name}.*
|
104
|
+
FROM
|
105
|
+
#{through_options.table_name}
|
106
|
+
JOIN
|
107
|
+
#{source_options.table_name} ON #{source_options.table_name}.#{source_options.foreign_key.to_s} = #{through_options.table_name}.#{through_options.primary_key.to_s}
|
108
|
+
WHERE
|
109
|
+
#{through_options.table_name}.#{through_options.primary_key} IN (#{Array.new(search_by.length,"?").join(", ")})
|
110
|
+
SQL
|
111
|
+
results.map{|result| source_options.model_class.new(result)}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require_relative '../../../db/lib/db_connection'
|
3
|
+
require_relative 'associatable'
|
4
|
+
require_relative 'searchable'
|
5
|
+
|
6
|
+
class BezelrecordBase
|
7
|
+
extend Associatable
|
8
|
+
extend Searchable
|
9
|
+
|
10
|
+
def self.columns
|
11
|
+
unless @columns
|
12
|
+
temp_columns = DBConnection.execute2(<<-SQL)
|
13
|
+
SELECT
|
14
|
+
*
|
15
|
+
FROM
|
16
|
+
#{table_name}
|
17
|
+
SQL
|
18
|
+
@columns = temp_columns.first.map { |column| column.to_sym }
|
19
|
+
end
|
20
|
+
@columns
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.finalize!
|
24
|
+
columns.each do |column|
|
25
|
+
|
26
|
+
define_method("#{column}") do
|
27
|
+
@attributes = self.attributes
|
28
|
+
@attributes[column]
|
29
|
+
end
|
30
|
+
|
31
|
+
define_method("#{column}=") do |value|
|
32
|
+
@attributes = self.attributes
|
33
|
+
@attributes[column] = value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.table_name=(table_name)
|
39
|
+
@table_name = table_name
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.table_name
|
43
|
+
@table_name || to_s.tableize
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.all
|
47
|
+
results = DBConnection.execute(<<-SQL)
|
48
|
+
SELECT
|
49
|
+
#{table_name}.*
|
50
|
+
FROM
|
51
|
+
#{table_name}
|
52
|
+
SQL
|
53
|
+
parse_all(results)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.parse_all(results)
|
57
|
+
objects = results.map do |object|
|
58
|
+
self.new(object)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.find(id)
|
63
|
+
object = DBConnection.execute(<<-SQL, id)
|
64
|
+
SELECT
|
65
|
+
*
|
66
|
+
FROM
|
67
|
+
#{table_name}
|
68
|
+
WHERE
|
69
|
+
id = ?
|
70
|
+
LIMIT
|
71
|
+
1
|
72
|
+
SQL
|
73
|
+
nil || object.map {|obj| self.new(obj)}.first
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialize(params = {})
|
77
|
+
table_columns = self.class.columns
|
78
|
+
|
79
|
+
params.each do |key, value|
|
80
|
+
raise "unknown attribute '#{key}'" unless table_columns.include?(key.to_sym)
|
81
|
+
send("#{key}=",value)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def attributes
|
86
|
+
@attributes = @attributes || {}
|
87
|
+
end
|
88
|
+
|
89
|
+
def attribute_values
|
90
|
+
table_cols = self.class.columns
|
91
|
+
table_cols
|
92
|
+
table_cols.map{ |col| self.send("#{col}")}
|
93
|
+
end
|
94
|
+
|
95
|
+
def insert
|
96
|
+
columns = self.class.columns
|
97
|
+
|
98
|
+
DBConnection.execute(<<-SQL, *attribute_values)
|
99
|
+
INSERT INTO
|
100
|
+
#{self.class.table_name} (#{columns.join(", ")})
|
101
|
+
VALUES
|
102
|
+
(#{Array.new(columns.length,"?").join(", ")})
|
103
|
+
SQL
|
104
|
+
self.send(:id=,DBConnection.last_insert_row_id)
|
105
|
+
end
|
106
|
+
|
107
|
+
def update
|
108
|
+
DBConnection.execute(<<-SQL,*attribute_values[1..-1], attribute_values.first)
|
109
|
+
UPDATE
|
110
|
+
#{self.class.table_name}
|
111
|
+
SET
|
112
|
+
#{self.class.columns[1..-1].map{|col| "#{col} = ?"}.join(", ")}
|
113
|
+
WHERE
|
114
|
+
id = ?
|
115
|
+
SQL
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
def save
|
120
|
+
id.nil? ? insert : update
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'db_connection'
|
2
|
+
|
3
|
+
module Searchable
|
4
|
+
def where(params)
|
5
|
+
where_line = params.keys.map{|key| "#{key} = ?"}
|
6
|
+
results = DBConnection.execute(<<-SQL, *params.values)
|
7
|
+
SELECT
|
8
|
+
*
|
9
|
+
FROM
|
10
|
+
#{table_name}
|
11
|
+
WHERE
|
12
|
+
#{where_line.join(" AND ")}
|
13
|
+
SQL
|
14
|
+
parse_all(results)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
require 'erb'
|
4
|
+
require_relative './session'
|
5
|
+
require_relative './flash'
|
6
|
+
|
7
|
+
class ControllerBase
|
8
|
+
attr_reader :req, :res, :params, :flash
|
9
|
+
|
10
|
+
def self.protect_from_forgery
|
11
|
+
@@csrf_auth = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(req, res, route_params = {})
|
15
|
+
@req = req
|
16
|
+
@res = res
|
17
|
+
@params = req.params.merge(route_params)
|
18
|
+
@flash = Flash.new(req)
|
19
|
+
@params['authenticity_token'] ||= SecureRandom.base64
|
20
|
+
end
|
21
|
+
|
22
|
+
def form_authenticity_token
|
23
|
+
@res.set_cookie('authenticity_token',@params['authenticity_token'])
|
24
|
+
@params['authenticity_token']
|
25
|
+
end
|
26
|
+
|
27
|
+
def check_authenticity_token(token = "")
|
28
|
+
@params['authenticity_token'] == token
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def already_built_response?
|
33
|
+
!!@already_built_response
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def redirect_to(url)
|
38
|
+
raise 'You cannot call render more than once' if already_built_response?
|
39
|
+
@res.status = 302
|
40
|
+
@res['Location'] = url
|
41
|
+
@already_built_response = true
|
42
|
+
|
43
|
+
session.store_session(@res)
|
44
|
+
end
|
45
|
+
|
46
|
+
def render_content(content, content_type)
|
47
|
+
raise 'You cannot call render more than once' if already_built_response?
|
48
|
+
@res['Content-Type'] = content_type
|
49
|
+
@res.write(content)
|
50
|
+
@already_built_response = true
|
51
|
+
|
52
|
+
session.store_session(@res)
|
53
|
+
end
|
54
|
+
|
55
|
+
def render(template_name)
|
56
|
+
file_name = "views/"
|
57
|
+
file_name += "#{self.class.to_s.underscore}/"
|
58
|
+
file_name += "#{template_name}.html.erb"
|
59
|
+
content = ERB.new(File.read(file_name)).result(binding)
|
60
|
+
|
61
|
+
render_content(content, "text/html")
|
62
|
+
end
|
63
|
+
|
64
|
+
def session
|
65
|
+
@session ||= Session.new(@req)
|
66
|
+
end
|
67
|
+
|
68
|
+
def invoke_action(name)
|
69
|
+
if @@csrf_auth && @req.request_method != "GET"
|
70
|
+
unless check_authenticity_token(@req.cookies['authenticity_token'])
|
71
|
+
raise "Invalid authenticity token"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
send(name)
|
76
|
+
render(name) unless already_built_response?
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'pg'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
PRINT_QUERIES = ENV['PRINT_QUERIES'] == 'true'
|
5
|
+
MIGRATIONS = Dir.glob('./db/migrate/*.sql').to_a
|
6
|
+
|
7
|
+
module Bezel
|
8
|
+
class DBConnection
|
9
|
+
def self.app_name
|
10
|
+
YAML.load_file(Dir.pwd + '/config/database.yml')['database']
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.add_to_version(file)
|
14
|
+
name = parse_migration_file(file)
|
15
|
+
execute(<<-SQL, [name])
|
16
|
+
INSERT INTO
|
17
|
+
version (name)
|
18
|
+
VALUES
|
19
|
+
($1);
|
20
|
+
SQL
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.columns(table_name)
|
24
|
+
columns = instance.exec(<<-SQL)
|
25
|
+
SELECT
|
26
|
+
attname
|
27
|
+
FROM
|
28
|
+
pg_attribute
|
29
|
+
WHERE
|
30
|
+
attrelid = '#{table_name}'::regclass AND
|
31
|
+
attnum > 0 AND
|
32
|
+
NOT attisdropped
|
33
|
+
SQL
|
34
|
+
|
35
|
+
columns.map { |col| col['attname'].to_sym }
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.ensure_version_table
|
39
|
+
table = nil
|
40
|
+
|
41
|
+
if table.nil?
|
42
|
+
execute(<<-SQL)
|
43
|
+
CREATE TABLE IF NOT EXISTS version (
|
44
|
+
id SERIAL PRIMARY KEY,
|
45
|
+
name VARCHAR(255) NOT NULL
|
46
|
+
);
|
47
|
+
SQL
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.execute(*args)
|
52
|
+
print_query(*args)
|
53
|
+
instance.exec(*args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.instance
|
57
|
+
open if @db.nil?
|
58
|
+
|
59
|
+
@db
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.migrate
|
63
|
+
ensure_version_table
|
64
|
+
to_migrate = MIGRATIONS.reject { |file| migrated?(file) }
|
65
|
+
to_migrate.each do |file|
|
66
|
+
add_to_version(file)
|
67
|
+
`psql -d #{app_name} -a -f #{file}`
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.migrated?(file)
|
72
|
+
name = parse_migration_file(file)
|
73
|
+
result = execute(<<-SQL, [name])
|
74
|
+
SELECT
|
75
|
+
*
|
76
|
+
FROM
|
77
|
+
version
|
78
|
+
WHERE
|
79
|
+
name = $1;
|
80
|
+
SQL
|
81
|
+
!!result.first
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.parse_migration_file(file)
|
85
|
+
filename = File.basename(file).split('.').first
|
86
|
+
u_idx = filename.index('_')
|
87
|
+
filename[0..u_idx - 1]
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.print_query(query, *interpolation_args)
|
91
|
+
return unless PRINT_QUERIES
|
92
|
+
|
93
|
+
puts '--------------------'
|
94
|
+
puts query
|
95
|
+
unless interpolation_args.empty?
|
96
|
+
puts "interpolate: #{interpolation_args.inspect}"
|
97
|
+
end
|
98
|
+
puts '--------------------'
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.open
|
102
|
+
@db = PG::Connection.new(
|
103
|
+
dbname: app_name,
|
104
|
+
port: 5432
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.reset
|
109
|
+
commands = [
|
110
|
+
"dropdb #{app_name}",
|
111
|
+
"createdb #{app_name}"
|
112
|
+
]
|
113
|
+
|
114
|
+
commands.each { |command| `#{command}` }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
data/lib/flash.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class Flash
|
4
|
+
def initialize(req)
|
5
|
+
if req.cookies['_bezel_flash']
|
6
|
+
@flash = JSON.parse(req.cookies['_bezel_flash'])
|
7
|
+
else
|
8
|
+
@flash = {}
|
9
|
+
end
|
10
|
+
@flash_now = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key,val)
|
14
|
+
@flash[key] = val
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](key)
|
18
|
+
return @flash_now[key] if @flash_now[key]
|
19
|
+
@flash[key.to_s]
|
20
|
+
end
|
21
|
+
|
22
|
+
def store_flash(res)
|
23
|
+
res.set_cookie('_bezel_flash', value: @flash.to_json, path: '/')
|
24
|
+
end
|
25
|
+
|
26
|
+
def now
|
27
|
+
@flash_now
|
28
|
+
end
|
29
|
+
end
|
data/lib/router.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
class Route
|
2
|
+
attr_reader :pattern, :http_method, :controller_class, :action_name
|
3
|
+
|
4
|
+
def initialize(pattern, http_method, controller_class, action_name)
|
5
|
+
@pattern = pattern
|
6
|
+
@http_method = http_method
|
7
|
+
@controller_class, @action_name = controller_class, action_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(req)
|
11
|
+
pattern =~ req.path && http_method == req.request_method.downcase.to_sym
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(req, res)
|
15
|
+
raise 'no action' unless matches?(req)
|
16
|
+
route_matches = @pattern.match(req.path)
|
17
|
+
route_params = {}
|
18
|
+
route_matches.names.each do |name|
|
19
|
+
route_params[name] = route_matches[name]
|
20
|
+
end
|
21
|
+
controller = controller_class.new(req,res,route_params)
|
22
|
+
controller.invoke_action(action_name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Router
|
27
|
+
attr_reader :routes
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@routes = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_route(pattern, method, controller_class, action_name)
|
34
|
+
@routes << Route.new(pattern, method, controller_class, action_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def draw(&proc)
|
38
|
+
self.instance_eval(&proc)
|
39
|
+
end
|
40
|
+
|
41
|
+
[:get, :post, :put, :delete].each do |http_method|
|
42
|
+
|
43
|
+
define_method(http_method) do |pattern, controller_class, method|
|
44
|
+
add_route(pattern, http_method, controller_class, method)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def match(req)
|
49
|
+
@routes.each do |route|
|
50
|
+
return route if route.matches?(req)
|
51
|
+
end
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def run(req, res)
|
56
|
+
route = match(req)
|
57
|
+
return res.status = 404 unless route
|
58
|
+
route.run(req,res)
|
59
|
+
end
|
60
|
+
end
|
data/lib/session.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class Session
|
4
|
+
def initialize(req)
|
5
|
+
if req.cookies['_']
|
6
|
+
@cookie = JSON.parse(req.cookies['_bezel'])
|
7
|
+
else
|
8
|
+
@cookie = {}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
@cookie[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(key, val)
|
17
|
+
@cookie[key] = val
|
18
|
+
end
|
19
|
+
|
20
|
+
def store_session(res)
|
21
|
+
json_cookie = @cookie.to_json
|
22
|
+
res.set_cookie('_bezel', value: json_cookie, path: '/' )
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'erb'
|
2
|
+
class ShowExceptions
|
3
|
+
attr_reader :app
|
4
|
+
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
begin
|
11
|
+
app.call(env)
|
12
|
+
rescue Exception => e
|
13
|
+
render_exception(e)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def render_exception(e)
|
20
|
+
response = Rack::Response.new([], 500, 'Content_Type'=> 'text/html')
|
21
|
+
response.write(e.message)
|
22
|
+
response.finish
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class StaticAssets
|
2
|
+
def initialize(app)
|
3
|
+
@app = app
|
4
|
+
end
|
5
|
+
|
6
|
+
def call(env)
|
7
|
+
file_path = "." + env['PATH_INFO']
|
8
|
+
res = Rack::Response.new
|
9
|
+
extension = /\.(\w*$)/.match(file_path)[1]
|
10
|
+
begin
|
11
|
+
res['Content-Type'] = set_content_type(extension)
|
12
|
+
content = File.read(file_path)
|
13
|
+
res.write(content)
|
14
|
+
rescue
|
15
|
+
res.status = 404
|
16
|
+
end
|
17
|
+
|
18
|
+
res.finish
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def set_content_type(extension)
|
24
|
+
case extension
|
25
|
+
when "txt"
|
26
|
+
"text/plain"
|
27
|
+
when "jpg"
|
28
|
+
"image/jpeg"
|
29
|
+
when "zip"
|
30
|
+
"application/zip"
|
31
|
+
when "png"
|
32
|
+
"image/png"
|
33
|
+
else
|
34
|
+
raise 'extension not supported'
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
ROUTER = Bezel::Router.new
|
2
|
+
ROUTER.draw do
|
3
|
+
#Some sample routes:
|
4
|
+
|
5
|
+
# get Regexp.new("^/cats/new$"), CatsController, :new
|
6
|
+
# post Regexp.new("^/cats/create$"), CatsController, :create
|
7
|
+
# get Regexp.new("^/cats$"), CatsController, :index
|
8
|
+
# get Regexp.new("^/cats/(?<cat_id>\\d+)$"), CatsController, :show
|
9
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<% if flash[:errors] %>
|
2
|
+
<% flash[:errors].each do |error| %>
|
3
|
+
<%= error %>
|
4
|
+
<% end %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<form action="/dogs" method="POST">
|
8
|
+
<label>Name
|
9
|
+
<input type="text" name="dog[name]" value="<%= @dog.name %>">
|
10
|
+
</label>
|
11
|
+
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
|
12
|
+
|
13
|
+
|
14
|
+
<label>Owner
|
15
|
+
<input type="text" name="dog[owner]" value="<%= @dog.owner %>">
|
16
|
+
</label>
|
17
|
+
|
18
|
+
<input type="submit">
|
19
|
+
</form>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= link_to "all dogs", dogs_path %>
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= session["count"] %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= (1..10).to_a.join(", ") %>
|
metadata
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bezel-app
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Keith Thompson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.19'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.19'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pg
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.18'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.18'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.2'
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 4.2.5.1
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - "~>"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '4.2'
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 4.2.5.1
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: pry
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.10.3
|
68
|
+
type: :runtime
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 0.10.3
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rack
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '1.6'
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 1.6.4
|
85
|
+
type: :runtime
|
86
|
+
prerelease: false
|
87
|
+
version_requirements: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - "~>"
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '1.6'
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 1.6.4
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: fileutils
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0.7'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0.7'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: bundler
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '1.11'
|
116
|
+
type: :development
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - "~>"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '1.11'
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: rake
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '10.0'
|
130
|
+
type: :development
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '10.0'
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: rspec
|
139
|
+
requirement: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - "~>"
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '3.0'
|
144
|
+
type: :development
|
145
|
+
prerelease: false
|
146
|
+
version_requirements: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - "~>"
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '3.0'
|
151
|
+
description: Replicate much of the core functionality of rails.
|
152
|
+
email:
|
153
|
+
- KeithM_Thompson@outlook.com
|
154
|
+
executables:
|
155
|
+
- bezel-app
|
156
|
+
extensions: []
|
157
|
+
extra_rdoc_files: []
|
158
|
+
files:
|
159
|
+
- ".byebug_history"
|
160
|
+
- ".gitconfig"
|
161
|
+
- ".gitignore"
|
162
|
+
- ".rspec"
|
163
|
+
- Gemfile
|
164
|
+
- Gemfile.lock
|
165
|
+
- bezel-app.gemspec
|
166
|
+
- bin/bezel-app
|
167
|
+
- lib/bezel.rb
|
168
|
+
- lib/bezelrecord_base/associatable.rb
|
169
|
+
- lib/bezelrecord_base/bezelrecord_base.rb
|
170
|
+
- lib/bezelrecord_base/searchable.rb
|
171
|
+
- lib/controller_base.rb
|
172
|
+
- lib/db_connection.rb
|
173
|
+
- lib/flash.rb
|
174
|
+
- lib/router.rb
|
175
|
+
- lib/session.rb
|
176
|
+
- lib/show_exceptions.rb
|
177
|
+
- lib/static_assets.rb
|
178
|
+
- template/app/controllers/application_controller.rb
|
179
|
+
- template/config/routes.rb
|
180
|
+
- template/db/seeds.rb
|
181
|
+
- views/cats_controller/index.html.erb
|
182
|
+
- views/cats_controller/new.html.erb
|
183
|
+
- views/dogs_controller/index.html.erb
|
184
|
+
- views/dogs_controller/new.html.erb
|
185
|
+
- views/dogs_controller/show.html.erb
|
186
|
+
- views/dummy_controller/index.html.erb
|
187
|
+
- views/my_controller/counting_show.html.erb
|
188
|
+
- views/my_controller/show.html.erb
|
189
|
+
homepage: http://github.com/keithm-thompson/Bezel
|
190
|
+
licenses:
|
191
|
+
- MIT
|
192
|
+
metadata:
|
193
|
+
allowed_push_host: https://rubygems.org
|
194
|
+
post_install_message:
|
195
|
+
rdoc_options: []
|
196
|
+
require_paths:
|
197
|
+
- lib
|
198
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
199
|
+
requirements:
|
200
|
+
- - ">="
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: '0'
|
203
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - ">="
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '0'
|
208
|
+
requirements: []
|
209
|
+
rubyforge_project:
|
210
|
+
rubygems_version: 2.2.2
|
211
|
+
signing_key:
|
212
|
+
specification_version: 4
|
213
|
+
summary: A simple ORM and MVC inspired by Rails.
|
214
|
+
test_files: []
|