bezel-app 0.0.1
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 +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: []
|