femto 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/.gitignore +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +126 -0
- data/Rakefile +1 -0
- data/example/App.rb +44 -0
- data/example/config.ru +2 -0
- data/example/views/home.haml +2 -0
- data/example/views/layout.haml +11 -0
- data/femto.gemspec +28 -0
- data/lib/femto.rb +144 -0
- data/lib/femto/model.rb +143 -0
- data/lib/femto/model/mongo_adapter.rb +69 -0
- data/lib/femto/renderer.rb +32 -0
- data/lib/femto/version.rb +3 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a93cec4c12adeba287bb64b1d4a48ce9efcd0ab6
|
4
|
+
data.tar.gz: 92011f12f45c0e25e8ecb410b1d08cf388acde05
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 05f1be7d809186c663ab14b379a19c24e4e61b07444054efdf201cc9e2b5238f1d91e36cbd909a23aac7be8d6d5f5ec345e7848360965968534a8b2eceacb861
|
7
|
+
data.tar.gz: 6643c3a350cd4b85241a575d345f470af049274c1264278c052eb4b8c4b07d14a4bb224a0244c8f1329394eefa1881d6619fd3d1a85f07523b7840d98d15fc8f
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
|
19
|
+
# RubyMine
|
20
|
+
.idea/
|
21
|
+
|
22
|
+
# Random commands
|
23
|
+
build.command
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 August
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Femto
|
2
|
+
|
3
|
+
A tiny web framework.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this to your Gemfile:
|
8
|
+
```ruby
|
9
|
+
gem 'femto', github: 'augustt198/femto'
|
10
|
+
```
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
First, setup your views/templates folder:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
# For example
|
18
|
+
@templates_dir = File.join __dir__, 'views'
|
19
|
+
```
|
20
|
+
|
21
|
+
Handle requests by passing a path and block to a HTTP verb method.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
get '/' do
|
25
|
+
render 'home'
|
26
|
+
end
|
27
|
+
|
28
|
+
# The 'view' option automatically renders a template!
|
29
|
+
get '/about', view: 'about_page' do
|
30
|
+
# You can create variables to use in your views
|
31
|
+
@copyright_date = '2014'
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
The render method tries to render a view if a String is passed, but other
|
36
|
+
content types can be rendered:
|
37
|
+
```ruby
|
38
|
+
get '/status' do
|
39
|
+
render json: {system_status: 'good'}
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
### Models
|
44
|
+
To use models, first you need specify the model adapter. `MongoAdapter` is the only model adapter currently:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# Connect to database
|
48
|
+
Femto::Model::MongoAdapter.connect host: 'localhost', port: 27107, db: 'test'
|
49
|
+
Femto::Model.adapter = Femto::Model::MongoAdapter
|
50
|
+
```
|
51
|
+
|
52
|
+
Add models with the `model` method, for example:
|
53
|
+
```ruby
|
54
|
+
model :user do |m|
|
55
|
+
m.field :username
|
56
|
+
m.field :password
|
57
|
+
m.field :created_at
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
This will create the class `User`. Use the model class to find, update, and insert:
|
62
|
+
```ruby
|
63
|
+
user = User.new(username: 'foo', password: 'bar')
|
64
|
+
user.save # Saves to database or updates if it already exists
|
65
|
+
User.find # => [#<User:0x007fcb5bef8840>]
|
66
|
+
```
|
67
|
+
|
68
|
+
The parameter passed to the block in the `model` method is the `ModelCreator`.
|
69
|
+
You can add your own methods to the model:
|
70
|
+
```ruby
|
71
|
+
model :user do |m|
|
72
|
+
m.set_method('confirmed?') { false }
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
You can also change the storage name of the model. The default is the model name + "s".
|
77
|
+
```ruby
|
78
|
+
model :category do |m|
|
79
|
+
m.storage :categories
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
### Layouts
|
84
|
+
|
85
|
+
You can define application-wide views by using the layout method.
|
86
|
+
```ruby
|
87
|
+
layout view: 'layout' do
|
88
|
+
# This block would be called every page load
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
An example layout view:
|
93
|
+
```html
|
94
|
+
<div class="container">
|
95
|
+
<!-- the yield keyword renders the current view -->
|
96
|
+
<%= yield %>
|
97
|
+
</div>
|
98
|
+
```
|
99
|
+
|
100
|
+
### Custom Renderers
|
101
|
+
Femto comes with the renderers `:json`, `:pretty_json`, and `:text`, but you can easily
|
102
|
+
add custom renderers.
|
103
|
+
|
104
|
+
|
105
|
+
For instance:
|
106
|
+
```ruby
|
107
|
+
Femto::Renderer.renderer :custom do |content|
|
108
|
+
['text/plain', content.to_s.reverse]
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
All renderers should return an array with the `Content-Type` at position `[0]`, and the actual content
|
113
|
+
at position `[1]`.
|
114
|
+
|
115
|
+
### [App Example](https://github.com/augustt198/femto/tree/master/example)
|
116
|
+
|
117
|
+
## Contributing
|
118
|
+
|
119
|
+
1. Fork it
|
120
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
121
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
122
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
123
|
+
5. Create new Pull Request
|
124
|
+
|
125
|
+
|
126
|
+
_femto_ is the SI prefix for 10<sup>-15</sup>
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/example/App.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'femto'
|
2
|
+
|
3
|
+
class App < Femto::Base
|
4
|
+
|
5
|
+
@template_dir = File.join(__dir__, 'views')
|
6
|
+
|
7
|
+
layout view: 'layout'
|
8
|
+
|
9
|
+
# Set up database
|
10
|
+
Femto::Model::MongoAdapter.connect host: 'localhost', port: 27017, db: 'testing'
|
11
|
+
Femto::Model.adapter = Femto::Model::MongoAdapter
|
12
|
+
|
13
|
+
model :user do |m|
|
14
|
+
m.field :password
|
15
|
+
m.field :username
|
16
|
+
m.field :created_at
|
17
|
+
m.set_method('confirmed?') { 42 }
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/', view: 'home' do
|
21
|
+
@time = Time.now
|
22
|
+
@number = rand 1000
|
23
|
+
end
|
24
|
+
|
25
|
+
get '/json' do
|
26
|
+
json = {features: {json_rendering: true}}
|
27
|
+
render pretty_json: json
|
28
|
+
end
|
29
|
+
|
30
|
+
get '/new_user' do
|
31
|
+
user = User.new
|
32
|
+
user.username = 'username_goes_here'
|
33
|
+
user.password = 'password_goes_here'
|
34
|
+
render text: 'User created'
|
35
|
+
end
|
36
|
+
|
37
|
+
get '/list_users' do
|
38
|
+
users = User.find
|
39
|
+
results = []
|
40
|
+
users.each { |u| results << u.inspect }
|
41
|
+
render pretty_json: {users: results}
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/example/config.ru
ADDED
data/femto.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'femto/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "femto"
|
8
|
+
spec.version = Femto::VERSION
|
9
|
+
spec.authors = ["August"]
|
10
|
+
spec.email = ["augustt198@gmail.com"]
|
11
|
+
spec.description = %q{A tiny web framework}
|
12
|
+
spec.summary = %q{A tiny web framework}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_dependency 'rack', '~> 1.4.5'
|
24
|
+
spec.add_dependency 'tilt', '~> 2.0.1'
|
25
|
+
spec.add_dependency 'haml', '~> 4.0.4'
|
26
|
+
spec.add_dependency 'mongo', '~> 1.10.0'
|
27
|
+
spec.add_dependency 'bson_ext', '~> 1.10.0'
|
28
|
+
end
|
data/lib/femto.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'femto/version'
|
2
|
+
require 'femto/renderer'
|
3
|
+
require 'femto/model'
|
4
|
+
require 'tilt'
|
5
|
+
require 'rack'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module Femto
|
9
|
+
|
10
|
+
class Base
|
11
|
+
class << self
|
12
|
+
|
13
|
+
attr_accessor :request, :response, :env, :routes, :template_dir,
|
14
|
+
:render, :content_type, :layout, :layout_block
|
15
|
+
|
16
|
+
# set up methods
|
17
|
+
%w(get post patch put delete).each do |http_verb|
|
18
|
+
define_method http_verb do |path, options={}, &block|
|
19
|
+
set_route http_verb, path, options, block
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def layout(options={}, &block)
|
24
|
+
if options[:template]
|
25
|
+
@layout = resolve_template options[:template]
|
26
|
+
elsif options[:view]
|
27
|
+
@layout = resolve_template options[:view]
|
28
|
+
end
|
29
|
+
@layout_block = block
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_route(verb, path, options, block)
|
33
|
+
@routes = {} unless @routes
|
34
|
+
@routes[verb] = [] unless @routes[verb]
|
35
|
+
@routes[verb] << [path, options, block]
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_request
|
39
|
+
catch :stop do
|
40
|
+
verb = request.request_method.downcase
|
41
|
+
request_path = request.path_info
|
42
|
+
routes = @routes[verb]
|
43
|
+
if routes
|
44
|
+
routes.each do |group|
|
45
|
+
path = group[0]
|
46
|
+
options = group[1]
|
47
|
+
block = group[2]
|
48
|
+
if request_path == path
|
49
|
+
instance_eval(&block)
|
50
|
+
if options[:template]
|
51
|
+
render options[:template]
|
52
|
+
elsif options[:view]
|
53
|
+
render options[:view]
|
54
|
+
end
|
55
|
+
if @layout and @content_type == 'text/html'
|
56
|
+
if @layout_block
|
57
|
+
@layout_block.call
|
58
|
+
end
|
59
|
+
@render = render_template(@layout) { @render }
|
60
|
+
end
|
61
|
+
if @render == nil
|
62
|
+
raise TemplateMissingException.new
|
63
|
+
end
|
64
|
+
response.write @render
|
65
|
+
response['Content-Type'] = content_type
|
66
|
+
return
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
stop
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def render(options)
|
75
|
+
if options.is_a? Hash
|
76
|
+
pair = options.to_a[0]
|
77
|
+
type = pair[0]
|
78
|
+
content = pair[1]
|
79
|
+
render_pair = Femto::Renderer.render(type, content)
|
80
|
+
@content_type = render_pair[0]
|
81
|
+
@render = render_pair[1]
|
82
|
+
elsif options.is_a? String
|
83
|
+
@render = render_template(resolve_template(options))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def model(name, &block)
|
88
|
+
Femto::Model.create_model name.to_s, &block
|
89
|
+
end
|
90
|
+
|
91
|
+
def resolve_template(file)
|
92
|
+
Dir[File.join(@template_dir, file + '.*')][0]
|
93
|
+
end
|
94
|
+
|
95
|
+
def render_template(template, &block)
|
96
|
+
@content_type = 'text/html'
|
97
|
+
Tilt.new(template).render self, {}, &block
|
98
|
+
end
|
99
|
+
|
100
|
+
def stop
|
101
|
+
throw :stop
|
102
|
+
end
|
103
|
+
|
104
|
+
def builder
|
105
|
+
@builder = Rack::Builder.new unless @builder
|
106
|
+
@builder
|
107
|
+
end
|
108
|
+
|
109
|
+
def call(env)
|
110
|
+
dup.call! env
|
111
|
+
end
|
112
|
+
|
113
|
+
def call!(env)
|
114
|
+
env['PATH_INFO'] = '/' if env['PATH_INFO'].empty?
|
115
|
+
@request = Rack::Request.new env
|
116
|
+
@response = Rack::Response.new
|
117
|
+
@render = nil
|
118
|
+
@content_type = nil
|
119
|
+
@env = env
|
120
|
+
handle_request
|
121
|
+
@response.finish
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
class TemplateMissingException < Exception
|
127
|
+
def initialize
|
128
|
+
super 'No template to be rendered was found'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
class RequestException < Exception
|
135
|
+
attr_reader :code
|
136
|
+
attr_reader :message
|
137
|
+
def initialize(code, msg)
|
138
|
+
super "#{code}: #{msg}"
|
139
|
+
@code = code
|
140
|
+
@message = msg
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
data/lib/femto/model.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'femto/model/mongo_adapter'
|
2
|
+
|
3
|
+
module Femto
|
4
|
+
|
5
|
+
module Model
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :adapter
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.create_model(name, &block)
|
12
|
+
class_name = camelize name
|
13
|
+
begin
|
14
|
+
Object.const_get class_name
|
15
|
+
return # Class already exists
|
16
|
+
rescue NameError
|
17
|
+
# ignored
|
18
|
+
end
|
19
|
+
model_creator = ModelCreator.new
|
20
|
+
block.call model_creator
|
21
|
+
|
22
|
+
# Create class
|
23
|
+
model_class = Class.new &model_creator.class_opts
|
24
|
+
Object.const_set(class_name, model_class)
|
25
|
+
|
26
|
+
# Create accessors for attributes
|
27
|
+
model_creator.fields.each do |a|
|
28
|
+
model_class.module_eval { attr_accessor a }
|
29
|
+
end
|
30
|
+
model_class.module_eval { attr_accessor :id }
|
31
|
+
|
32
|
+
# Add custom methods
|
33
|
+
model_creator.custom_methods.each_pair do |method_name, method_block|
|
34
|
+
model_class.module_eval { define_method method_name, &method_block }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add model methods
|
38
|
+
model_class.define_singleton_method 'find' do |query={}|
|
39
|
+
Model.adapter.find(model_class, query)
|
40
|
+
end
|
41
|
+
model_class.define_singleton_method 'where' do |query={}|
|
42
|
+
Model.adapter.find(model_class, query)
|
43
|
+
end
|
44
|
+
model_class.define_singleton_method 'create' do |fields={}|
|
45
|
+
model = model_class.new fields
|
46
|
+
model.save
|
47
|
+
model
|
48
|
+
end
|
49
|
+
model_class.define_singleton_method 'all' do
|
50
|
+
model_class.find
|
51
|
+
end
|
52
|
+
|
53
|
+
model_class.module_eval do
|
54
|
+
define_method 'update' do
|
55
|
+
Model.adapter.update self
|
56
|
+
end
|
57
|
+
|
58
|
+
define_method 'save' do
|
59
|
+
Model.adapter.update self
|
60
|
+
end
|
61
|
+
|
62
|
+
define_method('remove') { Model.adapter.remove self }
|
63
|
+
define_method('delete') { Model.adapter.remove self }
|
64
|
+
|
65
|
+
define_method 'to_hash' do
|
66
|
+
result = {}
|
67
|
+
self.class.model_attrs[:fields].each do |f|
|
68
|
+
val = send f
|
69
|
+
result[f] = val if val
|
70
|
+
end
|
71
|
+
result
|
72
|
+
end
|
73
|
+
|
74
|
+
define_method 'initialize' do |fields={}|
|
75
|
+
fields.each_pair do |key, val|
|
76
|
+
next unless self.class.model_attrs[:fields].include? key
|
77
|
+
send key.to_s + '=', val
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Model attributes (fields, storage, etc...)
|
83
|
+
class << model_class
|
84
|
+
attr_accessor :model_attrs
|
85
|
+
end
|
86
|
+
storage = model_creator.storage_name ? model_creator.storage_name.to_s : name + 's'
|
87
|
+
model_class.model_attrs = {
|
88
|
+
fields: model_creator.fields,
|
89
|
+
storage_name: storage
|
90
|
+
}
|
91
|
+
|
92
|
+
# Create method for getting defined fields
|
93
|
+
model_class.define_singleton_method('fields') { model_creator.fields }
|
94
|
+
end
|
95
|
+
|
96
|
+
class ModelCreator
|
97
|
+
attr_accessor :fields
|
98
|
+
attr_accessor :defaults
|
99
|
+
attr_accessor :types
|
100
|
+
attr_accessor :class_opts
|
101
|
+
attr_accessor :custom_methods
|
102
|
+
attr_accessor :storage_name
|
103
|
+
|
104
|
+
def initialize
|
105
|
+
@fields = []
|
106
|
+
@defaults = {}
|
107
|
+
@types = {}
|
108
|
+
@custom_methods = {}
|
109
|
+
end
|
110
|
+
|
111
|
+
def field(name, options={})
|
112
|
+
return unless name
|
113
|
+
fields << name
|
114
|
+
if options[:default]
|
115
|
+
defaults[name]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def class_options(&block)
|
120
|
+
@class_opts = block
|
121
|
+
end
|
122
|
+
|
123
|
+
def set_method(name, &block)
|
124
|
+
custom_methods[name] = block
|
125
|
+
end
|
126
|
+
|
127
|
+
def storage(name)
|
128
|
+
@storage_name = name
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Taken from http://infovore.org/archives/2006/08/11/writing-your-own-camelizer-in-ruby/
|
133
|
+
def self.camelize(snake_case, first_upcase=true)
|
134
|
+
if first_upcase
|
135
|
+
snake_case.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
136
|
+
else
|
137
|
+
snake_case.first + camelize(snake_case)[1..-1]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
|
3
|
+
module Femto
|
4
|
+
module Model
|
5
|
+
class MongoAdapter
|
6
|
+
class << self
|
7
|
+
|
8
|
+
attr_accessor :client
|
9
|
+
attr_accessor :db
|
10
|
+
|
11
|
+
def connect(options=nil)
|
12
|
+
if options
|
13
|
+
@client = Mongo::Connection.new(options[:host], options[:port])
|
14
|
+
@db = @client[options[:db]]
|
15
|
+
else
|
16
|
+
@client = Mongo::Connection.new
|
17
|
+
@db = @client['test']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_hash(model)
|
22
|
+
result = {}
|
23
|
+
model.class.model_attrs[:fields].each do |f|
|
24
|
+
var = model.send f
|
25
|
+
result[f] = var if var
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
def find(cls, query)
|
31
|
+
results = []
|
32
|
+
get_coll(cls).find(query).each do |res|
|
33
|
+
model = cls.new(symbolize_keys(res))
|
34
|
+
model.id = res['_id']
|
35
|
+
results << model
|
36
|
+
end
|
37
|
+
results
|
38
|
+
end
|
39
|
+
|
40
|
+
def update(model)
|
41
|
+
coll = get_coll model.class
|
42
|
+
if model.id
|
43
|
+
coll.update({:_id => model.id}, model.to_hash)
|
44
|
+
else
|
45
|
+
model.id = coll.insert model.to_hash
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove(model)
|
50
|
+
coll = get_coll model.class
|
51
|
+
if model.id
|
52
|
+
coll.remove(:_id => model.id)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_coll(cls)
|
57
|
+
@db[cls.model_attrs[:storage_name]]
|
58
|
+
end
|
59
|
+
|
60
|
+
def symbolize_keys(hash)
|
61
|
+
hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Femto
|
4
|
+
|
5
|
+
module Renderer
|
6
|
+
|
7
|
+
@renderers = {}
|
8
|
+
|
9
|
+
def self.render(type, content)
|
10
|
+
renderer = @renderers[type]
|
11
|
+
raise RendererNotFoundException.new('No renderer for type ' + type.inspect) unless renderer
|
12
|
+
renderer.call content
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.renderer(type, &block)
|
16
|
+
@renderers[type] = block
|
17
|
+
end
|
18
|
+
|
19
|
+
# Setup default renderers
|
20
|
+
renderer(:json) { |c| ['application/json', c.to_json] }
|
21
|
+
renderer(:pretty_json) { |c| ['application/json', JSON.pretty_generate(c)] }
|
22
|
+
renderer(:text) { |c| ['text/plain', c.to_s] }
|
23
|
+
|
24
|
+
class RendererNotFoundException < Exception
|
25
|
+
def initialize(reason)
|
26
|
+
super reason
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: femto
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- August
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.4.5
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.4.5
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: tilt
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.0.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.0.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: haml
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 4.0.4
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 4.0.4
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mongo
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.10.0
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.10.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bson_ext
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.10.0
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.10.0
|
111
|
+
description: A tiny web framework
|
112
|
+
email:
|
113
|
+
- augustt198@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- Gemfile
|
120
|
+
- LICENSE.txt
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- example/App.rb
|
124
|
+
- example/config.ru
|
125
|
+
- example/views/home.haml
|
126
|
+
- example/views/layout.haml
|
127
|
+
- femto.gemspec
|
128
|
+
- lib/femto.rb
|
129
|
+
- lib/femto/model.rb
|
130
|
+
- lib/femto/model/mongo_adapter.rb
|
131
|
+
- lib/femto/renderer.rb
|
132
|
+
- lib/femto/version.rb
|
133
|
+
homepage: ''
|
134
|
+
licenses:
|
135
|
+
- MIT
|
136
|
+
metadata: {}
|
137
|
+
post_install_message:
|
138
|
+
rdoc_options: []
|
139
|
+
require_paths:
|
140
|
+
- lib
|
141
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
requirements: []
|
152
|
+
rubyforge_project:
|
153
|
+
rubygems_version: 2.2.0.rc.1
|
154
|
+
signing_key:
|
155
|
+
specification_version: 4
|
156
|
+
summary: A tiny web framework
|
157
|
+
test_files: []
|