├── 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 | 9 | 10 | 11 | 13 | 14 | <% @posts.each do |post| %> 15 | 16 | 17 | 22 | 25 |
TitleTagsPublished? 12 |
<%= Admin::Posts.a(post.title, :show, post.id) %><%= post.tags %> 18 | <% if post.published %> 19 | published on <%= post.published_at %> 20 | <% end %> 21 | 23 | <% end %> 24 |
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 | 5 | 6 | 7 | 8 | 9 | 10 | <% @articles.each do |article| %> 11 | 12 | 13 | 14 | 19 | 20 | <% end %> 21 | 22 |
TitleURLTags
<%= Admin::Articles.a(article.title, :show, article.id) %><%= anchor(article.url) %> 15 | <% article.tags.each do |tag| %> 16 | <%= tag.name %> 17 | <% end %> 18 |
23 |
24 | 25 |
26 | <%= render_partial(:new) %> 27 |
28 | 29 |
30 | Install Bookmarklet 31 |
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 | 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 |
20 |

<%= @title %>

21 |
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 | --------------------------------------------------------------------------------