reloj 0.1.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/.rspec +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +64 -0
- data/README.md +89 -0
- data/Rakefile +1 -0
- data/bin/reloj +66 -0
- data/lib/reloj/core/controller_base.rb +68 -0
- data/lib/reloj/core/flash.rb +38 -0
- data/lib/reloj/core/params.rb +52 -0
- data/lib/reloj/core/route_helper.rb +45 -0
- data/lib/reloj/core/router.rb +89 -0
- data/lib/reloj/core/session.rb +32 -0
- data/lib/reloj/orm/associatable.rb +70 -0
- data/lib/reloj/orm/associatable2.rb +34 -0
- data/lib/reloj/orm/db_connection.rb +59 -0
- data/lib/reloj/orm/model_base.rb +132 -0
- data/lib/reloj/orm/pg_db.rb +59 -0
- data/lib/reloj/orm/searchable.rb +26 -0
- data/lib/reloj/skeletons/common_files/Gemfile +6 -0
- data/lib/reloj/skeletons/common_files/Rakefile +25 -0
- data/lib/reloj/skeletons/new_app/app/controllers/.keep +0 -0
- data/lib/reloj/skeletons/new_app/app/models/.keep +0 -0
- data/lib/reloj/skeletons/new_app/app/views/.keep +0 -0
- data/lib/reloj/skeletons/new_app/config/routes.rb +7 -0
- data/lib/reloj/skeletons/new_app/db/.keep +0 -0
- data/lib/reloj/skeletons/sample_app/app/controllers/cats_controller.rb +24 -0
- data/lib/reloj/skeletons/sample_app/app/models/cat.rb +8 -0
- data/lib/reloj/skeletons/sample_app/app/views/cats_controller/index.html.erb +5 -0
- data/lib/reloj/skeletons/sample_app/app/views/cats_controller/new.html.erb +11 -0
- data/lib/reloj/skeletons/sample_app/config/routes.rb +10 -0
- data/lib/reloj/skeletons/sample_app/db/setup.sql +46 -0
- data/lib/reloj/version.rb +3 -0
- data/lib/reloj.rb +8 -0
- data/reloj.gemspec +41 -0
- metadata +206 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ee697fff25d8314ec1c3fd14e7316e34ffdd6e37
|
4
|
+
data.tar.gz: 9e0129f049be415f577a1f66a6bc55c18c9609fc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2d0a1fa8bffc06eb067d4fe9ebea366dbda41bfa50f367f62e9ba4b78af992279887c5787e8c956600c765a6cb33e4b7626e4e7b7eead06f08fd7cb1087fefef
|
7
|
+
data.tar.gz: f1d90f2e54f1e42d6337ccc7a780e130da1e161c2aab0ef109b5d4d10726a5a7f2b85b6d5c93325d58bf37db90ed63a049b087b42b46e6257a60f82ebe944d62
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
reloj (0.1.0)
|
5
|
+
activesupport (~> 4.2)
|
6
|
+
pg (~> 0.18)
|
7
|
+
require_all (~> 1.3)
|
8
|
+
webrick (~> 1.3)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
activesupport (4.2.4)
|
14
|
+
i18n (~> 0.7)
|
15
|
+
json (~> 1.7, >= 1.7.7)
|
16
|
+
minitest (~> 5.1)
|
17
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
18
|
+
tzinfo (~> 1.1)
|
19
|
+
byebug (6.0.2)
|
20
|
+
coderay (1.1.0)
|
21
|
+
diff-lcs (1.2.5)
|
22
|
+
i18n (0.7.0)
|
23
|
+
json (1.8.3)
|
24
|
+
method_source (0.8.2)
|
25
|
+
minitest (5.8.1)
|
26
|
+
pg (0.18.3)
|
27
|
+
pry (0.10.1)
|
28
|
+
coderay (~> 1.1.0)
|
29
|
+
method_source (~> 0.8.1)
|
30
|
+
slop (~> 3.4)
|
31
|
+
rake (10.4.2)
|
32
|
+
require_all (1.3.2)
|
33
|
+
rspec (3.3.0)
|
34
|
+
rspec-core (~> 3.3.0)
|
35
|
+
rspec-expectations (~> 3.3.0)
|
36
|
+
rspec-mocks (~> 3.3.0)
|
37
|
+
rspec-core (3.3.2)
|
38
|
+
rspec-support (~> 3.3.0)
|
39
|
+
rspec-expectations (3.3.1)
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
41
|
+
rspec-support (~> 3.3.0)
|
42
|
+
rspec-mocks (3.3.2)
|
43
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
44
|
+
rspec-support (~> 3.3.0)
|
45
|
+
rspec-support (3.3.0)
|
46
|
+
slop (3.6.0)
|
47
|
+
thread_safe (0.3.5)
|
48
|
+
tzinfo (1.2.2)
|
49
|
+
thread_safe (~> 0.1)
|
50
|
+
webrick (1.3.1)
|
51
|
+
|
52
|
+
PLATFORMS
|
53
|
+
ruby
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
bundler (~> 1.10)
|
57
|
+
byebug (~> 6.0)
|
58
|
+
pry (~> 0.10)
|
59
|
+
rake (~> 10.0)
|
60
|
+
reloj!
|
61
|
+
rspec (~> 3.3)
|
62
|
+
|
63
|
+
BUNDLED WITH
|
64
|
+
1.10.6
|
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Reloj
|
2
|
+
A lightweight web framework for Ruby for creating database-backed web applications with the Model-View-Controller pattern.
|
3
|
+
|
4
|
+
## Getting Started
|
5
|
+
|
6
|
+
1. Install Reloj at the command prompt if you haven't yet:
|
7
|
+
|
8
|
+
gem install reloj
|
9
|
+
|
10
|
+
2. At the command prompt, create a new Reloj application:
|
11
|
+
|
12
|
+
reloj new myapp
|
13
|
+
|
14
|
+
where "myapp" is the application name.
|
15
|
+
|
16
|
+
3. Change directory to `myapp` and start the web server:
|
17
|
+
|
18
|
+
cd myapp
|
19
|
+
reloj server
|
20
|
+
|
21
|
+
|
22
|
+
4. Using a browser, go to `http://localhost:3000` and you'll see:
|
23
|
+
"Welcome to Reloj!"
|
24
|
+
|
25
|
+
## Models and ORM
|
26
|
+
Reloj uses the active record pattern for its object-relational mapping.
|
27
|
+
To use this functionality in your app, create a class for your model in `app/models` and have the model inherit from ModelBase
|
28
|
+
```ruby
|
29
|
+
class Cat < ModelBase
|
30
|
+
# custom code goes here
|
31
|
+
|
32
|
+
finalize!
|
33
|
+
end
|
34
|
+
```
|
35
|
+
###Some commands:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
Cat.all
|
39
|
+
```
|
40
|
+
* Returns an array with instances of class Cat, one instance for each row in table cats
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
Cat.find(2)
|
44
|
+
```
|
45
|
+
* Finds the record in table cats with id 2, returns instance of Cat corresponding to that record
|
46
|
+
|
47
|
+
## Controllers
|
48
|
+
Create controllers in `app/controllers`. Controllers should inherit from `ControllerBase`.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
class CatsController < ControllerBase
|
52
|
+
|
53
|
+
def index
|
54
|
+
@cats = Cat.all
|
55
|
+
render :index
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
## Routes
|
62
|
+
Write your routes in `config/routes.rb`
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
module App
|
66
|
+
ROUTES = Proc.new do
|
67
|
+
get '/cats', CatsController, :index
|
68
|
+
get '/cats/new', CatsController, :new
|
69
|
+
post '/cats', CatsController, :create
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
## Running the sample app
|
75
|
+
Reloj includes a generator for a sample app. To check it out:
|
76
|
+
|
77
|
+
1. Generate the sample app
|
78
|
+
|
79
|
+
reloj generate:sample
|
80
|
+
|
81
|
+
2. Move into the sample app directory
|
82
|
+
|
83
|
+
cd reloj_sample
|
84
|
+
|
85
|
+
3. Run the server
|
86
|
+
|
87
|
+
reloj server
|
88
|
+
|
89
|
+
4. Navigate to localhost:3000
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/reloj
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'reloj'
|
4
|
+
|
5
|
+
class TaskHandler
|
6
|
+
|
7
|
+
def self.run_server
|
8
|
+
require 'require_all'
|
9
|
+
require_all "#{Dir.pwd}/app/**/*.rb"
|
10
|
+
require_relative "#{Dir.pwd}/config/routes.rb"
|
11
|
+
|
12
|
+
router = Reloj::Router.new
|
13
|
+
router.draw(&App::ROUTES)
|
14
|
+
|
15
|
+
server = WEBrick::HTTPServer.new(Port: 3000)
|
16
|
+
server.mount_proc('/') { |req, res| router.run(req, res) }
|
17
|
+
trap('INT') { server.shutdown }
|
18
|
+
server.start
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.generate_project(src, name)
|
22
|
+
# copy skeleton
|
23
|
+
copy_directory(src, name)
|
24
|
+
|
25
|
+
# copy Rakefile and Gemfile
|
26
|
+
copy_directory("common_files/.", name)
|
27
|
+
|
28
|
+
# write db.yml
|
29
|
+
config = { dbname: name }
|
30
|
+
File.write(File.join(Dir.pwd, "#{name}/config/db.yml"), YAML.dump(config))
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.copy_directory(src, dest)
|
34
|
+
FileUtils.cp_r(File.join(skels_dir, src), "./#{dest}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.skels_dir
|
38
|
+
@skels_dir ||= File.expand_path(
|
39
|
+
File.dirname(__FILE__) +
|
40
|
+
'/../lib/reloj/skeletons'
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
case ARGV[0]
|
46
|
+
when "server"
|
47
|
+
TaskHandler.run_server
|
48
|
+
when "new"
|
49
|
+
name = ARGV[1].nil? ? "reloj_app" : ARGV[1]
|
50
|
+
TaskHandler.generate_project('new_app', name)
|
51
|
+
when "db"
|
52
|
+
case ARGV[1]
|
53
|
+
when "create"
|
54
|
+
Database.create
|
55
|
+
when "delete"
|
56
|
+
Database.delete
|
57
|
+
when "setup"
|
58
|
+
Database.setup
|
59
|
+
when "reset"
|
60
|
+
Database.reset
|
61
|
+
end
|
62
|
+
when "generate:sample"
|
63
|
+
TaskHandler.generate_project('sample_app', 'reloj_sample')
|
64
|
+
else
|
65
|
+
puts "Invalid reloj command"
|
66
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
require 'active_support/inflector'
|
4
|
+
require 'erb'
|
5
|
+
require_relative './session'
|
6
|
+
require_relative './params'
|
7
|
+
require_relative './flash'
|
8
|
+
require_relative './route_helper'
|
9
|
+
|
10
|
+
|
11
|
+
module Reloj
|
12
|
+
class ControllerBase
|
13
|
+
include RouteHelper
|
14
|
+
attr_reader :params, :req, :res
|
15
|
+
|
16
|
+
def initialize(req, res, route_params = {}, paths = [])
|
17
|
+
@req = req
|
18
|
+
@res = res
|
19
|
+
@already_built_response = false
|
20
|
+
@params = Params.new(req, route_params)
|
21
|
+
@paths = paths
|
22
|
+
self.class.create_helper_methods(paths)
|
23
|
+
end
|
24
|
+
|
25
|
+
def already_built_response?
|
26
|
+
@already_built_response
|
27
|
+
end
|
28
|
+
|
29
|
+
def redirect_to(url)
|
30
|
+
raise if already_built_response?
|
31
|
+
@res.status = 302
|
32
|
+
@res["Location"] = url
|
33
|
+
@already_built_response = true
|
34
|
+
flash.store_flash(@res)
|
35
|
+
session.store_session(@res)
|
36
|
+
end
|
37
|
+
|
38
|
+
def render_content(content, content_type)
|
39
|
+
raise if already_built_response?
|
40
|
+
@already_built_response = true
|
41
|
+
@res.content_type = content_type
|
42
|
+
@res.body = content
|
43
|
+
flash.store_flash(@res)
|
44
|
+
session.store_session(@res)
|
45
|
+
end
|
46
|
+
|
47
|
+
def render(template_name)
|
48
|
+
controller_name = self.class.to_s.underscore
|
49
|
+
f = File.read("app/views/#{controller_name}/#{template_name}.html.erb")
|
50
|
+
f = ERB.new(f)
|
51
|
+
render_content(f.result(binding), "text/html")
|
52
|
+
end
|
53
|
+
|
54
|
+
def session
|
55
|
+
@session ||= Session.new(@req)
|
56
|
+
end
|
57
|
+
|
58
|
+
def flash
|
59
|
+
@flash ||= Flash.new(@req)
|
60
|
+
end
|
61
|
+
|
62
|
+
def invoke_action(name)
|
63
|
+
self.send(name)
|
64
|
+
render(name) unless self.already_built_response?
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'webrick'
|
3
|
+
|
4
|
+
module Reloj
|
5
|
+
class Flash
|
6
|
+
# find the cookie for this app
|
7
|
+
# deserialize the cookie into a hash
|
8
|
+
def initialize(req)
|
9
|
+
req.cookies.each do |cookie|
|
10
|
+
if cookie.name == '_flash'
|
11
|
+
@_old_flash = JSON.parse(cookie.value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
@_old_flash ||= {}
|
15
|
+
@_new_flash ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](key)
|
19
|
+
@_old_flash[key] || @_new_flash[key]
|
20
|
+
end
|
21
|
+
|
22
|
+
def []=(key, val)
|
23
|
+
@_new_flash[key] = val
|
24
|
+
end
|
25
|
+
|
26
|
+
def now
|
27
|
+
@_old_flash
|
28
|
+
end
|
29
|
+
|
30
|
+
# serialize the hash into json and save in a cookie
|
31
|
+
# add to the responses cookies
|
32
|
+
def store_flash(res)
|
33
|
+
cookie = WEBrick::Cookie.new('_flash', @_new_flash.to_json)
|
34
|
+
cookie.path = '/'
|
35
|
+
res.cookies << cookie
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Reloj
|
4
|
+
class Params
|
5
|
+
def initialize(req, route_params = {})
|
6
|
+
@params = route_params
|
7
|
+
if req.query_string
|
8
|
+
@params.merge!(parse_www_encoded_form(req.query_string))
|
9
|
+
end
|
10
|
+
@params.merge!(parse_www_encoded_form(req.body)) if req.body
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
key = key.to_s
|
15
|
+
@params[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
@params.to_json.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
class AttributeNotFoundError < ArgumentError; end;
|
23
|
+
|
24
|
+
private
|
25
|
+
# this should return deeply nested hash, ex:
|
26
|
+
# user[address][street]=main&user[address][zip]=89436
|
27
|
+
# should return
|
28
|
+
# { "user" => { "address" => { "street" => "main", "zip" => "89436" } } }
|
29
|
+
def parse_www_encoded_form(www_encoded_form)
|
30
|
+
x = URI::decode_www_form(www_encoded_form)
|
31
|
+
result = {}
|
32
|
+
x.each do |arr|
|
33
|
+
parsed_keys = parse_key(arr.first)
|
34
|
+
value = arr.last
|
35
|
+
location_in_result = result
|
36
|
+
parsed_keys.each_with_index do |parsed_key, i|
|
37
|
+
location_in_result[parsed_key] = value if i == parsed_keys.length - 1
|
38
|
+
location_in_result[parsed_key] ||= {}
|
39
|
+
location_in_result = location_in_result[parsed_key]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
# this should return an array
|
47
|
+
# user[address][street] should return ['user', 'address', 'street']
|
48
|
+
def parse_key(key)
|
49
|
+
key.split(/\]\[|\[|\]/)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RouteHelper
|
2
|
+
|
3
|
+
def RouteHelper.included(klass)
|
4
|
+
klass.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
#maybe add link_to and button_to here
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def create_helper_methods(paths)
|
12
|
+
paths.each do |path|
|
13
|
+
create_helper_method(path)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_helper_method(path)
|
18
|
+
nouns = path.split('/')
|
19
|
+
nouns.delete("")
|
20
|
+
if nouns.any? { |noun| noun[0] == ":" }
|
21
|
+
build_nested_route_helper(nouns)
|
22
|
+
else
|
23
|
+
method_name = nouns.join("_") + "_path"
|
24
|
+
define_method(method_name) { path }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_nested_route_helper(nouns)
|
29
|
+
just_names = nouns.select { |noun| noun[0] != ":" }
|
30
|
+
method_name = nouns.select do |noun|
|
31
|
+
noun[0] != ":"
|
32
|
+
end.join("_") + "_path"
|
33
|
+
|
34
|
+
define_method(method_name) do |*ids|
|
35
|
+
result = []
|
36
|
+
just_names.each do |noun|
|
37
|
+
result << noun
|
38
|
+
result << ids.shift unless ids.empty?
|
39
|
+
end
|
40
|
+
result.join('/')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative './route_helper'
|
2
|
+
|
3
|
+
|
4
|
+
module Reloj
|
5
|
+
class Route
|
6
|
+
attr_reader :pattern, :http_method, :controller_class, :action_name
|
7
|
+
|
8
|
+
def initialize(pattern, http_method, controller_class, action_name)
|
9
|
+
@pattern = pattern
|
10
|
+
@http_method = http_method
|
11
|
+
@controller_class = controller_class
|
12
|
+
@action_name = action_name
|
13
|
+
# save all helpers in the routes
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def matches?(req)
|
18
|
+
req.request_method.downcase.to_sym == @http_method &&
|
19
|
+
@pattern.match(req.path)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def run(req, res, paths = [])
|
24
|
+
route_params = {}
|
25
|
+
matches = @pattern.match(req.path)
|
26
|
+
matches.names.each do |key|
|
27
|
+
route_params[key] = matches[key]
|
28
|
+
end
|
29
|
+
#pass the methods to the controller class here
|
30
|
+
@controller_class.new(req, res, route_params, paths).invoke_action(@action_name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Router
|
35
|
+
attr_reader :routes, :helpers
|
36
|
+
|
37
|
+
def self.regex_from_pattern_string(pattern_string)
|
38
|
+
x = pattern_string.split('/').map do |part|
|
39
|
+
part.gsub(/:(.+)[\/]{,1}/, '(?<\1>\d+)')
|
40
|
+
end.join('/')
|
41
|
+
|
42
|
+
Regexp.new("^#{x}\/?$")
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize
|
46
|
+
@routes = []
|
47
|
+
@paths = []
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_helper_attributes(pattern, action_name)
|
51
|
+
#need controller name
|
52
|
+
# will get that when passed to controller
|
53
|
+
#need action
|
54
|
+
@paths << pattern
|
55
|
+
end
|
56
|
+
|
57
|
+
# simply adds a new route to the list of routes
|
58
|
+
def add_route(pattern, method, controller_class, action_name)
|
59
|
+
parsed_pattern = Router.regex_from_pattern_string(pattern.to_s)
|
60
|
+
route = Route.new(parsed_pattern, method, controller_class, action_name)
|
61
|
+
@routes << route
|
62
|
+
add_helper_attributes(pattern, action_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
def draw(&proc)
|
66
|
+
instance_eval(&proc)
|
67
|
+
end
|
68
|
+
|
69
|
+
[:get, :post, :put, :delete].each do |http_method|
|
70
|
+
define_method(http_method) do |pattern, controller_class, action_name|
|
71
|
+
add_route(pattern, http_method, controller_class, action_name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def match(req)
|
76
|
+
@routes.each { |route| return route if route.matches?(req) }
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def run(req, res)
|
81
|
+
if match(req)
|
82
|
+
#pass in the helper_attributes here
|
83
|
+
match(req).run(req, res, @paths)
|
84
|
+
else
|
85
|
+
res.status = 404
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'webrick'
|
3
|
+
|
4
|
+
module Reloj
|
5
|
+
class Session
|
6
|
+
# find the cookie for this app, deserialize into hash
|
7
|
+
def initialize(req)
|
8
|
+
req.cookies.each do |cookie|
|
9
|
+
if cookie.name == '_rails_lite_app'
|
10
|
+
@_rails_lite_app = JSON.parse(cookie.value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@_rails_lite_app ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# convenience methods
|
17
|
+
def [](key)
|
18
|
+
@_rails_lite_app[key]
|
19
|
+
end
|
20
|
+
|
21
|
+
def []=(key, val)
|
22
|
+
@_rails_lite_app[key] = val
|
23
|
+
end
|
24
|
+
|
25
|
+
# serialize the hash into json and save in a cookie, add to responses
|
26
|
+
def store_session(res)
|
27
|
+
cookie = WEBrick::Cookie.new('_rails_lite_app', @_rails_lite_app.to_json)
|
28
|
+
cookie.path = '/'
|
29
|
+
res.cookies << cookie
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative 'model_base'
|
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
|
+
class_name.constantize
|
13
|
+
end
|
14
|
+
|
15
|
+
def table_name
|
16
|
+
model_class.table_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class BelongsToOptions < AssocOptions
|
21
|
+
def initialize(name, options = {})
|
22
|
+
defaults = {
|
23
|
+
foreign_key: "#{name.to_s.underscore}_id".to_sym,
|
24
|
+
primary_key: :id,
|
25
|
+
class_name: name.to_s.camelcase.singularize
|
26
|
+
}.merge(options)
|
27
|
+
defaults.each do |attribute, value|
|
28
|
+
self.send("#{attribute}=", value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class HasManyOptions < AssocOptions
|
34
|
+
def initialize(name, self_class_name, options = {})
|
35
|
+
defaults = {
|
36
|
+
foreign_key: "#{self_class_name.to_s.underscore}_id".to_sym,
|
37
|
+
primary_key: :id,
|
38
|
+
class_name: name.to_s.singularize.camelcase
|
39
|
+
}.merge(options)
|
40
|
+
defaults.each do |attribute, value|
|
41
|
+
self.send("#{attribute}=", value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module Associatable
|
47
|
+
def belongs_to(name, options = {})
|
48
|
+
assoc_options[name] = options = BelongsToOptions.new(name, options)
|
49
|
+
define_method(name) do
|
50
|
+
foreign_key_value = send(options.foreign_key)
|
51
|
+
model_class = options.model_class
|
52
|
+
model_class.where(options.primary_key => foreign_key_value).first
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def has_many(name, options = {})
|
57
|
+
options = HasManyOptions.new(name, self.name, options)
|
58
|
+
define_method(name) do
|
59
|
+
options.model_class.where( options.foreign_key => id )
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def assoc_options
|
64
|
+
@assoc_options ||= {}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class ModelBase
|
69
|
+
extend Associatable
|
70
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'associatable'
|
2
|
+
|
3
|
+
module Associatable
|
4
|
+
# Remember to go back to 04_associatable to write ::assoc_options
|
5
|
+
|
6
|
+
def has_one_through(name, through_name, source_name)
|
7
|
+
self_options = assoc_options[name]
|
8
|
+
through_options = assoc_options[through_name]
|
9
|
+
#very close to finishing
|
10
|
+
define_method(name) do
|
11
|
+
# p all your assoc_options keys/values
|
12
|
+
source_options = through_options.model_class.assoc_options[source_name]
|
13
|
+
source_table = source_options.table_name
|
14
|
+
through_table = through_options.table_name
|
15
|
+
x = DBConnection.execute(<<-SQL)
|
16
|
+
SELECT
|
17
|
+
#{source_table}.*
|
18
|
+
FROM
|
19
|
+
#{source_table}
|
20
|
+
INNER JOIN
|
21
|
+
#{through_table}
|
22
|
+
ON
|
23
|
+
#{source_table}.id = #{through_table}.#{source_options.foreign_key}
|
24
|
+
INNER JOIN
|
25
|
+
#{self.class.table_name}
|
26
|
+
ON
|
27
|
+
#{self.class.table_name}.#{through_options.foreign_key} = #{through_table}.id;
|
28
|
+
SQL
|
29
|
+
|
30
|
+
source_options.model_class.parse_all(x).first
|
31
|
+
# use constantize
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|