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 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
@@ -0,0 +1,2 @@
1
+ [core]
2
+ editor = atom --wait
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rack', '~> 1.6.4'
4
+ gem 'byebug'
5
+ gem "rspec", "~> 3.1.0"
6
+ gem "activesupport", '~> 4.2'
7
+ gem 'thor'
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,3 @@
1
+ class ApplicationController < Bezel::ControllerBase
2
+ ApplicationController.protect_from_forgery
3
+ 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,7 @@
1
+ module Bezel
2
+ class Seed
3
+ def self.populate
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ <h1>ALL THE CATS</h1>
2
+ <pre><%= @cats.to_s %></pre>
3
+ <a href="/cats/new">New cat!</a>
@@ -0,0 +1,11 @@
1
+ <form action="/cats" method="POST">
2
+ <label>Name
3
+ <input type="text" name="cat[name]" value="<%= @cat.name %>">
4
+ </label>
5
+
6
+ <label>Owner
7
+ <input type="text" name="cat[owner]" value="<%= @cat.owner %>">
8
+ </label>
9
+
10
+ <input type="submit">
11
+ </form>
@@ -0,0 +1,8 @@
1
+ <h1>ALL THE DOGS</h1>
2
+
3
+ <% if flash[:notice] %>
4
+ Notice: <%= flash[:notice] %>
5
+ <% end %>
6
+
7
+ <pre>Dog Data: <%= @dogs.to_s %></pre>
8
+ <a href="/dogs/new">New Dog!</a>
@@ -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: []