├── view
├── admin
│ ├── auth
│ │ ├── logout.erb
│ │ ├── index.erb
│ │ ├── login.erb
│ │ └── signup.erb
│ ├── posts
│ │ ├── edit.erb
│ │ └── index.erb
│ ├── quotes
│ │ └── index.erb
│ └── articles
│ │ ├── show.erb
│ │ ├── new.erb
│ │ └── index.erb
├── bookmarklet.erb
└── index.erb
├── Rakefile
├── public
├── favicon.ico
├── images
│ └── bg.png
├── css
│ ├── styles.css
│ ├── text.css
│ ├── layout.css
│ ├── grid.css
│ └── reset.css
└── js
│ └── quote_article.js
├── model
├── hyperlink.rb
├── init.rb
├── quote.rb
├── tag.rb
├── bookmarklet.rb
├── taggable.rb
├── post.rb
├── article.rb
└── user.rb
├── Gemfile
├── controller
├── admin
│ ├── quotes.rb
│ ├── home.rb
│ ├── posts.rb
│ ├── articles.rb
│ └── auth.rb
├── init.rb
├── citations.rb
├── main.rb
└── admin.rb
├── app.rb
├── config.ru
├── Gemfile.lock
├── README.md
├── spec
└── helper.rb
├── task
└── ramaze.rake
└── layout
└── default.erb
/view/admin/auth/logout.erb:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/view/admin/posts/edit.erb:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | Dir.glob(File.expand_path('../task/*.rake', __FILE__)) { |task| import task }
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csaunders/citation/master/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/csaunders/citation/master/public/images/bg.png
--------------------------------------------------------------------------------
/view/admin/quotes/index.erb:
--------------------------------------------------------------------------------
1 | <% @quotes.each do |quote| %>
2 |
<%= quote.contents %>
3 | <% end %>
4 |
--------------------------------------------------------------------------------
/view/admin/articles/show.erb:
--------------------------------------------------------------------------------
1 | <% @article.quotes.each do |quote| %>
2 | <%= quote.contents %>
3 | <% end %>
4 |
--------------------------------------------------------------------------------
/model/hyperlink.rb:
--------------------------------------------------------------------------------
1 | class Hyperlink
2 | attr_reader :url, :title
3 | def initialize(url: nil, title: title)
4 | @url = url
5 | @title = title
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/view/admin/auth/index.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= render_partial(:signup) %>
3 |
4 |
5 |
6 | <%= render_partial(:login) %>
7 |
8 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'ramaze', '>= 2.0.0'
4 | gem 'sequel'
5 | gem 'sqlite3'
6 | gem 'bcrypt'
7 | gem 'rake'
8 |
9 | gem 'pry'
10 | gem 'pry-debugger'
11 |
--------------------------------------------------------------------------------
/controller/admin/quotes.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class Quotes < AdminController
3 | map '/quotes'
4 |
5 | def index
6 | @quotes = Quote.all
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/controller/admin/home.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class Home < AdminController
3 | map '/home'
4 |
5 | def index
6 | @title = "Welcome to your Admin"
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/view/admin/auth/login.erb:
--------------------------------------------------------------------------------
1 | Login
2 |
3 | <%=
4 | form_for(@article, method: :post, action: Admin::Auth.r(:create)) do |f|
5 | f.input_text('Username', :username, class: 'row')
6 | f.input_password('Password', :password, class: 'row')
7 |
8 | f.input_submit('Submit', class: 'row')
9 | end
10 | %>
11 |
--------------------------------------------------------------------------------
/view/admin/articles/new.erb:
--------------------------------------------------------------------------------
1 | Add an Article
2 |
3 | <%=
4 | form_for(@article, method: :post, action: Admin::Articles.r(:save)) do |f|
5 | f.input_text('Title', :title, class: 'row')
6 | f.input_text('Article URL', :url, class: 'row')
7 | f.input_text('Tags', :tagstring, class: 'row')
8 |
9 | f.input_submit('Submit', class: 'row')
10 | end
11 | %>
12 |
--------------------------------------------------------------------------------
/model/init.rb:
--------------------------------------------------------------------------------
1 | require 'sequel'
2 | require 'bcrypt'
3 |
4 | Sequel::Model.plugin(:schema)
5 | DB=Sequel.sqlite('./app.sqlite')
6 |
7 | require __DIR__('taggable')
8 | require __DIR__('tag')
9 | require __DIR__('user')
10 | require __DIR__('article')
11 | require __DIR__('quote')
12 | require __DIR__('hyperlink')
13 | require __DIR__('bookmarklet')
14 | require __DIR__('post')
15 |
16 |
--------------------------------------------------------------------------------
/view/admin/auth/signup.erb:
--------------------------------------------------------------------------------
1 | Sign up
2 |
3 | <%=
4 | form_for(@article, method: :post, action: Admin::Auth.r(:create)) do |f|
5 | f.input_text('Username', :username, class: 'row')
6 | f.input_password('Password', :password, class: 'row')
7 | f.input_password('Confirm Password', :confirmation, class: 'row')
8 |
9 | f.input_submit('Submit', class: 'row')
10 | end
11 | %>
12 |
--------------------------------------------------------------------------------
/model/quote.rb:
--------------------------------------------------------------------------------
1 | class Quote < Sequel::Model
2 | many_to_one :article
3 | many_to_many :tags, left_key: :taggable_id, right_key: :tag_id, join_table: :tags_quote_tags
4 |
5 | set_schema do
6 | primary_key :id
7 | Integer :article_id
8 | String :contents, text: true
9 | end
10 |
11 | create_table unless table_exists?
12 | end
13 |
14 | class QuotesTags < Sequel::Model
15 | include Taggable
16 | tagged_on Quote
17 | end
18 |
--------------------------------------------------------------------------------
/public/css/styles.css:
--------------------------------------------------------------------------------
1 | .tag {
2 | background-color: #5D9CE3;
3 | border-color: #3184E0;
4 | border-radius: 5px;
5 | padding: 3px;
6 | margin: 5px;
7 | color: white;
8 | display: block;
9 | float: left;
10 | }
11 |
12 | .listing-header {
13 | text-align: left;
14 | }
15 |
16 | .listing > tr > td {
17 | padding: 10px;
18 | vertical-align: middle;
19 | }
20 |
21 | .listing > tr:nth-child(2n+1) {
22 | background-color: #dedede;
23 | }
24 |
--------------------------------------------------------------------------------
/view/bookmarklet.erb:
--------------------------------------------------------------------------------
1 | javascript:(function(){
2 | quote = function(){Citation.Quote({token: '<%= @token %>', url: '//<%= @citation_path %>'})};
3 | if(document.getElementById('citation-needed') !== null){
4 | quote();
5 | } else {
6 | var bookmarklet = document.createElement('script');
7 | bookmarklet.onload = quote;
8 | bookmarklet.src = '//<%= @script_path %>';
9 | bookmarklet.id = 'citation-needed';
10 | document.body.appendChild(bookmarklet);
11 | }
12 | }());
13 |
--------------------------------------------------------------------------------
/app.rb:
--------------------------------------------------------------------------------
1 | # This file contains your application, it requires dependencies and necessary
2 | # parts of the application.
3 | require 'rubygems'
4 | require 'ramaze'
5 | require 'securerandom'
6 | require 'pry'
7 | require 'pry-debugger'
8 |
9 | # Make sure that Ramaze knows where you are
10 | Ramaze.options.roots = [__DIR__]
11 |
12 | require __DIR__('model/init')
13 | require __DIR__('controller/init')
14 |
15 | Ramaze::Cache.options.session = Ramaze::Cache::Sequel.using(connection: DB, table: :citation_sessions)
16 |
17 | Bookmarklet.host = 'localhost:7000'
18 |
--------------------------------------------------------------------------------
/model/tag.rb:
--------------------------------------------------------------------------------
1 | class Tag < Sequel::Model
2 | set_schema do
3 | primary_key :id
4 | String :name
5 | index :name
6 | end
7 |
8 | create_table unless table_exists?
9 |
10 | def self.normalize(tag)
11 | tag.strip.downcase
12 | end
13 |
14 | def self.find_or_create_missing(tags)
15 | tags = tags.sort.uniq
16 | entries = order(:name).where(name: tags).all
17 | tags.map do |tag|
18 | entry = entries.shift
19 | if entry.nil? || entry.name != tag
20 | entry = new(name: tag).save
21 | entry.save
22 | end
23 | entry
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/model/bookmarklet.rb:
--------------------------------------------------------------------------------
1 | class Bookmarklet
2 | attr_reader :token, :script_path, :citation_path
3 |
4 | def self.host=(host)
5 | @@host = host
6 | end
7 |
8 | def initialize(user)
9 | @token = user.token
10 | @script_path = determine_script_path
11 | @citation_path = "#{@@host}/#{Citations.r('')}"
12 | end
13 |
14 | def generate(context)
15 | context.render_file(__DIR__('../view/bookmarklet.erb'), token: token, script_path: script_path, citation_path: citation_path)
16 | end
17 |
18 | private
19 |
20 | def determine_script_path
21 | "#{@@host}/js/quote_article.js"
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/view/admin/posts/index.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= Admin::Posts.a('Add Post', :edit, nil) %>
4 |
5 |
6 |
7 |
8 | | Title |
9 | Tags |
10 | Published? |
11 |
12 | |
13 |
14 | <% @posts.each do |post| %>
15 | <%= Admin::Posts.a(post.title, :show, post.id) %> |
16 | <%= post.tags %> |
17 |
18 | <% if post.published %>
19 | published on <%= post.published_at %>
20 | <% end %>
21 | |
22 |
23 | <% end %>
24 | |
25 |
26 |
--------------------------------------------------------------------------------
/model/taggable.rb:
--------------------------------------------------------------------------------
1 | module Taggable
2 | def self.included(base)
3 | base.extend(ClassMethods)
4 | base.class_eval do
5 | set_schema do
6 | Integer :tag_id
7 | Integer :taggable_id
8 | index [:tag_id, :taggable_id], unique: true
9 | end
10 | create_table unless table_exists?
11 | end
12 | end
13 |
14 | module ClassMethods
15 | def tagged_on(model)
16 | table = model.table_name.to_sym
17 | join_table = "#{table}_tags".to_sym
18 | model.many_to_many :tags, left_key: :tag_id, right_key: :taggable_id, join_table: join_table
19 | Tag.many_to_many table, left_key: :taggable_id, right_key: :tag_id, join_table: join_table
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/model/post.rb:
--------------------------------------------------------------------------------
1 | class Post < Sequel::Model
2 | many_to_one :user
3 | many_to_many :tags, left_key: :taggable_id, right_key: :tag_id, join_table: :tags_post_tags
4 |
5 | set_schema do
6 | primary_key :id
7 | Integer :user_id
8 |
9 | String :title
10 | String :body, text: true
11 | String :html, text: true
12 | DateTime :created_at, null: false
13 | DateTime :published_at
14 | TrueClass :published, default: true
15 | index [:user_id, :published]
16 | index [:id, :user_id]
17 | end
18 |
19 | create_table unless table_exists?
20 |
21 | private
22 | def before_create
23 | self.created_at = Time.now.utc
24 | end
25 | end
26 |
27 | class PostsTags < Sequel::Model
28 | include Taggable
29 | tagged_on Post
30 | end
31 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rackup
2 | #
3 | # config.ru for ramaze apps
4 | #
5 | # Rackup is a useful tool for running Rack applications, which uses the
6 | # Rack::Builder DSL to configure middleware and build up applications easily.
7 | #
8 | # Rackup automatically figures out the environment it is run in, and runs your
9 | # application as FastCGI, CGI, or standalone with Mongrel or WEBrick -- all from
10 | # the same configuration.
11 | #
12 | # Do not set the adapter.handler in here, it will be ignored.
13 | # You can choose the adapter like `ramaze start -s mongrel` or set it in the
14 | # 'start.rb' and use `ruby start.rb` instead.
15 | require ::File.expand_path('../app', __FILE__)
16 |
17 | Ramaze.start(:root => Ramaze.options.roots, :started => true)
18 |
19 | run Ramaze
20 |
--------------------------------------------------------------------------------
/controller/init.rb:
--------------------------------------------------------------------------------
1 | # Define a subclass of Ramaze::Controller holding your defaults for all controllers. Note
2 | # that these changes can be overwritten in sub controllers by simply calling the method
3 | # but with a different value.
4 | class Controller < Ramaze::Controller
5 | layout :default
6 | helper :blue_form, :xhtml
7 | engine :erb
8 |
9 | def hyperlinks
10 | []
11 | end
12 |
13 | before_all do
14 | @hyperlinks = hyperlinks
15 | end
16 |
17 | end
18 |
19 | # Here you can require all your other controllers. Note that if you have multiple
20 | # controllers you might want to do something like the following:
21 | #
22 | # Dir.glob('controller/*.rb').each do |controller|
23 | # require(controller)
24 | # end
25 | #
26 | require __DIR__('main')
27 | require __DIR__('citations')
28 | require __DIR__('admin')
29 |
--------------------------------------------------------------------------------
/controller/admin/posts.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class Posts < AdminController
3 | map '/posts'
4 |
5 | def index
6 | @posts = current_user.posts
7 | end
8 |
9 | def edit(id)
10 | @post = find_post(id)
11 | end
12 |
13 | def save
14 | redirect Posts.r('') unless request.post? || request.put?
15 | data = request.subset(*%i(title body published))
16 | post = find_post(request.subset(:id)['id'])
17 | post.set(data)
18 | post.save
19 | end
20 |
21 | def destroy(id)
22 | if request.delete? && (post = find_post(id))
23 | post.delete
24 | end
25 | redirect Posts.r('')
26 | end
27 |
28 | private
29 | def find_post(id)
30 | Post.where(id: id, user_id: current_user.id).first || Post.new(user_id: current_user.id)
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/controller/admin/articles.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class Articles < AdminController
3 | engine :erb
4 | helper :blue_form
5 | map '/articles'
6 |
7 | def index
8 | @bookmarklet = Bookmarklet.new(User.first)
9 | @title = "Articles"
10 | @articles = current_user.articles
11 | end
12 |
13 | def show(id)
14 | @article = Article.where(id: id, user_id: current_user.id).first
15 | @title = "Articles / #{@article.title}"
16 | end
17 |
18 | def new
19 | @title = "Create a new article"
20 | @article = Article.new
21 | end
22 |
23 | def save
24 | if request.post?
25 | data = request.subset(:id, :url, :title, :tagstring)
26 |
27 | form = ArticleForm.new(current_user, data)
28 | form.save
29 | end
30 |
31 | redirect Articles.r('')
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/view/admin/articles/index.erb:
--------------------------------------------------------------------------------
1 |
2 |
Articles
3 |
4 |
9 |
10 | <% @articles.each do |article| %>
11 |
12 | | <%= Admin::Articles.a(article.title, :show, article.id) %> |
13 | <%= anchor(article.url) %> |
14 |
15 | <% article.tags.each do |tag| %>
16 | <%= tag.name %>
17 | <% end %>
18 | |
19 |
20 | <% end %>
21 |
22 |
23 |
24 |
25 |
26 | <%= render_partial(:new) %>
27 |
28 |
29 |
32 |
--------------------------------------------------------------------------------
/controller/citations.rb:
--------------------------------------------------------------------------------
1 | require 'pry-debugger'
2 | class Citations < Controller
3 | map '/citations'
4 | trait :default_action_name => 'create'
5 |
6 | def create
7 | attrs = request.subset(:token, :quote, :url, :title)
8 | user = fetch_user(attrs['token'])
9 | respond('Unauthorized', status: 401) unless user
10 |
11 | article = fetch_or_create_article(user, attrs['title'], attrs['url'])
12 | article.add_quote(contents: attrs['quote'])
13 | respond('Citation.Success("abracadabra")')
14 | end
15 |
16 | private
17 |
18 | def fetch_user(token)
19 | User.where(token: token).first
20 | end
21 |
22 | def fetch_or_create_article(user, title, url)
23 | unless article = Article.where(title: title, url: url, user_id: user.id).first
24 | article = Article.new(user_id: user.id, title: title, url: url)
25 | article.save
26 | end
27 | article
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/controller/main.rb:
--------------------------------------------------------------------------------
1 | # Default url mappings are:
2 | #
3 | # * a controller called Main is mapped on the root of the site: /
4 | # * a controller called Something is mapped on: /something
5 | #
6 | # If you want to override this, add a line like this inside the class:
7 | #
8 | # map '/otherurl'
9 | #
10 | # this will force the controller to be mounted on: /otherurl.
11 | class MainController < Controller
12 | engine :erb
13 | # the index action is called automatically when no other action is specified
14 | def index
15 | @title = 'Welcome to Ramaze!'
16 | end
17 |
18 | # the string returned at the end of the function is used as the html body
19 | # if there is no template for the action. if there is a template, the string
20 | # is silently ignored
21 | def notemplate
22 | @title = 'Welcome to Ramaze!'
23 |
24 | return 'There is no \'notemplate.xhtml\' associated with this action.'
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/controller/admin/auth.rb:
--------------------------------------------------------------------------------
1 | module Admin
2 | class Auth < AdminController
3 | map '/auth'
4 | skip_authentication :index, :signup, :login, :create
5 |
6 | def index
7 | @title = "Auth Index"
8 | end
9 |
10 | def signup
11 | @user = User.new
12 | end
13 |
14 | def login
15 | @user = User.new
16 | end
17 |
18 | def create
19 | if request.post?
20 | attrs = request.subset('username', 'password', 'confirmation')
21 | if User.exists?(attrs['username'])
22 | user = User.authenticate(attrs)
23 | else
24 | user = User.new(username: attrs['username'])
25 | user.set_password(password: attrs['password'], confirmation: attrs['confirmation'])
26 | user.save
27 | end
28 | end
29 | set_user(user)
30 | redirect Admin::Home.r('')
31 | end
32 |
33 | def logout
34 | session.clear
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | bcrypt (3.1.7)
5 | coderay (1.1.0)
6 | columnize (0.8.9)
7 | debugger (1.6.6)
8 | columnize (>= 0.3.1)
9 | debugger-linecache (~> 1.2.0)
10 | debugger-ruby_core_source (~> 1.3.2)
11 | debugger-linecache (1.2.0)
12 | debugger-ruby_core_source (1.3.5)
13 | innate (2013.02.21)
14 | rack (~> 1.5.2)
15 | method_source (0.8.2)
16 | pry (0.9.12.6)
17 | coderay (~> 1.0)
18 | method_source (~> 0.8)
19 | slop (~> 3.4)
20 | pry-debugger (0.2.2)
21 | debugger (~> 1.3)
22 | pry (~> 0.9.10)
23 | rack (1.5.2)
24 | rake (10.3.2)
25 | ramaze (2012.12.08)
26 | innate (>= 2012.12)
27 | rake
28 | sequel (4.11.0)
29 | slop (3.5.0)
30 | sqlite3 (1.3.9)
31 |
32 | PLATFORMS
33 | ruby
34 |
35 | DEPENDENCIES
36 | bcrypt
37 | pry
38 | pry-debugger
39 | rake
40 | ramaze (>= 2.0.0)
41 | sequel
42 | sqlite3
43 |
--------------------------------------------------------------------------------
/model/article.rb:
--------------------------------------------------------------------------------
1 | class Article < Sequel::Model
2 | one_to_many :quotes
3 | many_to_one :user
4 | many_to_many :tags, left_key: :taggable_id, right_key: :tag_id, join_table: :tags_article_tags
5 |
6 | set_schema do
7 | primary_key :id
8 | Integer :user_id
9 |
10 | String :url
11 | String :title
12 | index :user_id
13 | end
14 |
15 | create_table unless table_exists?
16 |
17 | def tagstring
18 | tags.join(',')
19 | end
20 | end
21 |
22 | class ArticlesTags < Sequel::Model
23 | include Taggable
24 | tagged_on Article
25 | end
26 |
27 | class ArticleForm
28 | attr_reader :tagstring
29 | def initialize(user, attrs)
30 | @tagstring = attrs.delete('tagstring')
31 | @article = Article.where(id: attrs['id'], user_id: user.id).first || Article.new(user_id: user.id)
32 | @article.set(attrs)
33 | end
34 |
35 | def save
36 | tags = tagstring.split(',').map { |t| Tag.normalize(t) }
37 | tag_entities = Tag.find_or_create_missing(tags)
38 |
39 | @article.save
40 | tag_entities.each { |tag| @article.add_tag(tag) }
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ramaze Application
2 |
3 | Welcome to your new Ramaze application. This README serves as a starting point
4 | for writing an application using the code you just generated.
5 |
6 | Once you've started working on your application you'll probably want to update
7 | this README so that its contents reflect your application's state and purpose.
8 |
9 | ## Requirements
10 |
11 | In order to run this application you'll need to have Ramaze 2.0.0 or newer and
12 | Rake. Optionally you can install Bundler and use it for Gem management, this
13 | can be done as following:
14 |
15 | $ gem install bundler
16 | $ bundle install
17 |
18 | ## Rake Tasks
19 |
20 | This application comes with a few predefined Rake tasks that make it easy to
21 | get started. You can list these tasks by running `rake -T` or `rake -D` (this
22 | shows longer descriptions for tasks if there are any).
23 |
24 | For example, to start a Ramaze console using Pry you'd run the following
25 | command:
26 |
27 | $ rake ramaze:pry
28 |
29 | ## Resources
30 |
31 | In case you need help you can refer to the following resources:
32 |
33 | * Ramaze website:
34 | * Github repository:
35 | * IRC channel: \#ramaze on Freenode
36 |
--------------------------------------------------------------------------------
/spec/helper.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../app', __FILE__)
2 |
3 | # This file can be used to set various configuration options for your testing
4 | # suite. Ramaze itself uses Bacon but you're not forced to use this. Want to
5 | # use Rspec instead? Go ahead!
6 | #
7 | # If you do happen to use Bacon you can uncomment the following lines to get
8 | # started with testing Ramaze using Bacon:
9 | #
10 | # require 'bacon'
11 | # require 'ramaze/spec/bacon'
12 |
13 | # The following code is an example on how to set up Capybara
14 | # (https://github.com/jnicklas/capybara) for Ramaze. If you don't use Capybara
15 | # you can safely remove these comments.
16 | #
17 | # require 'capybara/dsl'
18 | #
19 | # Capybara.configure do |config|
20 | # config.default_driver = :rack_test
21 | # config.app = Ramaze
22 | # end
23 | #
24 | # shared :capybara do
25 | # Ramaze.setup_dependencies
26 | #
27 | # extend Capybara::DSL
28 | # end
29 | #
30 |
31 | # The following few lines of code are the most basic settings you'll want to
32 | # use for testing Ramaze. They ensure that the environment is set correctly and
33 | # that your output isn't clogged with non important information.
34 | Ramaze.middleware :spec do
35 | run Ramaze.core
36 | end
37 |
38 | Ramaze::Log.level = Logger::ERROR
39 | Ramaze.options.mode = :spec
40 |
--------------------------------------------------------------------------------
/model/user.rb:
--------------------------------------------------------------------------------
1 | class User < Sequel::Model
2 | one_to_many :articles
3 | one_to_many :posts
4 |
5 | set_schema do
6 | primary_key :id
7 | String :username, null: false
8 | String :password, null: false
9 | String :token
10 |
11 | index :username, unique: true
12 | index :token, unique: true
13 | end
14 |
15 | create_table unless table_exists?
16 |
17 | def before_create
18 | self.token = SecureRandom.hex(16)
19 | end
20 |
21 | def self.authenticate(creds)
22 | username, password = creds['username'], creds['password']
23 |
24 | if blank?(username) || blank?(password)
25 | return false
26 | end
27 |
28 | user = where(username: username).first
29 |
30 | if user && user.password == password
31 | return user
32 | else
33 | return false
34 | end
35 | end
36 |
37 | def self.exists?(username)
38 | !where(username: username).first.nil?
39 | end
40 |
41 | def set_password(password: nil, confirmation: nil)
42 | if blank?(password) || blank?(confirmation)
43 | return
44 | elsif password != confirmation
45 | return
46 | end
47 |
48 | self.password = BCrypt::Password.create(password, cost: 10)
49 | end
50 |
51 | def password
52 | password = super
53 |
54 | if !password.nil?
55 | return BCrypt::Password.new(password)
56 | else
57 | return nil
58 | end
59 | end
60 |
61 | private
62 | def self.blank?(str)
63 | str.nil? || str.length <= 0
64 | end
65 |
66 | def blank?(str)
67 | User.blank?(str)
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/task/ramaze.rake:
--------------------------------------------------------------------------------
1 | # This file contains a predefined set of Rake tasks that can be useful when
2 | # developing Ramaze applications. You're free to modify these tasks to your
3 | # liking, they will not be overwritten when updating Ramaze.
4 |
5 | namespace :ramaze do
6 | app = File.expand_path('../../app', __FILE__)
7 |
8 | desc 'Starts a Ramaze console using IRB'
9 | task :irb do
10 | require app
11 | require 'irb'
12 | require 'irb/completion'
13 |
14 | ARGV.clear
15 | IRB.start
16 | end
17 |
18 | # Pry can be installed using `gem install pry`.
19 | desc 'Starts a Ramaze console using Pry'
20 | task :pry do
21 | require app
22 | require 'pry'
23 |
24 | ARGV.clear
25 | Pry.start
26 | end
27 |
28 | # In case you want to use a different server or port you can freely modify
29 | # the options passed to `Ramaze.start()`.
30 | desc 'Starts Ramaze for development'
31 | task :start do
32 | require app
33 |
34 | Ramaze.start(
35 | :adapter => :webrick,
36 | :port => 7000,
37 | :file => __FILE__,
38 | :root => Ramaze.options.roots
39 | )
40 | end
41 |
42 | desc 'Lists all the routes defined using Ramaze::Route'
43 | task :routes do
44 | require app
45 |
46 | if Ramaze::Route::ROUTES.empty?
47 | abort 'No routes have been defined using Ramaze::Route'
48 | end
49 |
50 | spacing = Ramaze::Route::ROUTES.map { |k, v| k.to_s }
51 | spacing = spacing.sort { |l, r| r.length <=> l.length }[0].length
52 |
53 | Ramaze::Route::ROUTES.each do |from, to|
54 | puts "%-#{spacing}s => %s" % [from, to]
55 | end
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/view/index.erb:
--------------------------------------------------------------------------------
1 |
2 | Congratulations, Ramaze is running fine and you can start working on your application.
3 |
4 |
5 |
6 | You can play around with this prototype by changing the following:
7 |
8 |
9 |
10 | -
11 |
view/index.xhtml: the content of this page.
12 |
13 | -
14 |
layout/default.xhtml: the layout for this page.
15 |
16 | -
17 |
controller/main.rb: the controller responsible for server this page.
18 |
19 |
20 |
21 |
22 | For more information, check out ramaze.net and
23 | the documentation.
24 |
25 | You can also read the
26 | YARD documentation
27 | or browse around the Ramaze source code.
28 |
29 |
30 |
31 | For help with Ramaze, visit
32 | #ramaze on irc.freenode.net.
33 |
34 | You can use Mibbit,
35 | an AJAX based IRC client or
36 |
37 | the official freenode irc java applet
38 | .
39 |
40 |
41 |
42 | Feel free to post to the
43 | Ramaze Google Group, your
44 | first mail has to go through moderation, so please be patient.
45 |
46 |
--------------------------------------------------------------------------------
/public/css/text.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Stylesheet used for styling headings, links, etc.
3 | *
4 | * @author Yorick Peterse
5 | * @link http://yorickpeterse.com/
6 | */
7 | body
8 | {
9 | background: url('../images/bg.png') repeat top left;
10 | border-top: 5px solid #444;
11 | color: #444;
12 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, sans-serif;
13 | font-size: 14px;
14 | }
15 |
16 | p
17 | {
18 | font-size: 14px;
19 | line-height: 22px;
20 | margin-bottom: 20px;
21 | }
22 |
23 | a
24 | {
25 | color: #444;
26 | }
27 |
28 | ol
29 | {
30 | margin-left: 20px;
31 | }
32 |
33 | ol li
34 | {
35 | list-style-type: decimal;
36 | }
37 |
38 | ul
39 | {
40 | margin-left: 18px;
41 | }
42 |
43 | ul li
44 | {
45 | list-style-type: disc;
46 | }
47 |
48 | ul, ol
49 | {
50 | line-height: 22px;
51 | margin-bottom: 20px;
52 | }
53 |
54 | a:hover, h1 a:hover
55 | {
56 | color: #E33F1E;
57 | }
58 |
59 | h1, h2, h3, h4, h5, h6
60 | {
61 | font-weight: bold;
62 | margin-bottom: 5px;
63 | }
64 |
65 | h1
66 | {
67 | font-size: 28px;
68 | }
69 |
70 | h2
71 | {
72 | font-size: 24px;
73 | }
74 |
75 | h3
76 | {
77 | font-size: 22px;
78 | }
79 |
80 | h4
81 | {
82 | font-size: 20px;
83 | }
84 |
85 | h5
86 | {
87 | font-size: 18px;
88 | }
89 |
90 | h6
91 | {
92 | font-size: 16px;
93 | }
94 |
95 | h1 a
96 | {
97 | color: #444;
98 | text-decoration: none;
99 | }
100 |
101 | pre
102 | {
103 | margin: 20px 0px;
104 | }
105 |
106 | code
107 | {
108 | background: #eee;
109 | }
110 |
--------------------------------------------------------------------------------
/layout/default.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= @title %>
7 |
8 | <%= css('reset') %>
9 | <%= css('grid') %>
10 | <%= css('layout') %>
11 | <%= css('text') %>
12 | <%= css('styles') %>
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
32 |
33 |
34 |
35 | <%= @content %>
36 |
37 |
38 |
39 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/controller/admin.rb:
--------------------------------------------------------------------------------
1 | class AdminController < Controller
2 | def self.map(location)
3 | super "/admin#{location}"
4 | end
5 | map ''
6 |
7 | def self.configuration
8 | @@configuration ||= {}
9 | end
10 |
11 | def self.skip_authentication(*actions)
12 | raise StandardError, 'Missing required actions to skip' if actions.nil?
13 | configuration[:skip_authentication] ||= {}
14 | configuration[:skip_authentication][self] = actions.map { |a| a.to_s }
15 | end
16 | skip_authentication :unauthorized
17 |
18 | before_all do
19 | redirect AdminController.r('unauthorized') unless authorized?
20 | end
21 |
22 | def skip_authentication
23 | AdminController.skip_authentication
24 | end
25 |
26 | def index
27 | redirect Admin::Home.r('')
28 | end
29 |
30 | def unauthorized
31 | @title = "You are not authorized to see this content"
32 | end
33 |
34 | def hyperlinks
35 | if authorized?
36 | [
37 | Hyperlink.new(url: Admin::Home.r(''), title: 'Home'),
38 | Hyperlink.new(url: Admin::Articles.r(''), title: 'Articles')
39 | ]
40 | else
41 | super
42 | end
43 | end
44 |
45 | protected
46 |
47 | def current_user
48 | @current_user ||= User.where(id: session[:user_id]).first
49 | end
50 |
51 | def set_user(user)
52 | session[:user_id] = user.id if user
53 | end
54 |
55 | def authorized?
56 | skip_authentication? || current_user
57 | end
58 |
59 | def skip_authentication?
60 | skipped_actions.include? action.method
61 | end
62 |
63 | def skipped_actions
64 | AdminController.configuration[:skip_authentication][action.node] || {}
65 | end
66 | end
67 |
68 | require __DIR__('admin/home')
69 | require __DIR__('admin/articles')
70 | require __DIR__('admin/quotes')
71 | require __DIR__('admin/posts')
72 | require __DIR__('admin/auth')
73 |
--------------------------------------------------------------------------------
/public/js/quote_article.js:
--------------------------------------------------------------------------------
1 | if(typeof(Citation) === 'undefined'){
2 | Citation = {};
3 | }
4 |
5 | Citation.Success = function(data) {
6 | console.log(data)
7 | }
8 |
9 | Citation.Modal = function(data, callback) {
10 | modal = document.createElement('div')
11 | modal.setAttribute('style', "width:50%;height:500px;z-index:500;margin-left:0 auto;margin-right:0 auto;background-color:red;position:fixed;left:25%;top:15%");
12 | modal.setAttribute('id', 'citation-add-tags');
13 | modal.setAttribute('class', 'citation-add-article');
14 |
15 | jQuery(modal).fadeIn().fadeOut();
16 | document.body.appendChild(modal);
17 | callback(data);
18 | }
19 |
20 | Citation.Quote = function(data) {
21 | sendQuote = function(data) {
22 | var quote = "";
23 | if (window.getSelection) {
24 | quote = window.getSelection().toString();
25 | } else if (document.selection && document.selection.type !== 'Control') {
26 | quote = document.selection.createRange().text;
27 | }
28 | if (quote !== "") {
29 | url = document.location.toString();
30 | title = document.title;
31 | jQuery.ajax({
32 | url: data.url,
33 | type: 'POST',
34 | jsonp: 'Citation.Success',
35 | dataType: 'jsonp',
36 | data: {
37 | token: data.token,
38 | quote: quote,
39 | url: url,
40 | title: title,
41 | tagstring: ""
42 | },
43 | success: function(response){
44 | console.log(response);
45 | }
46 | });
47 | }
48 | }
49 | if (typeof(jQuery) === 'undefined') {
50 | jq = document.createElement('script');
51 | jq.onload = (function(){Citation.Modal(data, sendQuote)});
52 | jq.src = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js';
53 | document.body.appendChild(jq);
54 | } else {
55 | Citation.Modal(data, sendQuote);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/public/css/layout.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Stylesheet used for the layout of most elements.
3 | *
4 | * @author Yorick Peterse
5 | * @link http://yorickpeterse.com/
6 | */
7 | #container
8 | {
9 | margin: 20px auto;
10 | width: 940px;
11 | }
12 |
13 | #content
14 | {
15 | background: #fff;
16 | border: 1px solid #ddd;
17 | padding: 20px;
18 | width: 898px;
19 | }
20 |
21 | /* Top part of the website, contains the title and the navigation menu */
22 | #top
23 | {
24 | background: #E33F1E;
25 | height: 70px;
26 | margin-bottom: 20px;
27 | padding: 0px 10px;
28 | }
29 |
30 | #top header h1
31 | {
32 | color: #fff;
33 | font-size: 38px;
34 | margin: 10px 0px 0px 0px;
35 | padding: 0px;
36 | }
37 |
38 | #top nav ul
39 | {
40 | float: right;
41 | margin-right: 15px;
42 | }
43 |
44 | #top nav ul li
45 | {
46 | float: left;
47 | font-size: 16px;
48 | list-style-type: none;
49 | margin-right: 10px;
50 | }
51 |
52 | #top nav ul li:last-child
53 | {
54 | margin-right: 0px;
55 | }
56 |
57 | #top nav ul li a
58 | {
59 | color: #fff;
60 | display: block;
61 | height: 45px;
62 | padding: 25px 10px 0px 10px;
63 | text-decoration: none;
64 | }
65 |
66 | #top nav ul li a:hover
67 | {
68 | background: #D43919;
69 | }
70 |
71 | /* Footer at the bottom of the page */
72 | #footer
73 | {
74 | text-align: center;
75 | }
76 |
77 | #footer p
78 | {
79 | font-size: 13px;
80 | margin-bottom: 10px;
81 | }
82 |
--------------------------------------------------------------------------------
/public/css/grid.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | /**
4 | * Modified version of the CSS grid (aka 1140 CSS) that can be found at http://cssgrid.net
5 | *
6 | * This modified version has some extra enhancements to make it a bit easier to work with
7 | * the grid. Along with these minor fixes I also renamed all grid classes to the names
8 | * used by the 960 grid system as I find "grid_6" easier to write than "sixcol".
9 | */
10 | .container
11 | {
12 | overflow: hidden;
13 | padding-left: 1%;
14 | padding-right: 1%;
15 | }
16 |
17 | .row
18 | {
19 | max-width: 96%;
20 | margin: 0 auto;
21 | overflow: hidden;
22 | width: 100%;
23 | }
24 |
25 | .grid_1, .grid_2, .grid_3, .grid_4, .grid_5, .grid_6, .grid_7, .grid_8, .grid_9,
26 | .grid_10, .grid_11
27 | {
28 | float: left;
29 | margin-right: 3.8%;
30 | min-height: 1px;
31 | }
32 |
33 | .row .grid_1
34 | {
35 | width: 4.85%;
36 | }
37 |
38 | .row .grid_2
39 | {
40 | width: 13.45%;
41 | }
42 |
43 | .row .grid_3
44 | {
45 | width: 22.05%;
46 | }
47 |
48 | .row .grid_4
49 | {
50 | width: 30.75%;
51 | }
52 |
53 | .row .grid_5
54 | {
55 | width: 39.45%;
56 | }
57 |
58 | .row .grid_6
59 | {
60 | width: 48%;
61 | }
62 |
63 | .row .grid_7
64 | {
65 | width: 56.75%;
66 | }
67 |
68 | .row .grid_8
69 | {
70 | width: 65.4%;
71 | }
72 |
73 | .row .grid_9
74 | {
75 | width: 74.05%;
76 | }
77 |
78 | .row .grid_10
79 | {
80 | width: 82.7%;
81 | }
82 |
83 | .row .grid_11
84 | {
85 | width: 91.35%;
86 | }
87 |
88 | .row .grid_12
89 | {
90 | float: left;
91 | width: 100%;
92 | }
93 |
94 | .last, .row > *:last-child
95 | {
96 | margin-right: 0px;
97 | }
98 |
99 | img, object, embed
100 | {
101 | max-width: 100%;
102 | }
103 |
104 | img
105 | {
106 | height: auto;
107 | }
108 |
--------------------------------------------------------------------------------
/public/css/reset.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | /**
4 | * http://meyerweb.com/eric/tools/css/reset/
5 | * v2.0 | 20110126
6 | * License: none (public domain)
7 | */
8 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre,
9 | a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small,
10 | strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset,
11 | form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside,
12 | canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output,
13 | ruby, section, summary, time, mark, audio, video
14 | {
15 | border: 0;
16 | font-size: 100%;
17 | font: inherit;
18 | margin: 0;
19 | padding: 0;
20 | vertical-align: baseline;
21 | }
22 |
23 | /* HTML5 display-role reset for older browsers */
24 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section
25 | {
26 | display: block;
27 | }
28 |
29 | body
30 | {
31 | line-height: 1.3;
32 | }
33 |
34 | ol, ul, ol li, ul li
35 | {
36 | list-style-type: none;
37 | margin: 0px;
38 | padding: 0px;
39 | }
40 |
41 | blockquote, q
42 | {
43 | quotes: none;
44 | }
45 |
46 | blockquote:before, blockquote:after, q:before, q:after
47 | {
48 | content: '';
49 | content: none;
50 | }
51 |
52 | table
53 | {
54 | /**
55 | * Instead of "collapse" I'm using "separate" as that allows me to give table cells
56 | * a border without having to go through a lot of trouble
57 | */
58 | border-collapse: separate;
59 | border-spacing: 0;
60 | width: 100%;
61 | }
62 |
63 | table th
64 | {
65 | font-weight: bold;
66 | }
67 |
68 | pre, code
69 | {
70 | font-size: 13px;
71 | font-family: monospace;
72 | }
73 |
74 | /**
75 | * These form elements usually don't trigger any special cursor and thus can confuse
76 | * users when these elements have custom styles (e.g. a background image).
77 | */
78 | input[type="submit"], input[type="button"], input[type="checkbox"], input[type="radio"],
79 | button, select
80 | {
81 | cursor: pointer;
82 | }
83 |
84 | *[disabled], *[disabled="disabled"]
85 | {
86 | cursor: not-allowed;
87 | }
88 |
89 | textarea
90 | {
91 | overflow: auto;
92 | }
93 |
94 | acronym, abbr
95 | {
96 | cursor: help;
97 | }
98 |
99 | /* Some typography related styles */
100 | body
101 | {
102 | font-size: 16px;
103 | }
104 |
105 | h1, h2, h3, h4, h5, h6
106 | {
107 | font-weight: bold;
108 | }
109 |
110 | a:hover, a:active
111 | {
112 | outline: none;
113 | }
114 |
115 | strong
116 | {
117 | font-weight: bold;
118 | }
119 |
120 | small
121 | {
122 | font-size: 11px;
123 | }
124 |
--------------------------------------------------------------------------------