├── log
└── .gitkeep
├── lib
├── tasks
│ └── .gitkeep
├── assets
│ └── .gitkeep
└── templates
│ └── erb
│ └── scaffold
│ └── _form.html.erb
├── public
├── favicon.ico
├── robots.txt
├── 400.html
├── 404.html
├── 500.html
└── 422.html
├── app
├── mailers
│ └── .gitkeep
├── assets
│ ├── javascripts
│ │ ├── ace
│ │ │ ├── mode-text.js
│ │ │ ├── mode-plain_text.js
│ │ │ ├── mode-properties.js
│ │ │ ├── mode-lucene.js
│ │ │ ├── mode-sql.js
│ │ │ ├── mode-ada.js
│ │ │ ├── mode-scheme.js
│ │ │ ├── mode-lisp.js
│ │ │ ├── mode-textile.js
│ │ │ ├── mode-cobol.js
│ │ │ ├── mode-toml.js
│ │ │ ├── mode-diff.js
│ │ │ ├── theme-github.js
│ │ │ ├── ext-modelist.js
│ │ │ ├── mode-verilog.js
│ │ │ ├── mode-c9search.js
│ │ │ ├── mode-tex.js
│ │ │ ├── mode-ini.js
│ │ │ ├── mode-latex.js
│ │ │ ├── mode-sh.js
│ │ │ ├── mode-rdoc.js
│ │ │ ├── mode-batchfile.js
│ │ │ ├── mode-snippets.js
│ │ │ ├── mode-tmsnippet.js
│ │ │ ├── mode-yaml.js
│ │ │ ├── mode-pascal.js
│ │ │ ├── mode-python.js
│ │ │ ├── mode-tcl.js
│ │ │ ├── mode-makefile.js
│ │ │ ├── mode-mushcode_high_rules.js
│ │ │ ├── mode-r.js
│ │ │ └── mode-livescript.js
│ │ ├── application.js
│ │ └── header.js
│ └── stylesheets
│ │ ├── application.css
│ │ ├── code_prettify.css
│ │ └── bootstrap_and_overrides.css.less
├── views
│ ├── favorites
│ │ └── just_required_for_rspec
│ ├── gists
│ │ ├── page.js.erb
│ │ ├── mine_page.js.erb
│ │ ├── user_page.js.erb
│ │ ├── user_fav_page.js.erb
│ │ ├── add_gist_files_input.js.erb
│ │ ├── _page.html.erb
│ │ ├── _mine_page.html.erb
│ │ ├── new.html.erb
│ │ ├── edit.html.erb
│ │ ├── _user_page.html.erb
│ │ ├── mine.html.erb
│ │ ├── index.html.erb
│ │ ├── _gist_files_input.html.erb
│ │ ├── _fork_of.html.erb
│ │ ├── _forks.html.erb
│ │ ├── _user_fav_page.html.erb
│ │ ├── _history.html.erb
│ │ ├── _list.html.erb
│ │ ├── _form.html.erb
│ │ └── show.html.erb
│ ├── users
│ │ ├── edit.html.erb
│ │ ├── _form.html.erb
│ │ └── show.html.erb
│ ├── common
│ │ ├── _flash_error.html.erb
│ │ ├── _flash_notice.html.erb
│ │ ├── _search_form.html.erb
│ │ ├── _favorites.html.erb
│ │ └── _mygists.html.erb
│ ├── sessions
│ │ └── failure.html.erb
│ ├── kaminari
│ │ ├── _gap.html.erb
│ │ ├── _first_page.html.erb
│ │ ├── _last_page.html.erb
│ │ ├── _page.html.erb
│ │ └── _paginator.html.erb
│ ├── comments
│ │ ├── _form.html.erb
│ │ └── _list.html.erb
│ ├── layouts
│ │ └── application.html.erb
│ └── root
│ │ └── index.html.erb
├── models
│ ├── concerns
│ │ └── basic_persistence.rb
│ ├── gist_file.rb
│ ├── comment.rb
│ ├── favorite.rb
│ ├── user.rb
│ ├── gist_history.rb
│ ├── gist_fork_creation.rb
│ ├── gist_persistence.rb
│ └── gist.rb
├── controllers
│ ├── root_controller.rb
│ ├── favorites_controller.rb
│ ├── comments_controller.rb
│ ├── sessions_controller.rb
│ ├── users_controller.rb
│ └── application_controller.rb
└── helpers
│ └── application_helper.rb
├── screenshot1.png
├── screenshot2.png
├── bin
├── rake
├── bundle
└── rails
├── Rakefile
├── .buildpacks
├── config
├── environment.rb
├── initializers
│ ├── session_store.rb
│ ├── filter_parameter_logging.rb
│ ├── mime_types.rb
│ ├── kaminari_config.rb
│ ├── omniauth.rb
│ ├── secret_token.rb
│ ├── unlimited_strength_cryptography.rb
│ ├── backtrace_silencers.rb
│ ├── quiet_assets.rb
│ ├── wrap_parameters.rb
│ ├── inflections.rb
│ └── client_side_validations.rb
├── boot.rb
├── locales
│ ├── zh.yml
│ ├── en.yml
│ ├── simple_form.zh.yml
│ └── simple_form.en.yml
├── database.yml
├── routes.rb
├── environments
│ ├── development.rb
│ ├── test.rb
│ └── production.rb
└── application.rb
├── config.ru
├── db
├── migrate
│ ├── 20140110041301_add_email_to_users.rb
│ ├── 20121003054038_create_gist_histories.rb
│ ├── 20121008094826_create_favorites.rb
│ ├── 20121003022144_create_users.rb
│ ├── 20121003022400_create_comments.rb
│ ├── 20121015023421_change_gist_histories_gist_id_not_null.rb
│ ├── 20121003022313_create_gist_files.rb
│ └── 20121003022220_create_gists.rb
├── seeds.rb
└── schema.rb
├── spec
├── models
│ ├── gist_file_spec.rb
│ ├── comment_spec.rb
│ ├── favorite_spec.rb
│ ├── concerns
│ │ └── basic_persistence_spec.rb
│ ├── gist_fork_creation_spec.rb
│ ├── gist_history_spec.rb
│ ├── user_spec.rb
│ ├── gist_persistence_spec.rb
│ └── gist_spec.rb
├── routing
│ ├── root_routing_spec.rb
│ ├── users_routing_spec.rb
│ ├── sessions_routing_spec.rb
│ ├── comments_routing_spec.rb
│ ├── favorites_routing_spec.rb
│ └── gists_routing_spec.rb
├── requests
│ ├── gists_spec.rb
│ └── users_spec.rb
├── factories.rb
├── controllers
│ ├── root_controller_spec.rb
│ ├── application_controller_spec.rb
│ ├── favorites_controller_spec.rb
│ ├── comments_controller_spec.rb
│ └── sessions_controller_spec.rb
├── spec_helper.rb
└── helpers
│ └── application_helper_spec.rb
├── .travis.yml
├── .gitignore
├── LICENSE.txt
├── app.json
├── Gemfile
├── README.md
└── CHAGELOG
/log/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/tasks/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/mailers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-text.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/views/favorites/just_required_for_rspec:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-Agent: *
2 | Allow: /
3 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | //= require twitter/bootstrap
2 |
--------------------------------------------------------------------------------
/screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seratch/gistub/HEAD/screenshot1.png
--------------------------------------------------------------------------------
/screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seratch/gistub/HEAD/screenshot2.png
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | *= require_self
3 | *= require_tree .
4 | */
5 |
--------------------------------------------------------------------------------
/app/views/gists/page.js.erb:
--------------------------------------------------------------------------------
1 | $('#gists').html('<%= j(render(:partial => 'gists/page')) %>');
2 | prettyPrint();
3 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/app/views/gists/mine_page.js.erb:
--------------------------------------------------------------------------------
1 | $('#gists').html('<%= j(render(:partial => 'gists/mine_page')) %>');
2 | prettyPrint();
3 |
--------------------------------------------------------------------------------
/app/views/gists/user_page.js.erb:
--------------------------------------------------------------------------------
1 | $('#user_gists').html('<%= j(render(:partial => 'gists/user_page')) %>');
2 | prettyPrint();
3 |
--------------------------------------------------------------------------------
/public/400.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 400 Bad Request
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 404 Not Found
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 500 Internal Server Error
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/assets/javascripts/header.js:
--------------------------------------------------------------------------------
1 | //= require jquery
2 | //= require jquery_ujs
3 | //= require code_prettify
4 | //= require_tree .
5 |
6 |
--------------------------------------------------------------------------------
/app/views/gists/user_fav_page.js.erb:
--------------------------------------------------------------------------------
1 | $('#favorites').html('<%= j(render(:partial => 'gists/user_fav_page')) %>');
2 | prettyPrint();
3 |
4 |
--------------------------------------------------------------------------------
/app/views/users/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= render 'users/form' %>
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 422 Unprocessable Entity
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 |
3 | require File.expand_path('../config/application', __FILE__)
4 |
5 | Gistub::Application.load_tasks
6 |
7 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/.buildpacks:
--------------------------------------------------------------------------------
1 | https://github.com/rcaught/heroku-buildpack-cmake
2 | https://codon-buildpacks.s3.amazonaws.com/buildpacks/frederick/heroku-buildpack-ruby.tgz
3 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require File.expand_path('../application', __FILE__)
3 |
4 | Gistub::Application.initialize!
5 |
6 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | Gistub::Application.config.session_store :cookie_store, key: '_gistub_session'
3 |
4 |
--------------------------------------------------------------------------------
/app/views/common/_flash_error.html.erb:
--------------------------------------------------------------------------------
1 | <% if flash[:error] %>
2 |
3 | <%= flash[:error] %>
4 |
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/app/views/common/_flash_notice.html.erb:
--------------------------------------------------------------------------------
1 | <% if flash[:notice] %>
2 |
3 | <%= flash[:notice] %>
4 |
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../../config/application', __FILE__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | require ::File.expand_path('../config/environment', __FILE__)
2 |
3 | map ENV['RAILS_RELATIVE_URL_ROOT'] || "/" do
4 | run Gistub::Application
5 | end
6 |
7 |
--------------------------------------------------------------------------------
/app/views/gists/add_gist_files_input.js.erb:
--------------------------------------------------------------------------------
1 | $('#gist_files_input').append('<%= j(render(:partial => 'gist_files_input', :locals => { :gist_file => GistFile.new }) ) %>');
2 |
--------------------------------------------------------------------------------
/db/migrate/20140110041301_add_email_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddEmailToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :email, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/views/gists/_page.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= paginate @gists, :remote => true, :window => 6, :params => {:action => 'page'} %>
3 | <%= render :partial => 'gists/list' %>
4 |
5 |
--------------------------------------------------------------------------------
/app/views/gists/_mine_page.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= paginate @gists, :remote => true, :window => 6, :params => {:action => 'mine_page'} %>
3 | <%= render :partial => 'gists/list' %>
4 |
5 |
--------------------------------------------------------------------------------
/app/views/gists/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
New Gist
4 |
5 | <%= render 'gists/form' %>
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/views/sessions/failure.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Authentication Cancelled
4 |
5 | <%= link_to 'Back to home', root_path %>
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/models/concerns/basic_persistence.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | module BasicPersistence
3 |
4 | def transaction
5 | ActiveRecord::Base.transaction do
6 | yield
7 | end
8 | end
9 |
10 | end
11 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | # Set up gems listed in the Gemfile.
3 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
4 |
5 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
6 |
--------------------------------------------------------------------------------
/app/controllers/root_controller.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class RootController < ApplicationController
3 |
4 | respond_to :html
5 |
6 | def index
7 | @gists = Gist.limit(5).recent
8 | end
9 |
10 | end
11 |
--------------------------------------------------------------------------------
/app/views/common/_search_form.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= form_tag search_gists_path, :method => :get do %>
3 | <%= text_field_tag :search_query, @search_query, :placeholder => "Search", :class => "span3" %>
4 | <% end %>
5 |
6 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/app/models/gist_file.rb:
--------------------------------------------------------------------------------
1 | class GistFile < ActiveRecord::Base
2 |
3 | validates :name, :presence => true
4 | validates :body, :presence => true
5 | validates :gist_history_id, :presence => true
6 |
7 | belongs_to :gist_history
8 |
9 | end
10 |
--------------------------------------------------------------------------------
/app/views/gists/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Show', @gist %> |
6 | <%= link_to 'Back', gists_path %>
7 |
8 |
9 |
10 | <%= render :partial => 'gists/history' %>
11 |
12 |
--------------------------------------------------------------------------------
/spec/models/gist_file_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe GistFile do
5 |
6 | it 'is available' do
7 | gist_file = create(:gist_file)
8 | expect(gist_file.gist_history).not_to be_nil
9 | end
10 |
11 | end
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - jruby-19mode
4 | - 2.1.5
5 | # TODO: tests on Ruby 2.2.0
6 | # - 2.2.0
7 | bundler_args: --without postgresql
8 | env:
9 | - DB=sqlite
10 | script:
11 | - RAILS_ENV=test bundle exec rake --trace db:migrate spec
12 |
13 |
--------------------------------------------------------------------------------
/app/views/gists/_user_page.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= paginate @gists, :remote => true, :window => 6,
3 | :params => {:controller => 'gists', :action => 'user_page', :user_id => @user.id}
4 | %>
5 | <%= render :partial => 'gists/list' %>
6 |
7 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # Add new mime types for use in respond_to blocks:
5 | # Mime::Type.register "text/richtext", :rtf
6 | # Mime::Type.register_alias "text/html", :iphone
7 |
--------------------------------------------------------------------------------
/spec/routing/root_routing_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require "spec_helper"
3 |
4 | describe RootController do
5 | describe "routing" do
6 |
7 | it "routes to root" do
8 | expect(get("/")).to route_to("root#index")
9 | end
10 |
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/models/comment.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class Comment < ActiveRecord::Base
3 |
4 | validates :body, presence: true
5 | validates :gist_id, presence: true
6 | validates :user_id, presence: true
7 |
8 | belongs_to :gist
9 | belongs_to :user
10 |
11 | end
12 |
--------------------------------------------------------------------------------
/spec/models/comment_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe Comment do
5 |
6 | it 'is available' do
7 | comment = create(:comment)
8 | expect(comment.user).not_to be_nil
9 | expect(comment.gist).not_to be_nil
10 | end
11 |
12 | end
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .bundle
3 | .loadpath
4 | .project
5 | .rvmrc
6 | .idea
7 | Thumbs.db
8 | log/*.log
9 | db/*.sqlite3
10 | doc/*
11 | public/cache/**/*
12 | tmp/
13 | vendor/
14 | public/assets/
15 | coverage/
16 | backup/
17 | test/reports
18 | out/
19 | untitled/
20 | *.iml
21 |
22 |
--------------------------------------------------------------------------------
/spec/models/favorite_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe Favorite do
5 |
6 | it 'is available' do
7 | favorite = create(:favorite)
8 | expect(favorite.user).not_to be_nil
9 | expect(favorite.gist).not_to be_nil
10 | end
11 |
12 | end
13 |
--------------------------------------------------------------------------------
/app/models/favorite.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class Favorite < ActiveRecord::Base
3 |
4 | validates :gist_id, presence: true
5 | validates :user_id, presence: true
6 |
7 | belongs_to :gist
8 | belongs_to :user
9 |
10 | scope :recent, lambda { order(:created_at).reverse_order }
11 |
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20121003054038_create_gist_histories.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class CreateGistHistories < ActiveRecord::Migration
3 | def change
4 | create_table :gist_histories do |t|
5 | t.integer :gist_id
6 | t.integer :user_id
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/config/initializers/kaminari_config.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | Kaminari.configure do |config|
3 | # config.default_per_page = 25
4 | # config.window = 4
5 | # config.outer_window = 0
6 | # config.left = 0
7 | # config.right = 0
8 | # config.page_method_name = :page
9 | # config.param_name = :page
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20121008094826_create_favorites.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class CreateFavorites < ActiveRecord::Migration
3 | def change
4 | create_table :favorites do |t|
5 | t.integer :user_id, :null => false
6 | t.integer :gist_id, :null => false
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/models/concerns/basic_persistence_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 |
3 | require 'spec_helper'
4 |
5 | describe BasicPersistence do
6 |
7 | describe '#transaction' do
8 | it 'works' do
9 | Gist.transaction do
10 | Gist.where(:id => 123).first
11 | end
12 | end
13 | end
14 |
15 | end
16 |
--------------------------------------------------------------------------------
/spec/requests/gists_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe "Gists" do
5 |
6 | describe "GET /gists" do
7 | it "works" do
8 | 3.times do
9 | create(:gist)
10 | end
11 | get gists_path
12 | expect(response.status).to be(200)
13 | end
14 | end
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/config/locales/zh.yml:
--------------------------------------------------------------------------------
1 | zh:
2 | activerecord:
3 | models:
4 | gist: 纪要
5 | gist_file: 文件
6 | gist_history: 历史
7 | user: 用户
8 | favorite: 收藏
9 | comment: 评论
10 | attributes:
11 | user:
12 | nickname: 昵称
13 | omniauth_provider: 认证
14 | omniauth_uid: 用户ID
15 | hello: "Hello world"
16 |
--------------------------------------------------------------------------------
/db/migrate/20121003022144_create_users.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class CreateUsers < ActiveRecord::Migration
3 | def change
4 | create_table :users do |t|
5 | t.string :nickname
6 | t.string :omniauth_provider, :null => false
7 | t.string :omniauth_uid, :null => false
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20121003022400_create_comments.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class CreateComments < ActiveRecord::Migration
3 | def change
4 | create_table :comments do |t|
5 | t.integer :gist_id, :null => false
6 | t.integer :user_id, :null => false
7 | t.text :body, :null => false
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20121015023421_change_gist_histories_gist_id_not_null.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class ChangeGistHistoriesGistIdNotNull < ActiveRecord::Migration
3 | def up
4 | change_column :gist_histories, :gist_id, :integer, :null => false
5 | end
6 |
7 | def down
8 | change_column :gist_histories, :gist_id, :integer, :null => true
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20121003022313_create_gist_files.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class CreateGistFiles < ActiveRecord::Migration
3 | def change
4 | create_table :gist_files do |t|
5 | t.string :name, :null => false
6 | t.text :body, :null => false
7 | t.integer :gist_history_id, :null => false
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/config/initializers/omniauth.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | Rails.application.config.middleware.use OmniAuth::Builder do
3 | require 'openid/store/filesystem'
4 | provider :open_id,
5 | :identifier => ENV['GISTUB_OPENID_IDENTIFIER'].presence || 'https://www.google.com/accounts/o8/id',
6 | :store => OpenID::Store::Filesystem.new("#{Rails.root}/tmp/openid")
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20121003022220_create_gists.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class CreateGists < ActiveRecord::Migration
3 | def change
4 | create_table :gists do |t|
5 | t.string :title, :null => false
6 | t.boolean :is_public, :null => false
7 | t.integer :user_id
8 | t.integer :source_gist_id
9 |
10 | t.timestamps
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | activerecord:
3 | models:
4 | gist: Gist
5 | gist_file: File
6 | gist_history: History
7 | user: User
8 | favorite: Favorite
9 | comment: Comment
10 | attributes:
11 | user:
12 | nickname: Nickname
13 | omniauth_provider: Authentication
14 | omniauth_uid: UID
15 | hello: "Hello world"
16 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | # This file should contain all the record creation needed to seed the database with its default values.
3 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
4 | #
5 | # Examples:
6 | #
7 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
8 | # Mayor.create(name: 'Emanuel', city: cities.first)
9 |
--------------------------------------------------------------------------------
/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | Gistub::Application.config.secret_token = ENV['GISTUB_SECRET_TOKEN'] || '9d823ec9de09d00bec258fee515a324f9499cc3d334bdd8955a4e779094cf1bf37f6c2d77ff65ef6222ab038a8b49f6e1fd81949b05ffdbaa64e5df2fc5f507b'
3 | Gistub::Application.config.secret_key_base = ENV['GISTUB_SECRET_KEY_BASE'] || 'something like 4f9499cc3d334bdd8955a4e779094cf1bf37f6c2'
4 |
5 |
--------------------------------------------------------------------------------
/spec/requests/users_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe "Users" do
5 |
6 | describe "GET /user/:id" do
7 | it "works" do
8 | user = create(:user, :nickname => 'XXXXX')
9 | get user_path(user)
10 | expect(response.status).to be(200)
11 | expect(response.body.include?("XXXXX")).to be_true
12 | end
13 | end
14 |
15 | end
16 |
--------------------------------------------------------------------------------
/config/initializers/unlimited_strength_cryptography.rb:
--------------------------------------------------------------------------------
1 | # http://stackoverflow.com/questions/14552303/opensslcipherciphererror-with-rails4-on-jruby
2 |
3 | if RUBY_PLATFORM == 'java'
4 | security_class = java.lang.Class.for_name('javax.crypto.JceSecurity')
5 | restricted_field = security_class.get_declared_field('isRestricted')
6 | restricted_field.accessible = true
7 | restricted_field.set nil, false
8 | end
9 |
10 |
--------------------------------------------------------------------------------
/app/views/kaminari/_gap.html.erb:
--------------------------------------------------------------------------------
1 | <%# Non-link tag that stands for skipped pages...
2 | - available local variables
3 | current_page: a page object for the currently displayed page
4 | num_pages: total number of pages
5 | per_page: number of items to fetch per page
6 | remote: data-remote
7 | -%>
8 | <%= raw(t 'views.pagination.truncate') %>
9 |
--------------------------------------------------------------------------------
/lib/templates/erb/scaffold/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%%= simple_form_for(@<%= singular_table_name %>) do |f| %>
2 | <%%= f.error_notification %>
3 |
4 |
5 | <%- attributes.each do |attribute| -%>
6 | <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %>
7 | <%- end -%>
8 |
9 |
10 |
11 | <%%= f.button :submit %>
12 |
13 | <%% end %>
14 |
--------------------------------------------------------------------------------
/app/views/kaminari/_first_page.html.erb:
--------------------------------------------------------------------------------
1 | <%# Link to the "First" page
2 | - available local variables
3 | url: url to the first page
4 | current_page: a page object for the currently displayed page
5 | num_pages: total number of pages
6 | per_page: number of items to fetch per page
7 | remote: data-remote
8 | -%>
9 |
10 | <%= link_to raw(t 'views.pagination.first'), url, :remote => remote %>
11 |
12 |
--------------------------------------------------------------------------------
/spec/models/gist_fork_creation_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 |
3 | require 'spec_helper'
4 |
5 | describe GistForkCreation do
6 |
7 | describe '#save!' do
8 | it 'works' do
9 | gist_fork_creation = GistForkCreation.new
10 | gist_to_fork = create(:gist_history).gist
11 | current_user = nil
12 | result = gist_fork_creation.save!(gist_to_fork, current_user)
13 | expect(result).not_to be_nil
14 | end
15 | end
16 |
17 | end
18 |
--------------------------------------------------------------------------------
/app/views/gists/mine.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= render :partial => 'common/flash_notice' %>
4 |
5 |
My Gists
6 | <%= render :partial => 'common/search_form' %>
7 |
8 |
9 |
10 | <%= render :partial => 'gists/mine_page' %>
11 |
12 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
5 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
6 |
7 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
8 | # Rails.backtrace_cleaner.remove_silencers!
9 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-plain_text.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/plain_text",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./text_highlight_rules").TextHighlightRules,u=e("./behaviour").Behaviour,a=function(){this.$tokenizer=new s((new o).getRules()),this.$behaviour=new u};r.inherits(a,i),function(){this.getNextLineIndent=function(e,t,n){return""}}.call(a.prototype),t.Mode=a})
--------------------------------------------------------------------------------
/app/views/kaminari/_last_page.html.erb:
--------------------------------------------------------------------------------
1 | <%# Link to the "Last" page
2 | - available local variables
3 | url: url to the last page
4 | current_page: a page object for the currently displayed page
5 | num_pages: total number of pages
6 | per_page: number of items to fetch per page
7 | remote: data-remote
8 | -%>
9 | <%# "next" class present for border styling in twitter bootstrap %>
10 | <%= link_to raw(t 'views.pagination.last'), url, {:remote => remote} %>
11 |
12 |
--------------------------------------------------------------------------------
/config/initializers/quiet_assets.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | if Rails.env.development?
3 | Rails.application.assets.logger = Logger.new('/dev/null')
4 | Rails::Rack::Logger.class_eval do
5 | def call_with_quiet_assets(env)
6 | previous_level = Rails.logger.level
7 | Rails.logger.level = Logger::ERROR if env['PATH_INFO'] =~ %r{^/assets/}
8 | call_without_quiet_assets(env)
9 | ensure
10 | Rails.logger.level = previous_level
11 | end
12 | alias_method_chain :call, :quiet_assets
13 | end
14 | end
15 |
16 |
--------------------------------------------------------------------------------
/app/views/gists/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= render :partial => 'common/flash_notice' %>
4 |
5 | <% @gist_list_title ||= "Gists" %>
6 |
<%= @gist_list_title %>
7 | <%= render :partial => 'common/search_form' %>
8 |
9 |
10 |
11 |
12 | <%= render :partial => 'gists/page' %>
13 |
14 |
15 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/views/kaminari/_page.html.erb:
--------------------------------------------------------------------------------
1 | <%# Link showing page number
2 | - available local variables
3 | page: a page object for "this" page
4 | url: url to this page
5 | current_page: a page object for the currently displayed page
6 | num_pages: total number of pages
7 | per_page: number of items to fetch per page
8 | remote: data-remote
9 | -%>
10 |
11 | <%= link_to page, url, opts = {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil} %>
12 |
13 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | # Be sure to restart your server when you modify this file.
3 | #
4 | # This file contains settings for ActionController::ParamsWrapper which
5 | # is enabled by default.
6 |
7 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
8 | ActiveSupport.on_load(:action_controller) do
9 | wrap_parameters format: [:json]
10 | end
11 |
12 | # Disable root element in JSON by default.
13 | ActiveSupport.on_load(:active_record) do
14 | self.include_root_in_json = false
15 | end
16 |
--------------------------------------------------------------------------------
/app/views/common/_favorites.html.erb:
--------------------------------------------------------------------------------
1 | <% if current_user.present? %>
2 |
3 | Favorite Gists
4 |
5 | <% my_favorite_gists.each do |gist| %>
6 |
7 |
8 | <%= link_to gist.title, gist %>
9 |
10 |
11 | created <%= time_ago_in_words(gist.created_at) + ' ago' %>
12 |
13 |
14 | <% end %>
15 |
16 | <%= link_to "More...", user_path(current_user) %>
17 |
18 |
19 |
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class User < ActiveRecord::Base
3 |
4 | validates :omniauth_provider, presence: true
5 | validates :omniauth_uid, presence: true
6 |
7 | has_many :gists, -> { order(:updated_at => :desc) }
8 | has_many :comments, -> { order(:updated_at => :desc) }
9 |
10 | def self.create_with_omniauth(auth)
11 | create! do |user|
12 | user.omniauth_provider = auth['provider']
13 | user.omniauth_uid = auth['uid']
14 | user.email = auth['info']['email']
15 | user.nickname = nil
16 | end
17 | end
18 |
19 |
20 | end
21 |
--------------------------------------------------------------------------------
/app/views/comments/_form.html.erb:
--------------------------------------------------------------------------------
1 | <% if current_user.present? %>
2 | <%
3 | @comment ||= Comment.new
4 | %>
5 | <%= simple_form_for [@gist, @comment] do |f| %>
6 |
7 | <%= f.error_notification %>
8 |
9 |
10 | <%= text_area_tag :body, nil, :rows => 8, :class => 'span8' %>
11 |
12 |
13 |
14 | <%= f.submit 'Submit', :class => 'btn btn-primary' %>
15 |
16 |
17 | <% end %>
18 | <% else %>
19 | Please <%= link_to 'sign in', signin_path %> to comment.
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/app/models/gist_history.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class GistHistory < ActiveRecord::Base
3 |
4 | validates :gist_id, presence: true
5 |
6 | belongs_to :gist
7 | belongs_to :user
8 |
9 | has_many :gist_files
10 |
11 | # Since ActiveRecord 4.0.1, following code doesn't work as expected
12 | #default_scope { order(:id).reverse_order }
13 | default_scope { order(:id => :desc) }
14 |
15 | def gist
16 | Gist.include_private.where(id: gist_id).first
17 | end
18 |
19 | def headline
20 | body = gist_files.first.try(:body)
21 | body.nil? ? '' : body.split("\n").take(3).join("\n")
22 | end
23 |
24 | end
25 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # Add new inflection rules using the following format
5 | # (all these examples are active by default):
6 | # ActiveSupport::Inflector.inflections do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 | #
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/app/views/gists/_gist_files_input.html.erb:
--------------------------------------------------------------------------------
1 | <% id = gist_file.try(:id) || "new#{Time.now.to_f.to_s.gsub(/\./, '')}" %>
2 |
3 |
4 | <%= text_field_tag 'gist_file_names[]', gist_file.try(:name), :placeholder => 'name this file...', :class => 'span8 gist_file_name', :id => "gist_file_names_#{id}"%>
5 |
6 |
7 | <%= text_area_tag 'gist_file_bodies[]', gist_file.try(:body), :rows => 15, :class => 'span8 gist_file_body', :id => "gist_file_bodies_#{id}" %>
8 |
9 |
Remove this…
10 |
11 |
12 |
--------------------------------------------------------------------------------
/spec/routing/users_routing_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require "spec_helper"
3 |
4 | describe UsersController do
5 | describe "routing" do
6 |
7 | it "routes to #show" do
8 | expect(get("/users/1")).to route_to("users#show", :id => "1")
9 | end
10 |
11 | it "routes to #edit" do
12 | expect(get("/users/1/edit")).to route_to("users#edit", :id => "1")
13 | end
14 |
15 | it "routes to #update" do
16 | expect(put("/users/1")).to route_to("users#update", :id => "1")
17 | end
18 |
19 | it "routes to #destroy" do
20 | expect(delete("/users/1")).to route_to("users#destroy", :id => "1")
21 | end
22 |
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | development:
7 | adapter: sqlite3
8 | database: db/development.sqlite3
9 | pool: 5
10 | timeout: 5000
11 |
12 | # Warning: The database defined as "test" will be erased and
13 | # re-generated from your development database when you run "rake".
14 | # Do not set this db to the same as development or production.
15 | test:
16 | adapter: sqlite3
17 | database: db/test.sqlite3
18 | pool: 5
19 | timeout: 5000
20 |
21 | production:
22 | adapter: sqlite3
23 | database: db/production.sqlite3
24 | pool: 5
25 | timeout: 5000
26 |
--------------------------------------------------------------------------------
/app/views/common/_mygists.html.erb:
--------------------------------------------------------------------------------
1 | <% if current_user.present? %>
2 |
3 | My Gists
4 |
5 | <% my_gists.each do |gist| %>
6 |
7 | <% if gist.is_public %>
8 |
9 | <% else %>
10 |
11 | <% end %>
12 | <%= link_to gist.title, gist %>
13 |
14 |
15 | created <%= time_ago_in_words(gist.created_at) + ' ago' %>
16 |
17 |
18 | <% end %>
19 |
20 | <%= link_to "More...", user_path(current_user) %>
21 |
22 |
23 |
24 | <% end %>
25 |
--------------------------------------------------------------------------------
/app/views/users/_form.html.erb:
--------------------------------------------------------------------------------
1 | Editing User
2 |
3 | <%= simple_form_for @user, :html => {:class => 'form-horizontal'} do |f| %>
4 |
5 | <%= render :partial => 'common/flash_notice' %>
6 | <%= f.error_notification %>
7 |
8 |
9 | <%= f.input :nickname, :input_html => {:class => 'span6'} %>
10 | <%= f.input :omniauth_provider, :input_html => {:class => 'span6'}, :disabled => true %>
11 | <%= f.input :omniauth_uid, :input_html => {:class => 'span6'}, :disabled => true %>
12 |
13 |
14 |
15 | <%= f.submit 'Submit', :class => 'btn btn-info' %>
16 | <%= link_to 'Cancel', root_path, :class => 'btn' %>
17 |
18 |
19 | <% end %>
20 |
--------------------------------------------------------------------------------
/app/views/gists/_fork_of.html.erb:
--------------------------------------------------------------------------------
1 | <% if @gist.source_gist.present? %>
2 |
3 | Fork of
4 |
5 |
6 |
7 |
8 | <%= link_to @gist.source_gist.title, @gist.source_gist %>
9 |
10 | <%= time_ago_in_words(@gist.source_gist.created_at) + ' ago' %>
11 | by
12 | <% if @gist.source_gist.user.nil? %>Anonymous
13 | <% else %>
14 | <%= link_to @gist.source_gist.user.nickname, user_path(@gist.source_gist.user) %>
15 | <%= gravatar_image(@gist.source_gist.user, :size => 15) %>
16 | <% end %>
17 |
18 |
19 |
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/app/models/gist_fork_creation.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class GistForkCreation
3 | include BasicPersistence
4 |
5 | def save!(gist_to_fork, current_user)
6 | transaction do
7 | created_gist = Gist.create!(
8 | title: gist_to_fork.title,
9 | source_gist_id: gist_to_fork.id,
10 | user_id: current_user.try(:id)
11 | )
12 | created_history = GistHistory.create!(gist_id: created_gist.id)
13 | gist_to_fork.latest_history.gist_files.each do |file|
14 | GistFile.create(
15 | gist_history_id: created_history.id,
16 | name: file.name,
17 | body: file.body
18 | )
19 | end
20 | created_gist
21 | end
22 | end
23 |
24 | end
25 |
--------------------------------------------------------------------------------
/app/controllers/favorites_controller.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class FavoritesController < ApplicationController
3 |
4 | before_action :login_required
5 |
6 | respond_to :html
7 |
8 | def create
9 | @gist = Gist.find_commentable_gist(params[:gist_id], current_user.try(:id))
10 | fav = Favorite.new
11 | fav.gist_id = @gist.id
12 | fav.user_id = current_user.id
13 | if fav.save
14 | redirect_to gist_path(@gist.id), notice: 'You liked this gist.'
15 | else
16 | render action: '../gists/show'
17 | end
18 | end
19 |
20 | def destroy
21 | own_fav = Favorite.where(id: params[:id], user_id: current_user.try(:id)).first
22 | destroy_and_redirect_to_gist(own_fav, 'Your love is cancelled.', 'Not found.')
23 | end
24 |
25 | end
26 |
--------------------------------------------------------------------------------
/app/views/gists/_forks.html.erb:
--------------------------------------------------------------------------------
1 | <% if @gist.forks.present? %>
2 |
3 | Forks
4 |
5 | <% @gist.forks.each do |gist| %>
6 |
7 |
8 |
9 | <%= link_to gist.title, gist %>
10 |
11 | <%= time_ago_in_words(gist.created_at) + ' ago' %>
12 | by
13 | <% if gist.user.nil? %>Anonymous
14 | <% else %>
15 | <%= link_to gist.user.nickname, user_path(gist.user) %>
16 | <%= gravatar_image(gist.user, :size => 15) %>
17 | <% end %>
18 |
19 |
20 | <% end %>
21 |
22 | <% end %>
23 |
--------------------------------------------------------------------------------
/spec/routing/sessions_routing_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require "spec_helper"
3 |
4 | describe SessionsController do
5 | describe "routing" do
6 |
7 | it "routes to #start" do
8 | expect(get("/signin")).to route_to("sessions#start")
9 | end
10 |
11 | it "routes to #callback" do
12 | expect(get("/auth/open_id/callback")).to route_to("sessions#create", :provider => 'open_id')
13 | expect(post("/auth/open_id/callback")).to route_to("sessions#create", :provider => 'open_id')
14 | end
15 |
16 | it "routes to #destroy" do
17 | expect(get("/signout")).to route_to("sessions#destroy")
18 | end
19 |
20 | it "routes to #failure" do
21 | expect(get("/auth/failure")).to route_to("sessions#failure")
22 | end
23 |
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/config/locales/simple_form.zh.yml:
--------------------------------------------------------------------------------
1 | zh:
2 | simple_form:
3 | "yes": '确定'
4 | "no": '取消'
5 | required:
6 | text: '必填'
7 | mark: '*'
8 | # You can uncomment the line below if you need to overwrite the whole required html.
9 | # When using html, text and mark won't be used.
10 | # html: '* '
11 | error_notification:
12 | default_message: "请查看以下错误信息:"
13 | # Labels and hints examples
14 | # labels:
15 | # defaults:
16 | # password: 'Password'
17 | # user:
18 | # new:
19 | # email: 'E-mail to sign in.'
20 | # edit:
21 | # email: 'E-mail.'
22 | # hints:
23 | # defaults:
24 | # username: 'User name to sign in.'
25 | # password: 'No special characters, please.'
26 |
--------------------------------------------------------------------------------
/config/locales/simple_form.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | simple_form:
3 | "yes": 'Yes'
4 | "no": 'No'
5 | required:
6 | text: 'required'
7 | mark: '*'
8 | # You can uncomment the line below if you need to overwrite the whole required html.
9 | # When using html, text and mark won't be used.
10 | # html: '* '
11 | error_notification:
12 | default_message: "Please review the problems below:"
13 | # Labels and hints examples
14 | # labels:
15 | # defaults:
16 | # password: 'Password'
17 | # user:
18 | # new:
19 | # email: 'E-mail to sign in.'
20 | # edit:
21 | # email: 'E-mail.'
22 | # hints:
23 | # defaults:
24 | # username: 'User name to sign in.'
25 | # password: 'No special characters, please.'
26 |
27 |
--------------------------------------------------------------------------------
/app/views/kaminari/_paginator.html.erb:
--------------------------------------------------------------------------------
1 | <%# The container tag
2 | - available local variables
3 | current_page: a page object for the currently displayed page
4 | num_pages: total number of pages
5 | per_page: number of items to fetch per page
6 | remote: data-remote
7 | paginator: the paginator that renders the pagination tags inside
8 | -%>
9 | <%= paginator.render do -%>
10 |
23 | <% end -%>
24 |
--------------------------------------------------------------------------------
/config/initializers/client_side_validations.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | # ClientSideValidations Initializer
3 |
4 | # Uncomment to disable uniqueness validator, possible security issue
5 | # ClientSideValidations::Config.disabled_validators = [:uniqueness]
6 |
7 | # Uncomment to validate number format with current I18n locale
8 | # ClientSideValidations::Config.number_format_with_locale = true
9 |
10 | # Uncomment the following block if you want each input field to have the validation messages attached.
11 | ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
12 | unless html_tag =~ /^#{html_tag}#{instance.error_message.first} }.html_safe
14 | else
15 | %{#{html_tag}
}.html_safe
16 | end
17 | end
18 |
19 |
--------------------------------------------------------------------------------
/spec/routing/comments_routing_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require "spec_helper"
3 |
4 | describe CommentsController do
5 | describe "routing" do
6 |
7 | it "routes to #create" do
8 | expect(get("/gists/123/comments")).not_to be_routable
9 | expect(post("/gists/123/comments")).to route_to("comments#create", :gist_id => "123")
10 | expect(put("/gists/123/comments")).not_to be_routable
11 | expect(delete("/gists/123/comments")).not_to be_routable
12 | end
13 |
14 | it "routes to #destroy" do
15 | expect(get("/gists/123/comments/1")).not_to be_routable
16 | expect(post("/gists/123/comments/1")).not_to be_routable
17 | expect(put("/gists/123/comments/1")).not_to be_routable
18 | expect(delete("/gists/123/comments/1")).to route_to("comments#destroy", :gist_id => "123", :id => "1")
19 | end
20 |
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/factories.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | FactoryGirl.define do
3 |
4 | factory :gist do
5 | title "Gist by FactoryGirl (#{Time.now})"
6 | is_public true
7 | user
8 | created_at Time.now
9 | updated_at Time.now
10 | end
11 |
12 | factory :gist_history do
13 | gist
14 | user
15 | end
16 |
17 | factory :gist_file do
18 | gist_history
19 | name 'sample.rb'
20 | body < body).gist_history
29 | expect(gist_history.headline).to eq(body.sub(/\n$/, ""))
30 | end
31 |
32 | end
33 |
--------------------------------------------------------------------------------
/spec/routing/favorites_routing_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require "spec_helper"
3 |
4 | describe FavoritesController do
5 | describe "routing" do
6 |
7 | it "routes to #create" do
8 | expect(get("/gists/123/favorites")).not_to be_routable
9 | expect(post("/gists/123/favorites")).to route_to("favorites#create", :gist_id => "123")
10 | expect(put("/gists/123/favorites")).not_to be_routable
11 | expect(delete("/gists/123/favorites")).not_to be_routable
12 | end
13 |
14 | it "routes to #destroy" do
15 | expect(get("/gists/123/favorites/1")).not_to be_routable
16 | expect(post("/gists/123/favorites/1")).not_to be_routable
17 | expect(put("/gists/123/favorites/1")).not_to be_routable
18 | expect(delete("/gists/123/favorites/1")).to route_to("favorites#destroy", :gist_id => "123", :id => "1")
19 | end
20 |
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/controllers/comments_controller.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class CommentsController < ApplicationController
3 |
4 | before_action :login_required
5 |
6 | respond_to :html
7 |
8 | def create
9 | @gist = Gist.find_commentable_gist(params[:gist_id], current_user.try(:id))
10 | @gist_history = @gist.latest_history
11 | comment = Comment.new
12 | comment.gist_id = @gist.id
13 | comment.user_id = current_user.try(:id)
14 | comment.body = params[:body]
15 | if comment.save
16 | redirect_to gist_path(@gist.id), notice: 'Comment is successfully added.'
17 | else
18 | render action: '../gists/show'
19 | end
20 | end
21 |
22 | def destroy
23 | comment = Comment.where(id: params[:id], user_id: current_user.try(:id)).first
24 | destroy_and_redirect_to_gist(comment, 'Comment is successfully removed.', 'Not found.')
25 | end
26 |
27 | end
28 |
--------------------------------------------------------------------------------
/app/views/gists/_user_fav_page.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= paginate @favorites, :remote => true, :window => 6,
3 | :params => {:controller => 'gists', :action => 'user_fav_page', :user_id => @user.id}
4 | %>
5 | <% @favorites.each do |fav| %>
6 | <% gist = fav.gist %>
7 | <% if gist.present? %>
8 |
9 | <%= link_to gist.title, gist %>
10 |
11 |
12 | <%= time_ago_in_words(gist.latest_history.created_at) + ' ago' %>
13 | by
14 | <% if gist.user.nil? %>Anonymous
15 | <% else %><%= link_to gist.user.nickname, user_path(gist.user) %>
16 | <% end %>
17 |
18 |
19 |
20 |
<%= gist.latest_history.headline %>
21 | <% end %>
22 | <% end %>
23 |
24 |
--------------------------------------------------------------------------------
/app/views/comments/_list.html.erb:
--------------------------------------------------------------------------------
1 | <% @gist.comments.each do |comment| %>
2 |
3 |
4 |
5 | <% if comment.user.present? %>
6 | <%= link_to comment.user.nickname, comment.user %>
7 | commented <%= time_ago_in_words(comment.created_at) + ' ago' %>
8 | <% if comment.user_id == current_user.try(:id) %>
9 | <%= link_to 'Delete', gist_comment_path(@gist, comment), :method => :delete, :data => {:confirm => 'Are you sure?'} %>
10 | <% end %>
11 | <% else %>
12 | A deleted user commented <%= time_ago_in_words(comment.created_at) + ' ago' %>
13 | <% end %>
14 |
15 |
16 | <% if Gistub::Application.config.gistub_auto_link %>
17 | <%= auto_link raw markdown(sanitize comment.body) %>
18 | <% else %>
19 | <%= raw markdown(sanitize comment.body) %>
20 | <% end %>
21 |
22 | <% end %>
23 |
--------------------------------------------------------------------------------
/spec/controllers/root_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe RootController do
5 |
6 | # for response.body
7 | render_views
8 |
9 | let(:user) { create(:user) }
10 |
11 | def valid_session
12 | {:user_id => user.id}
13 | end
14 |
15 | describe "GET index" do
16 | it "contains content for logged in user" do
17 | get :index, {}, valid_session
18 | expect(response).to render_template("root/index")
19 | expect(response.status).to eq(200)
20 | expect(response.body).to match(%r{My Gists })
21 | expect(response.body).to match(%r{Favorite Gists })
22 | end
23 |
24 | it "contains content" do
25 | get :index, {}, {}
26 | expect(response).to render_template("root/index")
27 | expect(response.status).to eq(200)
28 | expect(response.body).not_to match(%r{My Gists })
29 | expect(response.body).not_to match(%r{Favorite Gists })
30 | end
31 | end
32 |
33 | end
34 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-properties.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/properties",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/properties_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./properties_highlight_rules").PropertiesHighlightRules,u=function(){var e=new o;this.$tokenizer=new s(e.getRules())};r.inherits(u,i),t.Mode=u}),ace.define("ace/mode/properties_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e=/\\u[0-9a-fA-F]{4}|\\/;this.$rules={start:[{token:"comment",regex:/[!#].*$/},{token:"keyword",regex:/[=:]$/},{token:"keyword",regex:/[=:]/,next:"value"},{token:"constant.language.escape",regex:e},{defaultToken:"variable"}],value:[{regex:/\\$/,token:"string",next:"value"},{regex:/$/,token:"string",next:"start"},{token:"constant.language.escape",regex:e},{defaultToken:"string"}]}};r.inherits(s,i),t.PropertiesHighlightRules=s})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-lucene.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/lucene",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/lucene_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./lucene_highlight_rules").LuceneHighlightRules,u=function(){this.$tokenizer=new s((new o).getRules())};r.inherits(u,i),t.Mode=u}),ace.define("ace/mode/lucene_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=function(){this.$rules={start:[{token:"constant.character.negation",regex:"[\\-]"},{token:"constant.character.interro",regex:"[\\?]"},{token:"constant.character.asterisk",regex:"[\\*]"},{token:"constant.character.proximity",regex:"~[0-9]+\\b"},{token:"keyword.operator",regex:"(?:AND|OR|NOT)\\b"},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"keyword",regex:"[\\S]+:"},{token:"string",regex:'".*?"'},{token:"text",regex:"\\s+"}]}};r.inherits(o,s),t.LuceneHighlightRules=o})
--------------------------------------------------------------------------------
/app/views/gists/_history.html.erb:
--------------------------------------------------------------------------------
1 | History
2 |
3 | <% @gist.gist_histories.each do |history| %>
4 |
5 |
6 |
7 | <% if history.id == @gist_history.id %>
8 | <%= time_ago_in_words(history.created_at) + ' ago' %>
9 | by
10 | <% if history.user.nil? %>Anonymous
11 | <% else %>
12 | <%= link_to history.user.nickname, user_path(history.user) %>
13 | <%= gravatar_image(history.user, :size => 15) %>
14 | <% end %>
15 | <% else %>
16 | <%= link_to time_ago_in_words(history.created_at) + ' ago', show_history_gist_path(@gist, history) %>
17 | by
18 | <% if history.user.nil? %>Anonymous
19 | <% else %>
20 | <%= link_to history.user.nickname, user_path(history.user) %>
21 | <%= gravatar_image(history.user, :size => 15) %>
22 | <% end %>
23 | <% end %>
24 |
25 |
26 | <% end %>
27 |
28 |
--------------------------------------------------------------------------------
/spec/models/user_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe User do
5 |
6 | it 'is available' do
7 | user = create(:user)
8 | expect(user).not_to be_nil
9 | end
10 |
11 | it 'create with omniauth without email' do
12 | auth = {
13 | 'provider' => 'open_id',
14 | 'uid' => 'xxxxxx',
15 | 'info' => {}
16 | }
17 | created = User.create_with_omniauth(auth)
18 | expect(created.omniauth_provider).to eq('open_id')
19 | expect(created.omniauth_uid).to eq('xxxxxx')
20 | expect(created.email).to be_nil
21 | expect(created.nickname).to be_nil
22 | end
23 |
24 | it 'create with omniauth with email' do
25 | auth = {
26 | 'provider' => 'open_id',
27 | 'uid' => 'xxxxxx',
28 | 'info' => {
29 | 'email' => 'test@test.org'
30 | }
31 | }
32 | created = User.create_with_omniauth(auth)
33 | expect(created.omniauth_provider).to eq('open_id')
34 | expect(created.omniauth_uid).to eq('xxxxxx')
35 | expect(created.email).to eq('test@test.org')
36 | expect(created.nickname).to be_nil
37 | end
38 |
39 | end
40 |
--------------------------------------------------------------------------------
/app/controllers/sessions_controller.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class SessionsController < ApplicationController
3 |
4 | protect_from_forgery :except => :create
5 |
6 | skip_before_action :login_required
7 | skip_before_action :nickname_required, only: [:destroy]
8 |
9 | def start
10 | return_to = params[:return_to] || root_path
11 | redirect_to url_for("#{root_path}auth/open_id?return_to=#{return_to}")
12 | end
13 |
14 | def failure
15 | respond_to { |format| format.html }
16 | end
17 |
18 | def create
19 | auth = request.env['omniauth.auth']
20 | if auth.present?
21 | user = User.where(
22 | omniauth_provider: auth['provider'],
23 | omniauth_uid: auth['uid']
24 | ).first || User.create_with_omniauth(auth)
25 |
26 | user.update!(email: auth['info']['email'])
27 |
28 | session[:user_id] = user.id
29 | return redirect_to params[:return_to] if params[:return_to].present?
30 | end
31 | redirect_to root_path
32 | end
33 |
34 | def destroy
35 | session[:user_id] = nil
36 | reset_session
37 | redirect_to root_path
38 | end
39 |
40 | end
41 |
--------------------------------------------------------------------------------
/spec/models/gist_persistence_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 |
3 | require 'spec_helper'
4 |
5 | describe GistPersistence do
6 |
7 | describe '#save_history!' do
8 | it 'works' do
9 | flash = double('flash')
10 | gist = create(:gist)
11 | user = create(:user)
12 | history = GistPersistence.new(flash).save_history!(gist.id, user.id)
13 | expect(history).not_to be_nil
14 | end
15 | end
16 |
17 | describe '#save_files!' do
18 | it 'works' do
19 | flash = double('flash')
20 | history = create(:gist_history)
21 | gist_files = [['name', 'body']]
22 | result = GistPersistence.new(flash).save_files!(history, gist_files)
23 | expect(result).not_to be_nil
24 | end
25 | end
26 |
27 | describe '#save!' do
28 | it 'works' do
29 | flash = double('flash')
30 | gist_creation = GistPersistence.new(flash)
31 | gist = build(:gist)
32 | gist_files = [['name', 'body']]
33 | current_user = nil
34 | result = gist_creation.save!(gist, gist_files, current_user)
35 | expect(result).not_to be_nil
36 | end
37 | end
38 |
39 |
40 | end
41 |
--------------------------------------------------------------------------------
/spec/controllers/application_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe RootController do
5 |
6 | let(:user) { create(:user) }
7 | let(:user_without_nickname) { create(:user, :nickname => nil) }
8 |
9 | def valid_session
10 | {:user_id => user.id}
11 | end
12 |
13 | def no_nickname_session
14 | {:user_id => user_without_nickname.id}
15 | end
16 |
17 | describe 'APIs from ApplicationController' do
18 | it 'provides #current_user' do
19 | user.destroy
20 | get :index, {}, valid_session
21 | end
22 |
23 | it 'provides #nickname_required' do
24 | get :index, {}, no_nickname_session
25 | expect(response).to redirect_to edit_user_path(user_without_nickname)
26 | end
27 | end
28 |
29 | end
30 |
31 | describe CommentsController do
32 |
33 | describe 'APIs from ApplicationController' do
34 | it 'provides #login_required' do
35 | post :create, {:gist_id => 1}, {}
36 | expect(response.status).to eq(302)
37 | expect(response).to redirect_to('http://test.host/signin?return_to=http%3A%2F%2Ftest.host%2Fgists%2F1%2Fcomments')
38 | end
39 | end
40 |
41 | end
42 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2012 Kazuhiro Sera
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 NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
24 |
--------------------------------------------------------------------------------
/app/views/users/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= render :partial => 'common/flash_notice' %>
4 |
5 |
6 | <%= gravatar_image(@user, :size => 30) %>
7 | <%= @user.nickname %>
8 |
9 |
10 |
11 |
12 |
13 | <%= @user.omniauth_provider %>
14 |
15 |
16 | <%= @user.omniauth_uid %>
17 |
18 |
19 |
20 | <% if @user.id == current_user.try(:id) %>
21 |
<%= link_to 'Edit', edit_user_path(@user) %>
22 |
23 |
<%= link_to 'Delete', user_path(@user), :method => :delete, :data => {:confirm => 'Are you sure?'} %>
24 | <% end %>
25 |
26 |
27 |
Public Gists
28 |
29 |
30 | <%= render :partial => 'gists/user_page' %>
31 |
32 |
33 |
Favorite Gists
34 |
35 |
36 | <%= render :partial => 'gists/user_fav_page' %>
37 |
38 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/models/gist_persistence.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class GistPersistence
3 | include BasicPersistence
4 |
5 | def initialize(flash)
6 | @flash = flash
7 | end
8 |
9 | def save_history!(gist_id, user_id)
10 | GistHistory.create!(
11 | gist_id: gist_id,
12 | user_id: user_id
13 | )
14 | end
15 |
16 | def save_files!(history, gist_files)
17 | if gist_files.select { |name, body| body.present? }.empty?
18 | @flash[:error] = 'Gist file is required.'
19 | raise 'Gist files are required!'
20 | end
21 | # If file name is absent, add default name such as file1,2,3... instead.
22 | file_count = 1
23 | gist_files.each do |name, body|
24 | GistFile.create(
25 | gist_history_id: history.id,
26 | name: name.present? ? name : "file#{file_count}",
27 | body: body
28 | )
29 | file_count += 1
30 | end
31 | end
32 |
33 | def save!(gist, gist_files, current_user)
34 | transaction do
35 | if gist.save!
36 | history = save_history!(gist.id, current_user.try(:id))
37 | save_files!(history, gist_files)
38 | end
39 | end
40 | end
41 |
42 | end
43 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Gistub",
3 | "description": "Sharing code snippets in-house",
4 | "keywords": ["gist"],
5 | "scripts": {
6 | "postdeploy": "bundle exec rake db:create db:migrate"
7 | },
8 | "env": {
9 | "BUILDPACK_URL": "https://github.com/heroku/heroku-buildpack-multi.git",
10 | "BUNDLE_WITHOUT": {
11 | "description": "bundle install --without ",
12 | "value": "test:development"
13 | },
14 | "GISTUB_APP_NAME": {
15 | "description": "variable to customize header",
16 | "value": "",
17 | "required": false
18 | },
19 | "GISTUB_OPENID_IDENTIFIER": {
20 | "description": "specify auth server instead of Google OpenID",
21 | "value": "",
22 | "required": false
23 | },
24 | "GISTUB_AUTO_LINK": {
25 | "description": "whether enabling rails_autolink",
26 | "value": "true",
27 | "required": false
28 | },
29 | "GISTUB_ALLOWS_ANONYMOUS": {
30 | "description": "whether enabling anonymous post",
31 | "value": "false",
32 | "required": false
33 | },
34 | "GISTUB_SECRET_TOKEN": {
35 | "generator": "secret"
36 | },
37 | "GISTUB_SECRET_KEY_BASE": {
38 | "generator": "secret"
39 | }
40 | },
41 | "addons": [
42 | "papertrail"
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | Gistub::Application.routes.draw do
3 |
4 | root :to => 'root#index'
5 |
6 | resources :gists do
7 |
8 | get :fork # after omniauth callback
9 | post :fork
10 |
11 | member do
12 | get 'history/:gist_history_id' => 'gists#show_history', :as => :show_history
13 | get 'raw_file' => 'gists#show_single_raw_file', :as => :show_single_raw_file, :format => :text
14 | get 'raw_file/:gist_file_id' => 'gists#show_raw_file', :as => :show_raw_file, :format => :text
15 | end
16 |
17 | collection do
18 | get :page
19 | get :find_my_recent_gists
20 | get :mine
21 | get :mine_page
22 | get :user_page
23 | get :user_fav_page
24 | get :add_gist_files_input
25 | get :search
26 | end
27 |
28 | resources :comments, :only => [:create, :destroy]
29 | resources :favorites, :only => [:create, :destroy]
30 |
31 | end
32 |
33 | resources :users, :only => [:edit, :update, :destroy, :show]
34 |
35 | get '/signin' => 'sessions#start', :as => :signin
36 | get '/auth/:provider/callback' => 'sessions#create'
37 | post '/auth/:provider/callback' => 'sessions#create'
38 | get '/signout' => 'sessions#destroy', :as => :signout
39 | get '/auth/failure' => 'sessions#failure'
40 |
41 | end
42 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | Gistub::Application.configure do
3 | # Settings specified here will take precedence over those in config/application.rb
4 |
5 | # In the development environment your application's code is reloaded on
6 | # every request. This slows down response time but is perfect for development
7 | # since you don't have to restart the web server when you make code changes.
8 | config.cache_classes = false
9 |
10 | # Do not eager load code on boot.
11 | config.eager_load = false
12 |
13 | # Show full error reports and disable caching
14 | config.consider_all_requests_local = true
15 | config.action_controller.perform_caching = false
16 |
17 | # Don't care if the mailer can't send
18 | config.action_mailer.raise_delivery_errors = false
19 |
20 | # Print deprecation notices to the Rails logger
21 | config.active_support.deprecation = :log
22 |
23 | # Only use best-standards-support built into browsers
24 | config.action_dispatch.best_standards_support = :builtin
25 |
26 | # Do not compress assets
27 | config.assets.compress = false
28 |
29 | # Expands the lines which load the assets
30 | config.assets.debug = true
31 |
32 | config.after_initialize do
33 | Bullet.enable = true
34 | #Bullet.raise = true
35 | Bullet.rails_logger = true
36 | end
37 |
38 | end
39 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | Gistub::Application.configure do
3 |
4 | # Do not eager load code on boot. This avoids loading your whole application
5 | # just for the purpose of running a single test. If you are using a tool that
6 | # preloads Rails for running tests, you may have to set it to true.
7 | config.eager_load = false
8 |
9 | # Configure static asset server for tests with Cache-Control for performance.
10 | config.serve_static_assets = true
11 |
12 | config.static_cache_control = "public, max-age=3600"
13 |
14 | # Show full error reports and disable caching.
15 | config.consider_all_requests_local = true
16 | config.action_controller.perform_caching = false
17 |
18 | # Raise exceptions instead of rendering exception templates.
19 | config.action_dispatch.show_exceptions = false
20 |
21 | # Disable request forgery protection in test environment.
22 | config.action_controller.allow_forgery_protection = false
23 |
24 | # Tell Action Mailer not to deliver emails to the real world.
25 | # The :test delivery method accumulates sent emails in the
26 | # ActionMailer::Base.deliveries array.
27 | config.action_mailer.delivery_method = :test
28 |
29 | # Print deprecation notices to the stderr.
30 | config.active_support.deprecation = :stderr
31 |
32 | config.after_initialize do
33 | Bullet.enable = true
34 | #Bullet.raise = true
35 | Bullet.rails_logger = true
36 | end
37 |
38 | end
39 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-sql.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/sql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/sql_highlight_rules","ace/range"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./sql_highlight_rules").SqlHighlightRules,u=e("../range").Range,a=function(){var e=new o;this.$tokenizer=new s(e.getRules()),this.$keywordList=e.$keywordList};r.inherits(a,i),function(){this.lineCommentStart="--"}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/sql_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="select|insert|update|delete|from|where|and|or|group|by|order|limit|offset|having|as|case|when|else|end|type|left|right|join|on|outer|desc|asc",t="true|false|null",n="count|min|max|avg|sum|rank|now|coalesce",r=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t},"identifier",!0);this.$rules={start:[{token:"comment",regex:"--.*$"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]}};r.inherits(s,i),t.SqlHighlightRules=s})
--------------------------------------------------------------------------------
/spec/controllers/favorites_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe FavoritesController do
5 | let(:gist) { create(:gist) }
6 | let(:user) { create(:user) }
7 |
8 | def valid_session
9 | {:user_id => user.id}
10 | end
11 |
12 | describe "POST create" do
13 | it "creates a new Favorite and redirects to the gist" do
14 | expect {
15 | post :create, {:gist_id => gist.id}, valid_session
16 | }.to change(Favorite, :count).by(1)
17 | expect(response).to redirect_to(gist)
18 | end
19 |
20 | it "shows ../gists/show when failing to create a new favorite" do
21 | allow_any_instance_of(Favorite).to receive(:save).and_return(false)
22 | post :create, {:gist_id => gist.id}, valid_session
23 | expect(response).to render_template("../gists/show")
24 | end
25 | end
26 |
27 | describe "DELETE destroy" do
28 | it "destroys the requested favorite and redirects to the gist page" do
29 | fav = create(:favorite, :gist => gist, :user => user)
30 | expect {
31 | delete :destroy, {:gist_id => gist.id, :id => fav.id}, valid_session
32 | }.to change(Favorite, :count).by(-1)
33 | expect(response).to redirect_to(gist)
34 | end
35 |
36 | it "redirects to gist when the favorite is not found" do
37 | expect {
38 | delete :destroy, {:gist_id => gist.id, :id => -1}, valid_session
39 | }.to change(Favorite, :count).by(0)
40 | expect(response).to redirect_to(gist)
41 | end
42 | end
43 |
44 | end
45 |
--------------------------------------------------------------------------------
/app/controllers/users_controller.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class UsersController < ApplicationController
3 |
4 | skip_before_action :nickname_required, only: [:edit, :update, :destroy]
5 | before_action :login_required, only: [:edit, :update, :destroy]
6 |
7 | respond_to :html
8 |
9 | def show
10 | @user = User.find(params[:id])
11 | @gists = Gist.where(user_id: @user.id).recent.page(1).per(10)
12 | @favorites = Favorite.where(user_id: @user.id).page(1).per(10)
13 | end
14 |
15 | def edit
16 | if current_user.try(:id) == params[:id].to_i
17 | @user = User.find(params[:id])
18 | render action: 'edit'
19 | else
20 | redirect_to root_path
21 | end
22 | end
23 |
24 | def update
25 | if current_user.try(:id) == params[:id].to_i
26 | @user = User.find(params[:id])
27 | if params[:user][:nickname].blank?
28 | flash[:notice] = 'Nickname is required.'
29 | return render action: 'edit'
30 | end
31 | if @user.update_attributes(user_params)
32 | redirect_to @user, notice: 'User was successfully updated.'
33 | else
34 | render action: 'edit'
35 | end
36 | else
37 | redirect_to root_path
38 | end
39 | end
40 |
41 | def destroy
42 | if current_user.id == params[:id].to_i
43 | user = User.find(current_user.id)
44 | user.destroy
45 | reset_session
46 | end
47 | redirect_to root_path
48 | end
49 |
50 | private
51 |
52 | def user_params
53 | params.require(:user).permit(:nickname)
54 | end
55 |
56 | end
57 |
--------------------------------------------------------------------------------
/spec/controllers/comments_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe CommentsController do
5 | let(:gist) { create(:gist) }
6 | let(:user) { create(:user) }
7 |
8 | def valid_session
9 | {:user_id => user.id}
10 | end
11 |
12 | describe "POST create" do
13 | describe "with valid params" do
14 | it "creates a new Comment and redirects to the gist" do
15 | expect {
16 | post :create, {:gist_id => gist.id, :body => 'LGTM'}, valid_session
17 | }.to change(Comment, :count).by(1)
18 | expect(response).to redirect_to(gist)
19 | end
20 | end
21 |
22 | describe "with invalid params" do
23 | it "re-renders the 'gists/show' template" do
24 | allow_any_instance_of(Comment).to receive(:save).and_return(false)
25 | post :create, {:gist_id => gist.id, :comment => {}}, valid_session
26 | expect(response).to render_template("../gists/show")
27 | end
28 | end
29 | end
30 |
31 | describe "DELETE destroy" do
32 | it "destroys the requested comment and redirects to the gist page" do
33 | comment = create(:comment, :gist => gist, :user => user)
34 | expect {
35 | delete :destroy, {:gist_id => gist.id, :id => comment.id}, valid_session
36 | }.to change(Comment, :count).by(-1)
37 | end
38 | it "redirects to gist when deleting non-existing comment" do
39 | expect {
40 | delete :destroy, {:gist_id => gist.id, :id => -1}, valid_session
41 | }.to change(Comment, :count).by(0)
42 | expect(response).to redirect_to(gist)
43 | end
44 | end
45 |
46 | end
47 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | class ApplicationController < ActionController::Base
3 |
4 | protect_from_forgery
5 |
6 | before_action :load_current_user, :nickname_required
7 |
8 | helper_method [:current_user, :anonymous_allowed]
9 |
10 | private
11 |
12 | def render_404
13 | render file: "#{Rails.root}/public/404", formats: [:html], status: 404
14 | end
15 |
16 | def anonymous_allowed
17 | Gistub::Application.config.gistub_allows_anonymous
18 | end
19 |
20 | def nickname_required
21 | if current_user.present? && current_user.nickname.nil?
22 | redirect_to edit_user_path(current_user)
23 | end
24 | end
25 |
26 | def login_required
27 | redirect_to signin_path(return_to: request.url) if current_user.blank?
28 | end
29 |
30 | def load_current_user
31 | if @cached_current_user.present?
32 | @cached_current_user
33 | else
34 | begin
35 | @cached_current_user = session[:user_id].present? ? User.find(session[:user_id]) : nil
36 | rescue Exception => e
37 | Rails.logger.debug e
38 | reset_session
39 | nil
40 | end
41 | end
42 | end
43 |
44 | def current_user
45 | @cached_current_user
46 | end
47 |
48 | def destroy_and_redirect_to_gist(active_record_model, success_notice, failure_notice)
49 | gist_id = params[:gist_id]
50 | if active_record_model.present?
51 | active_record_model.destroy
52 | redirect_to gist_path(gist_id), notice: success_notice
53 | else
54 | redirect_to gist_path(gist_id), notice: failure_notice
55 | end
56 | end
57 |
58 | def debug_log_back_trace(e)
59 | Rails.logger.debug e.backtrace.join("\n")
60 | end
61 |
62 | end
63 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-ada.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/ada",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/ada_highlight_rules","ace/range"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./ada_highlight_rules").AdaHighlightRules,u=e("../range").Range,a=function(){this.$tokenizer=new s((new o).getRules())};r.inherits(a,i),function(){this.lineCommentStart="--"}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/ada_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="abort|else|new|return|abs|elsif|not|reverse|abstract|end|null|accept|entry|select|access|exception|of|separate|aliased|exit|or|some|all|others|subtype|and|for|out|synchronized|array|function|overriding|at|tagged|generic|package|task|begin|goto|pragma|terminate|body|private|then|if|procedure|type|case|in|protected|constant|interface|until||is|raise|use|declare|range|delay|limited|record|when|delta|loop|rem|while|digits|renames|with|do|mod|requeue|xor",t="true|false|null",n="count|min|max|avg|sum|rank|now|coalesce|main",r=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t},"identifier",!0);this.$rules={start:[{token:"comment",regex:"--.*$"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]}};r.inherits(s,i),t.AdaHighlightRules=s})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-scheme.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/scheme",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/scheme_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./scheme_highlight_rules").SchemeHighlightRules,u=function(){var e=new o;this.$tokenizer=new s(e.getRules())};r.inherits(u,i),function(){this.lineCommentStart=";"}.call(u.prototype),t.Mode=u}),ace.define("ace/mode/scheme_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="case|do|let|loop|if|else|when",t="eq?|eqv?|equal?|and|or|not|null?",n="#t|#f",r="cons|car|cdr|cond|lambda|lambda*|syntax-rules|format|set!|quote|eval|append|list|list?|member?|load",i=this.createKeywordMapper({"keyword.control":e,"keyword.operator":t,"constant.language":n,"support.function":r},"identifier",!0);this.$rules={start:[{token:"comment",regex:";.*$"},{token:["storage.type.function-type.scheme","text","entity.name.function.scheme"],regex:"(?:\\b(?:(define|define-syntax|define-macro))\\b)(\\s+)((?:\\w|\\-|\\!|\\?)*)"},{token:"punctuation.definition.constant.character.scheme",regex:"#:\\S+"},{token:["punctuation.definition.variable.scheme","variable.other.global.scheme","punctuation.definition.variable.scheme"],regex:"(\\*)(\\S*)(\\*)"},{token:"constant.numeric",regex:"#[xXoObB][0-9a-fA-F]+"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?"},{token:i,regex:"[a-zA-Z_#][a-zA-Z0-9_\\-\\?\\!\\*]*"},{token:"string",regex:'"(?=.)',next:"qqstring"}],qqstring:[{token:"constant.character.escape.scheme",regex:"\\\\."},{token:"string",regex:'[^"\\\\]+',merge:!0},{token:"string",regex:"\\\\$",next:"qqstring",merge:!0},{token:"string",regex:'"|$',next:"start",merge:!0}]}};r.inherits(s,i),t.SchemeHighlightRules=s})
--------------------------------------------------------------------------------
/app/assets/stylesheets/code_prettify.css:
--------------------------------------------------------------------------------
1 | /* Pretty printing styles. Used with prettify.js. */
2 |
3 | /* SPAN elements with the classes below are added by prettyprint. */
4 | .pln { color: #000 } /* plain text */
5 |
6 | @media screen {
7 | .str { color: #006400 } /* string content */
8 | .kwd { color: #00008b } /* a keyword */
9 | .com { color: #d2b48c } /* a comment */
10 | .typ { color: #b22222 } /* a type name */
11 | .lit { color: #8b4513 } /* a literal value */
12 | /* punctuation, lisp open bracket, lisp close bracket */
13 | .pun, .opn, .clo { color: #660 }
14 | .tag { color: #008 } /* a markup tag name */
15 | .atn { color: #606 } /* a markup attribute name */
16 | .atv { color: #080 } /* a markup attribute value */
17 | .dec, .var { color: #606 } /* a declaration; a variable name */
18 | .fun { color: red } /* a function name */
19 | }
20 |
21 | /* Use higher contrast and text-weight for printable form. */
22 | @media print, projection {
23 | .str { color: #006400 }
24 | .kwd { color: #00008b; font-weight: bold }
25 | .com { color: #d2b48c; font-style: italic }
26 | .typ { color: #b22222; font-weight: bold }
27 | .lit { color: #8b4513 }
28 | .pun, .opn, .clo { color: #440 }
29 | .tag { color: #006; font-weight: bold }
30 | .atn { color: #404 }
31 | .atv { color: #060 }
32 | }
33 |
34 | /* Specify class=linenums on a pre to get line numbering */
35 | ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE } /* IE indents via margin-left */
36 |
37 | /* Put a border around prettyprinted code snippets. */
38 | pre.prettyprint { padding: 10px 10px 10px 30px; border: 1px solid #888 }
39 |
40 | li.L0,
41 | li.L1,
42 | li.L2,
43 | li.L3,
44 | li.L5,
45 | li.L6,
46 | li.L7,
47 | /*li.L8 { list-style-type: none } */
48 |
49 | /* Alternate shading for lines */
50 | li.L1,
51 | li.L3,
52 | li.L5,
53 | li.L7,
54 | /* li.L9 { background: #eee } */
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # TODO: rails 4.2
4 | gem 'rails', '5.2.6.2'
5 | gem 'jquery-rails', '>= 4.0.1'
6 | gem 'rails_autolink', '>= 1.1.6'
7 |
8 | gem 'qiita-markdown', :platforms => :ruby
9 | gem 'kramdown', :platforms => :jruby
10 |
11 | gem 'omniauth-openid'
12 | gem 'erubis'
13 | gem 'kaminari', '>= 0.16.1'
14 |
15 | gem 'simple_form', '>= 4.0.0'
16 |
17 | gem 'coveralls', require: false
18 |
19 | gem 'pg', group: :postgresql
20 |
21 | group :development do
22 | # better_errors 2.0 requires Ruby 2.0 or higher
23 | gem 'better_errors', '1.1.0'
24 | gem 'magic_encoding'
25 | gem 'binding_of_caller', :platforms => :ruby
26 | end
27 |
28 | group :test, :development do
29 | gem 'bullet'
30 | gem 'pry-rails'
31 | gem 'sqlite3', :platforms => :ruby
32 | gem 'activerecord-jdbcsqlite3-adapter', :platforms => :jruby
33 | gem 'factory_girl'
34 | gem 'factory_girl_rails', '>= 4.5.0'
35 | # TODO: rspec 3
36 | gem 'rspec-rails', '2.14.2'
37 | gem 'rspec-kickstarter'
38 | end
39 |
40 | group :test do
41 | gem 'simplecov', :require => false
42 | gem 'simplecov-rcov', :require => false
43 | end
44 |
45 | group :assets do
46 | gem 'less-rails', '>= 2.6.0'
47 | gem 'twitter-bootstrap-rails', '>= 2.2.8'
48 | gem 'therubyracer', :platforms => :ruby
49 | gem 'therubyrhino', :platforms => :jruby
50 | gem 'uglifier', '>= 1.0.3'
51 | end
52 |
53 | group :server do
54 | # bin/rails s mizuno
55 | gem 'mizuno', :platforms => :jruby
56 | gem 'thin', :platforms => :ruby
57 | end
58 |
59 | # rails g rspec:install
60 | # rails g simple_form:install --bootstrap
61 | # rails g bootstrap:install
62 | # rails g bootstrap:layout application fluid
63 | # rails g kaminari:config
64 |
65 | # bundle exec rake assets:precompile RAILS_ENV=production RAILS_GROUPS=assets
66 |
67 | # JRuby
68 | # see: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md
69 | # export JRUBY_OPTS=--1.9
70 |
71 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-lisp.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/lisp",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/lisp_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./lisp_highlight_rules").LispHighlightRules,u=function(){var e=new o;this.$tokenizer=new s(e.getRules()),this.$keywordList=e.$keywordList};r.inherits(u,i),function(){this.lineCommentStart=";"}.call(u.prototype),t.Mode=u}),ace.define("ace/mode/lisp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="case|do|let|loop|if|else|when",t="eq|neq|and|or",n="null|nil",r="cons|car|cdr|cond|lambda|format|setq|setf|quote|eval|append|list|listp|memberp|t|load|progn",i=this.createKeywordMapper({"keyword.control":e,"keyword.operator":t,"constant.language":n,"support.function":r},"identifier",!0);this.$rules={start:[{token:"comment",regex:";.*$"},{token:["storage.type.function-type.lisp","text","entity.name.function.lisp"],regex:"(?:\\b(?:(defun|defmethod|defmacro))\\b)(\\s+)((?:\\w|\\-|\\!|\\?)*)"},{token:["punctuation.definition.constant.character.lisp","constant.character.lisp"],regex:"(#)((?:\\w|[\\\\+-=<>'\"])+)"},{token:["punctuation.definition.variable.lisp","variable.other.global.lisp","punctuation.definition.variable.lisp"],regex:"(\\*)(\\S*)(\\*)"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+(?:L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?(?:L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"string",regex:'"(?=.)',next:"qqstring"}],qqstring:[{token:"constant.character.escape.lisp",regex:"\\\\."},{token:"string",regex:'[^"\\\\]+'},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"start"}]}};r.inherits(s,i),t.LispHighlightRules=s})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-textile.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/textile",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/textile_highlight_rules","ace/mode/matching_brace_outdent"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./textile_highlight_rules").TextileHighlightRules,u=e("./matching_brace_outdent").MatchingBraceOutdent,a=function(){this.$tokenizer=new s((new o).getRules()),this.$outdent=new u};r.inherits(a,i),function(){this.getNextLineIndent=function(e,t,n){return e=="intag"?n:""},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)}}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/textile_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:function(e){return e.charAt(0)=="h"?"markup.heading."+e.charAt(1):"markup.heading"},regex:"h1|h2|h3|h4|h5|h6|bq|p|bc|pre",next:"blocktag"},{token:"keyword",regex:"[\\*]+|[#]+"},{token:"text",regex:".+"}],blocktag:[{token:"keyword",regex:"\\. ",next:"start"},{token:"keyword",regex:"\\(",next:"blocktagproperties"}],blocktagproperties:[{token:"keyword",regex:"\\)",next:"blocktag"},{token:"string",regex:"[a-zA-Z0-9\\-_]+"},{token:"keyword",regex:"#"}]}};r.inherits(s,i),t.TextileHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i})
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 | <% app_name = ENV['GISTUB_APP_NAME'].presence || 'Gistub' %>
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= content_for?(:title) ? yield(:title) + ' - ' + app_name : app_name %>
9 | <%= csrf_meta_tags %>
10 |
11 |
14 |
15 | <%= stylesheet_link_tag "application", :media => "all" %>
16 | <%= javascript_include_tag "header" %>
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <% if current_user.present? %>
26 | <%= link_to current_user.nickname, user_path(current_user), :class => 'btn btn-small' %>
27 | <%= link_to 'Sign out', signout_path, :class => 'btn btn-small' %>
28 | <% else %>
29 | <%= link_to 'Sign in', signin_path, :class => 'btn btn-small' %>
30 | <% end %>
31 |
32 | <%= link_to app_name, root_path, :class => 'brand' %>
33 |
34 |
35 |
36 | <%= link_to "New Gist", new_gist_path %>
37 | <%= link_to "Public Gists", gists_path %>
38 | <%= link_to "My Gists", mine_gists_path %>
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | <%= yield %>
49 |
50 |
51 |
Gistub : Sharing code snippets in-house - version 1.3.6
52 |
53 |
54 |
55 |
56 | <%= javascript_include_tag "application" %>
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'simplecov'
3 | if ENV['TRAVIS']
4 | require 'coveralls'
5 | Coveralls.wear!
6 | else
7 | SimpleCov.start 'rails'
8 | end
9 |
10 | # This file is copied to spec/ when you run 'rails generate rspec:install'
11 | ENV["RAILS_ENV"] ||= 'test'
12 | require File.expand_path("../../config/environment", __FILE__)
13 | require 'rspec/rails'
14 | require 'rspec/autorun'
15 |
16 | # Requires supporting ruby files with custom matchers and macros, etc,
17 | # in spec/support/ and its subdirectories.
18 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
19 |
20 | RSpec.configure do |config|
21 |
22 | # ## Mock Framework
23 | #
24 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
25 | #
26 | # config.mock_with :mocha
27 | # config.mock_with :flexmock
28 | # config.mock_with :rr
29 |
30 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
31 | #config.fixture_path = "#{::Rails.root}/spec/fixtures"
32 |
33 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
34 | # examples within a transaction, remove the following line or assign false
35 | # instead of true.
36 | config.use_transactional_fixtures = true
37 |
38 | # If true, the base class of anonymous controllers will be inferred
39 | # automatically. This will be the default behavior in future versions of
40 | # rspec-rails.
41 | config.infer_base_class_for_anonymous_controllers = false
42 |
43 | # Run specs in random order to surface order dependencies. If you find an
44 | # order dependency and want to debug it, you can fix the order by providing
45 | # the seed, which is printed after each run.
46 | # --seed 1234
47 | config.order = "random"
48 |
49 | # https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md
50 | # If repeating "FactoryGirl" is too verbose for you, you can mix the syntax methods in:
51 | config.include FactoryGirl::Syntax::Methods
52 |
53 | end
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gistub : Sharing code snippets in-house
2 |
3 | Gistub is a stand alone application for sharing snippet such as `gist.github.com`.
4 |
5 | If you're familiar with Rails apps, you can set up Gistub in several minutes.
6 |
7 | Many companies and organizations use Gistub for sharing code snippets safely in house.
8 |
9 | [](https://travis-ci.org/seratch/gistub)
10 | [](https://coveralls.io/r/seratch/gistub?branch=develop)
11 | [](https://codeclimate.com/github/seratch/gistub)
12 |
13 | [](https://heroku.com/deploy)
14 |
15 | ## How to use
16 |
17 | `master` branch is always the lastest stable version.
18 |
19 | ```sh
20 | git clone git://github.com/seratch/gistub.git -b master
21 | cd gistub
22 | bin/bundle install
23 | bin/rake db:migrate
24 | bin/rails s
25 | ```
26 |
27 | Access `http://localhost:3000/` through web browser.
28 |
29 | ## Live Demo
30 |
31 | http://gistub.herokuapp.com/
32 |
33 | Top page:
34 |
35 | 
36 |
37 | Rich Editor with Ace:
38 |
39 | 
40 |
41 |
42 | ## Configuration over environment variables
43 |
44 | Specify settings in `.bashrc` or others.
45 |
46 | ```
47 | export GISTUB_APP_NAME="MyGistub"
48 | export GISTUB_OPENID_IDENTIFIER=https://your_auth_server/openid/
49 | export GISTUB_AUTO_LINK=true
50 | export GISTUB_ALLOWS_ANONYMOUS=false
51 | export GISTUB_SECRET_TOKEN=xxx...
52 | export GISTUB_SECRET_KEY_BASE=yyy...
53 | ```
54 |
55 | ## Gistub Tools
56 |
57 | ### For Emacs users
58 |
59 | https://github.com/tototoshi/gistub-el
60 |
61 | ### For Vimmers
62 |
63 | https://github.com/glidenote/nogistub.vim
64 |
65 | ## License
66 |
67 | (The MIT License)
68 |
69 | Copyright (c) 2012 Kazuhiro Sera
70 |
71 |
72 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/bootstrap_and_overrides.css.less:
--------------------------------------------------------------------------------
1 | @import "twitter/bootstrap/bootstrap";
2 | @import "twitter/bootstrap/responsive";
3 | @import "twitter/bootstrap/sprites.less";
4 |
5 | // Set the correct sprite paths
6 | @iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings");
7 | @iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white");
8 |
9 | // Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
10 | @fontAwesomeEotPath: asset-url("fontawesome-webfont.eot");
11 | @fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot?#iefix");
12 | @fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff");
13 | @fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf");
14 | @fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular");
15 |
16 | // Font Awesome
17 | @import "fontawesome/font-awesome";
18 |
19 | // Set the correct sprite paths
20 | @iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings.png");
21 | @iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white.png");
22 |
23 | // Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
24 | @fontAwesomeEotPath: asset-url("fontawesome-webfont.eot");
25 | @fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot?#iefix");
26 | @fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff");
27 | @fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf");
28 | @fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular");
29 |
30 | // Font Awesome
31 | @import "fontawesome/font-awesome";
32 |
33 | // Your custom LESS stylesheets goes here
34 | //
35 | // Since bootstrap was imported above you have access to its mixins which
36 | // you may use and inherit here
37 | //
38 | // If you'd like to override bootstrap's own variables, you can do so here as well
39 | // See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation
40 | //
41 | // Example:
42 | // @linkColor: #ff0000;
43 |
44 | body { padding-top: 60px; }
45 |
46 |
--------------------------------------------------------------------------------
/app/views/root/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= render :partial => 'common/flash_notice' %>
4 |
5 | <% unless @gists.to_a.size == 0 %>
6 |
Latest 5 Public Gists
7 | <%= render :partial => 'common/search_form' %>
8 |
9 |
10 | <% @gists.each do |gist| %>
11 | <% if gist.latest_history.present? %>
12 |
13 | <%= link_to gist.title, gist %>
14 | <% if gist.favorites.present? %>
15 | <%= gist.favorites.size %> <% if gist.favorites.size == 1 %>like<% else %>likes<% end %><% if gist.comments.present? %>,<% end %>
16 | <% end %>
17 | <% if gist.comments.present? %>
18 | <%= gist.comments.size %> <% if gist.comments.size == 1 %>comment<% else %>comments<% end %>
19 | <% end %>
20 |
21 |
22 | <%= time_ago_in_words(gist.latest_history.created_at) + ' ago' %>
23 | by
24 | <% if gist.user.nil? %>Anonymous
25 | <% else %>
26 | <%= link_to gist.user.nickname, user_path(gist.user) %>
27 | <%= gravatar_image(gist.user, :size => 20) %>
28 | <% end %>
29 |
30 |
31 |
32 |
<%= gist.latest_history.headline %>
33 | <% end %>
34 | <% end %>
35 |
36 |
37 |
38 | <%= link_to 'Read more...', gists_path %>
39 |
40 |
41 |
42 |
43 | <% end %>
44 |
45 |
New Gist
46 |
47 | <%= render 'gists/form' %>
48 |
49 |
54 |
55 |
56 |
57 | <%= render :partial => 'common/mygists' %>
58 | <%= render :partial => 'common/favorites' %>
59 |
60 |
--------------------------------------------------------------------------------
/spec/controllers/sessions_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe SessionsController do
5 | let(:user) { create(:user) }
6 |
7 | def valid_session
8 | {:user_id => user.id}
9 | end
10 |
11 | describe "GET /sessions/start" do
12 | it "accepts requests" do
13 | get :start, {:return_to => '/foo'}, valid_session
14 | expect(response.status).to eq(302)
15 | expect(response.location).to eq("http://test.host/auth/open_id?return_to=/foo")
16 | end
17 | end
18 |
19 | describe "GET /sessions/failure" do
20 | it "accepts requests" do
21 | get :failure, {}, valid_session
22 | expect(response.status).to eq(200)
23 | end
24 | end
25 |
26 | describe "GET /auth/:provider/callback" do
27 | it "accepts requests without header" do
28 | get :create, {:provider => 'open_id'}
29 | expect(response.status).to eq(302)
30 | expect(response).to redirect_to root_path
31 | end
32 | it "creates new user" do
33 | expect {
34 | request.env['omniauth.auth'] = {
35 | 'provider' => 'open_id',
36 | 'uid' => 'yyy',
37 | 'info' => { 'email' => 'test@test.org' }
38 | }
39 | get :create, {:provider => 'open_id'}
40 | }.to change { User.count }.from(0).to(1)
41 | u = User.first
42 | expect(u.omniauth_provider).to eq('open_id')
43 | expect(u.omniauth_uid).to eq('yyy')
44 | expect(u.email).to eq('test@test.org')
45 | end
46 | it "updates existing user" do
47 | expect {
48 | request.env['omniauth.auth'] = {
49 | 'provider' => user.omniauth_provider,
50 | 'uid' => user.omniauth_uid,
51 | 'info' => { 'email' => 'test@test.org' }
52 | }
53 | get :create, {:provider => user.omniauth_provider}
54 | }.not_to change { User.count }.from(1)
55 | user.reload
56 | expect(user.email).to eq('test@test.org')
57 | end
58 | end
59 |
60 | describe "DELETE /sessions/destroy" do
61 | it "accepts requests" do
62 | post :destroy, {}, valid_session
63 | expect(response.status).to eq(302)
64 | expect(response).to redirect_to(root_path)
65 | end
66 | end
67 |
68 | end
69 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | begin
3 | require 'kramdown'
4 | rescue LoadError
5 | end
6 |
7 | require 'digest/md5'
8 | require 'cgi'
9 |
10 | module ApplicationHelper
11 |
12 | def is_anonymous_gist_allowed
13 | anonymous_allowed || current_user.present?
14 | end
15 |
16 | def my_gists
17 | if current_user.present?
18 | Gist.limit(5).find_my_recent_gists(current_user.id)
19 | else
20 | nil
21 | end
22 | end
23 |
24 | def my_favorite_gists
25 | if current_user.present?
26 | favorites = Favorite.where(user_id: current_user.id).limit(5).order(:id).reverse_order
27 | favorites.map { |fav| fav.gist }.select(&:present?)
28 | else
29 | nil
30 | end
31 | end
32 |
33 | def recent_gists
34 | Gist.recent.limit(10).find_all
35 | end
36 |
37 | def is_already_favorited(gist)
38 | find_my_favorite(gist).present?
39 | end
40 |
41 | def find_my_favorite(gist)
42 | if current_user.present?
43 | Favorite.where(user_id: current_user.id).where(gist_id: gist.id).first
44 | else
45 | nil
46 | end
47 | end
48 |
49 | def favorite_users(gist)
50 | gist.favorites.map { |f| f.user }
51 | end
52 |
53 | def markdown(md_body)
54 | if defined?(Kramdown)
55 | # JRuby
56 | Kramdown::Document.new(md_body).to_html
57 | else
58 | # MRI
59 | processor = Qiita::Markdown::Processor.new
60 | result = processor.call(md_body)
61 | result[:output].to_s.html_safe
62 | end
63 | rescue Exception
64 | md_body
65 | end
66 |
67 | def gravatar_image(user, options = {})
68 | return nil if user.nil? || user.email.nil?
69 |
70 | hash = Digest::MD5.hexdigest(user.email.downcase)
71 | opts = options.dup
72 | query = opts.select { |k, v| [:size, :d].include?(k) }
73 | .map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }
74 | .join('&')
75 | img_options = {
76 | :src => "//www.gravatar.com/avatar/#{hash}?#{query}",
77 | :alt => user.nickname
78 | }
79 | if size = options[:size]
80 | img_options[:width] = size
81 | img_options[:height] = size
82 | end
83 |
84 | tag 'img', img_options, false, false
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/spec/routing/gists_routing_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require "spec_helper"
3 |
4 | describe GistsController do
5 | describe "routing" do
6 |
7 | it "routes to #index" do
8 | expect(get("/gists")).to route_to("gists#index")
9 | end
10 |
11 | it "routes to #new" do
12 | expect(get("/gists/new")).to route_to("gists#new")
13 | end
14 |
15 | it "routes to #show" do
16 | expect(get("/gists/1")).to route_to("gists#show", :id => "1")
17 | end
18 |
19 | it "routes to #show_history" do
20 | expect(get("/gists/1/history/2")).to route_to("gists#show_history", :id => "1", :gist_history_id => "2")
21 | end
22 |
23 | it "routes to #show_raw_file" do
24 | expect(get("/gists/1/raw_file/2")).to route_to("gists#show_raw_file", :id => "1", :gist_file_id => "2")
25 | end
26 |
27 | it "routes to #edit" do
28 | expect(get("/gists/1/edit")).to route_to("gists#edit", :id => "1")
29 | end
30 |
31 | it "routes to #create" do
32 | expect(post("/gists")).to route_to("gists#create")
33 | end
34 |
35 | it "routes to #update" do
36 | expect(put("/gists/1")).to route_to("gists#update", :id => "1")
37 | end
38 |
39 | it "routes to #fork" do
40 | expect(post("/gists/1/fork")).to route_to("gists#fork", :gist_id => "1")
41 | end
42 |
43 | it "routes to #destroy" do
44 | expect(delete("/gists/1")).to route_to("gists#destroy", :id => "1")
45 | end
46 |
47 | it "routes to #page" do
48 | expect(get("/gists/page")).to route_to("gists#page")
49 | end
50 |
51 | it "routes to #user_page" do
52 | expect(get("/gists/user_page")).to route_to("gists#user_page")
53 | end
54 |
55 | it "routes to #mine" do
56 | expect(get("/gists/mine")).to route_to("gists#mine")
57 | end
58 |
59 | it "routes to #mine_page" do
60 | expect(get("/gists/mine_page")).to route_to("gists#mine_page")
61 | end
62 |
63 | it "routes to #user_fav_page" do
64 | expect(get("/gists/user_fav_page")).to route_to("gists#user_fav_page")
65 | end
66 |
67 | it "routes to #add_gist_files_input" do
68 | expect(get("/gists/add_gist_files_input")).to route_to("gists#add_gist_files_input")
69 | end
70 |
71 | end
72 | end
73 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-cobol.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/cobol",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/cobol_highlight_rules","ace/range"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./cobol_highlight_rules").CobolHighlightRules,u=e("../range").Range,a=function(){this.$tokenizer=new s((new o).getRules())};r.inherits(a,i),function(){this.lineCommentStart="*"}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/cobol_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="ACCEPT|MERGE|SUM|ADD||MESSAGE|TABLE|ADVANCING|MODE|TAPE|AFTER|MULTIPLY|TEST|ALL|NEGATIVE|TEXT|ALPHABET|NEXT|THAN|ALSO|NO|THEN|ALTERNATE|NOT|THROUGH|AND|NUMBER|THRU|ANY|OCCURS|TIME|ARE|OF|TO|AREA|OFF|TOP||ASCENDING|OMITTED|TRUE|ASSIGN|ON|TYPE|AT|OPEN|UNIT|AUTHOR|OR|UNTIL|BEFORE|OTHER|UP|BLANK|OUTPUT|USE|BLOCK|PAGE|USING|BOTTOM|PERFORM|VALUE|BY|PIC|VALUES|CALL|PICTURE|WHEN|CANCEL|PLUS|WITH|CD|POINTER|WRITE|CHARACTER|POSITION||ZERO|CLOSE|POSITIVE|ZEROS|COLUMN|PROCEDURE|ZEROES|COMMA|PROGRAM|COMMON|PROGRAM-ID|COMMUNICATION|QUOTE|COMP|RANDOM|COMPUTE|READ|CONTAINS|RECEIVE|CONFIGURATION|RECORD|CONTINUE|REDEFINES|CONTROL|REFERENCE|COPY|REMAINDER|COUNT|REPLACE|DATA|REPORT|DATE|RESERVE|DAY|RESET|DELETE|RETURN|DESTINATION|REWIND|DISABLE|REWRITE|DISPLAY|RIGHT|DIVIDE|RUN|DOWN|SAME|ELSE|SEARCH|ENABLE|SECTION|END|SELECT|ENVIRONMENT|SENTENCE|EQUAL|SET|ERROR|SIGN|EXIT|SEQUENTIAL|EXTERNAL|SIZE|FLASE|SORT|FILE|SOURCE|LENGTH|SPACE|LESS|STANDARD|LIMIT|START|LINE|STOP|LOCK|STRING|LOW-VALUE|SUBTRACT",t="true|false|null",n="count|min|max|avg|sum|rank|now|coalesce|main",r=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t},"identifier",!0);this.$rules={start:[{token:"comment",regex:"\\*.*$"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]}};r.inherits(s,i),t.CobolHighlightRules=s})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-toml.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/toml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/toml_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./toml_highlight_rules").TomlHighlightRules,u=e("./folding/cstyle").FoldMode,a=function(){var e=new o;this.foldingRules=new u,this.$tokenizer=new s(e.getRules())};r.inherits(a,i),function(){this.lineCommentStart="#"}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/toml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e=this.createKeywordMapper({"constant.language.boolean":"true|false"},"identifier"),t="[a-zA-Z\\$_¡-][a-zA-Z\\d\\$_¡-]*\\b";this.$rules={start:[{token:"comment.toml",regex:/#.*$/},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:["variable.keygroup.toml"],regex:"(?:^\\s*)(\\[([^\\]]+)\\])"},{token:e,regex:t},{token:"support.date.toml",regex:"\\d{4}-\\d{2}-\\d{2}(T)\\d{2}:\\d{2}:\\d{2}(Z)"},{token:"constant.numeric.toml",regex:"-?\\d+(\\.?\\d+)?"}],qqstring:[{token:"string",regex:"\\\\$",next:"qqstring"},{token:"constant.language.escape",regex:'\\\\[0tnr"\\\\]'},{token:"string",regex:'"|$',next:"start"},{defaultToken:"string"}]}};r.inherits(s,i),t.TomlHighlightRules=s}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i){var s=i.index;return i[1]?this.openingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s+i[0].length,1)}if(t!=="markbeginend")return;var i=r.match(this.foldingStopMarker);if(i){var s=i.index+i[0].length;return i[1]?this.closingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s,-1)}}}.call(o.prototype)})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-diff.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/diff",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/diff_highlight_rules","ace/mode/folding/diff"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./diff_highlight_rules").DiffHighlightRules,u=e("./folding/diff").FoldMode,a=function(){this.$tokenizer=new s((new o).getRules()),this.foldingRules=new u(["diff","index","\\+{3}","@@|\\*{5}"],"i")};r.inherits(a,i),function(){}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/diff_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{regex:"^(?:\\*{15}|={67}|-{3}|\\+{3})$",token:"punctuation.definition.separator.diff",name:"keyword"},{regex:"^(@@)(\\s*.+?\\s*)(@@)(.*)$",token:["constant","constant.numeric","constant","comment.doc.tag"]},{regex:"^(\\d+)([,\\d]+)(a|d|c)(\\d+)([,\\d]+)(.*)$",token:["constant.numeric","punctuation.definition.range.diff","constant.function","constant.numeric","punctuation.definition.range.diff","invalid"],name:"meta."},{regex:"^(\\-{3}|\\+{3}|\\*{3})( .+)$",token:["constant.numeric","meta.tag"]},{regex:"^([!+>])(.*?)(\\s*)$",token:["support.constant","text","invalid"]},{regex:"^([<\\-])(.*?)(\\s*)$",token:["support.function","string","invalid"]},{regex:"^(diff)(\\s+--\\w+)?(.+?)( .+)?$",token:["variable","variable","keyword","variable"]},{regex:"^Index.+$",token:"variable"},{regex:"^\\s+$",token:"text"},{regex:"\\s*$",token:"invalid"},{defaultToken:"invisible",caseInsensitive:!0}]}};r.inherits(s,i),t.DiffHighlightRules=s}),ace.define("ace/mode/folding/diff",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(e,t){this.regExpList=e,this.flag=t,this.foldingStartMarker=RegExp("^("+e.join("|")+")",this.flag)};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i={row:n,column:r.length},o=this.regExpList;for(var u=1;u<=o.length;u++){var a=RegExp("^("+o.slice(0,u).join("|")+")",this.flag);if(a.test(r))break}for(var f=e.getLength();++n span {font-weight: normal !important;}.ace-github .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-github .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-github .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-github .ace_gutter-active-line {background-color : rgba(0, 0, 0, 0.07);}.ace-github .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-github .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-github .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)})
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | # This file is auto-generated from the current state of the database. Instead
3 | # of editing this file, please use the migrations feature of Active Record to
4 | # incrementally modify your database, and then regenerate this schema definition.
5 | #
6 | # Note that this schema.rb definition is the authoritative source for your
7 | # database schema. If you need to create the application database on another
8 | # system, you should be using db:schema:load, not running all the migrations
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 | # you'll amass, the slower it'll run and the greater likelihood for issues).
11 | #
12 | # It's strongly recommended that you check this file into your version control system.
13 |
14 | ActiveRecord::Schema.define(version: 20140110041301) do
15 |
16 | create_table "comments", force: true do |t|
17 | t.integer "gist_id", null: false
18 | t.integer "user_id", null: false
19 | t.text "body", null: false
20 | t.datetime "created_at"
21 | t.datetime "updated_at"
22 | end
23 |
24 | create_table "favorites", force: true do |t|
25 | t.integer "user_id", null: false
26 | t.integer "gist_id", null: false
27 | t.datetime "created_at"
28 | t.datetime "updated_at"
29 | end
30 |
31 | create_table "gist_files", force: true do |t|
32 | t.string "name", null: false
33 | t.text "body", null: false
34 | t.integer "gist_history_id", null: false
35 | t.datetime "created_at"
36 | t.datetime "updated_at"
37 | end
38 |
39 | create_table "gist_histories", force: true do |t|
40 | t.integer "gist_id", null: false
41 | t.integer "user_id"
42 | t.datetime "created_at"
43 | t.datetime "updated_at"
44 | end
45 |
46 | create_table "gists", force: true do |t|
47 | t.string "title", null: false
48 | t.boolean "is_public", null: false
49 | t.integer "user_id"
50 | t.integer "source_gist_id"
51 | t.datetime "created_at"
52 | t.datetime "updated_at"
53 | end
54 |
55 | create_table "users", force: true do |t|
56 | t.string "nickname"
57 | t.string "omniauth_provider", null: false
58 | t.string "omniauth_uid", null: false
59 | t.datetime "created_at"
60 | t.datetime "updated_at"
61 | t.string "email"
62 | end
63 |
64 | end
65 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/ext-modelist.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/ext/modelist",["require","exports","module"],function(e,t,n){function i(e){var t=a.text,n=e.split(/[\/\\]/).pop();for(var i=0;i|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]}};r.inherits(s,i),t.VerilogHighlightRules=s})
--------------------------------------------------------------------------------
/app/views/gists/_list.html.erb:
--------------------------------------------------------------------------------
1 | <% if @gists.empty? %>
2 | No gist found
3 | <% end %>
4 | <% @gists.each do |gist| %>
5 | <% if gist.latest_history %>
6 |
7 | <% if gist.is_public %>
8 | <%= link_to gist.title, gist %>
9 | <% if gist.favorites.present? %>
10 | <%= gist.favorites.size %> <% if gist.favorites.size == 1 %>like<% else %>likes<% end %><% if gist.comments.present? %>,<% end %>
11 | <% end %>
12 | <% if gist.comments.present? %>
13 | <%= gist.comments.size %> <% if gist.comments.size == 1 %>comment<% else %>comments<% end %>
14 | <% end %>
15 |
16 |
17 | <%= time_ago_in_words(gist.latest_history.created_at) + ' ago' %>
18 | by
19 | <% if gist.user.nil? %>
20 | Anonymous
21 | <% else %>
22 | <%= link_to gist.user.nickname, user_path(gist.user) %>
23 | <%= gravatar_image(gist.user, :size => 20) %>
24 | <% end %>
25 |
26 |
27 | <% else %>
28 |
29 |
<%= link_to gist.title, gist %>
30 | <% if gist.favorites.present? %>
31 |
<%= gist.favorites.size %> <% if gist.favorites.size == 1 %>like<% else %>likes<% end %><% if gist.comments.present? %>,<% end %>
32 | <% end %>
33 | <% if gist.comments.present? %>
34 |
<%= gist.comments.size %> <% if gist.comments.size == 1 %>comment<% else %>comments<% end %>
35 | <% end %>
36 |
37 |
38 | <%= time_ago_in_words(gist.latest_history.created_at) + ' ago' %>
39 | by
40 | <% if gist.user.nil? %>
41 | Anonymous
42 | <% else %>
43 | <%= link_to gist.user.nickname, user_path(gist.user) %>
44 | <% end %>
45 |
46 |
47 |
48 | <% end %>
49 |
50 | <%= gist.latest_history.headline %>
51 | <% end %>
52 | <% end %>
53 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-c9search.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/c9search",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/c9search_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/c9search"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./c9search_highlight_rules").C9SearchHighlightRules,u=e("./matching_brace_outdent").MatchingBraceOutdent,a=e("./folding/c9search").FoldMode,f=function(){this.$tokenizer=new s((new o).getRules()),this.$outdent=new u,this.foldingRules=new a};r.inherits(f,i),function(){this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)}}.call(f.prototype),t.Mode=f}),ace.define("ace/mode/c9search_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:["c9searchresults.constant.numeric","c9searchresults.text","c9searchresults.text"],regex:"(^\\s+[0-9]+)(:\\s*)(.+)"},{token:["string","text"],regex:"(.+)(:$)"}]}};r.inherits(s,i),t.C9SearchHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/c9search",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(){};r.inherits(o,s),function(){this.foldingStartMarker=/^(\S.*\:|Searching for.*)$/,this.foldingStopMarker=/^(\s+|Found.*)$/,this.getFoldWidgetRange=function(e,t,n){var r=e.doc.getAllLines(n),s=r[n],o=/^(Found.*|Searching for.*)$/,u=/^(\S.*\:|\s*)$/,a=o.test(s)?o:u;if(this.foldingStartMarker.test(s)){for(var f=n+1,l=e.getLength();f=0;f--){s=r[f];if(a.test(s))break}return new i(f,s.length,n,0)}}}.call(o.prototype)})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-tex.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/tex",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/tex_highlight_rules","ace/mode/matching_brace_outdent"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./text_highlight_rules").TextHighlightRules,u=e("./tex_highlight_rules").TexHighlightRules,a=e("./matching_brace_outdent").MatchingBraceOutdent,f=function(e){e?this.$tokenizer=new s((new o).getRules()):this.$tokenizer=new s((new u).getRules()),this.$outdent=new a};r.inherits(f,i),function(){this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.allowAutoInsert=function(){return!1}}.call(f.prototype),t.Mode=f}),ace.define("ace/mode/tex_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=function(e){e||(e="text"),this.$rules={start:[{token:"comment",regex:"%.*$"},{token:e,regex:"\\\\[$&%#\\{\\}]"},{token:"keyword",regex:"\\\\(?:documentclass|usepackage|newcounter|setcounter|addtocounter|value|arabic|stepcounter|newenvironment|renewenvironment|ref|vref|eqref|pageref|label|cite[a-zA-Z]*|tag|begin|end|bibitem)\\b",next:"nospell"},{token:"keyword",regex:"\\\\(?:[a-zA-z0-9]+|[^a-zA-z0-9])"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])}]"},{token:e,regex:"\\s+"}],nospell:[{token:"comment",regex:"%.*$",next:"start"},{token:"nospell."+e,regex:"\\\\[$&%#\\{\\}]"},{token:"keyword",regex:"\\\\(?:documentclass|usepackage|newcounter|setcounter|addtocounter|value|arabic|stepcounter|newenvironment|renewenvironment|ref|vref|eqref|pageref|label|cite[a-zA-Z]*|tag|begin|end|bibitem)\\b"},{token:"keyword",regex:"\\\\(?:[a-zA-z0-9]+|[^a-zA-z0-9])",next:"start"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])]"},{token:"paren.keyword.operator",regex:"}",next:"start"},{token:"nospell."+e,regex:"\\s+"},{token:"nospell."+e,regex:"\\w+"}]}};r.inherits(o,s),t.TexHighlightRules=o}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-ini.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/ini",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/ini_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./ini_highlight_rules").IniHighlightRules,u=e("./folding/cstyle").FoldMode,a=function(){var e=new o;this.foldingRules=new u,this.$tokenizer=new s(e.getRules())};r.inherits(a,i),function(){this.lineCommentStart=";",this.blockComment={start:"/*",end:"*/"}}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/ini_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"punctuation.definition.comment.ini",regex:"#.*",push_:[{token:"comment.line.number-sign.ini",regex:"$",next:"pop"},{defaultToken:"comment.line.number-sign.ini"}]},{token:"punctuation.definition.comment.ini",regex:";.*",push_:[{token:"comment.line.semicolon.ini",regex:"$",next:"pop"},{defaultToken:"comment.line.semicolon.ini"}]},{token:["keyword.other.definition.ini","text","punctuation.separator.key-value.ini"],regex:"\\b([a-zA-Z0-9_.-]+)\\b(\\s*)(=)"},{token:["punctuation.definition.entity.ini","constant.section.group-title.ini","punctuation.definition.entity.ini"],regex:"^(\\[)(.*?)(\\])"},{token:"punctuation.definition.string.begin.ini",regex:"'",push:[{token:"punctuation.definition.string.end.ini",regex:"'",next:"pop"},{token:"constant.character.escape.ini",regex:"\\\\."},{defaultToken:"string.quoted.single.ini"}]},{token:"punctuation.definition.string.begin.ini",regex:'"',push:[{token:"punctuation.definition.string.end.ini",regex:'"',next:"pop"},{defaultToken:"string.quoted.double.ini"}]}]},this.normalizeRules()};s.metaData={fileTypes:["ini","conf"],keyEquivalent:"^~I",name:"Ini",scopeName:"source.ini"},r.inherits(s,i),t.IniHighlightRules=s}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i){var s=i.index;return i[1]?this.openingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s+i[0].length,1)}if(t!=="markbeginend")return;var i=r.match(this.foldingStopMarker);if(i){var s=i.index+i[0].length;return i[1]?this.closingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s,-1)}}}.call(o.prototype)})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-latex.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/latex",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/latex_highlight_rules","ace/mode/folding/latex","ace/range"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./latex_highlight_rules").LatexHighlightRules,u=e("./folding/latex").FoldMode,a=e("../range").Range,f=function(){this.$tokenizer=new s((new o).getRules()),this.foldingRules=new u};r.inherits(f,i),function(){this.lineCommentStart="%"}.call(f.prototype),t.Mode=f}),ace.define("ace/mode/latex_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"keyword",regex:"\\\\(?:[^a-zA-Z]|[a-zA-Z]+)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"string",regex:"\\$(?:(?:\\\\.)|(?:[^\\$\\\\]))*?\\$"},{token:"comment",regex:"%.*$"}]}};r.inherits(s,i),t.LatexHighlightRules=s}),ace.define("ace/mode/folding/latex",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range","ace/token_iterator"],function(e,t,n){var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=e("../../token_iterator").TokenIterator,u=t.FoldMode=function(){};r.inherits(u,i),function(){this.foldingStartMarker=/^\s*\\(begin)|(section|subsection)\b|{\s*$/,this.foldingStopMarker=/^\s*\\(end)\b|^\s*}/,this.getFoldWidgetRange=function(e,t,n){var r=e.doc.getLine(n),i=this.foldingStartMarker.exec(r);if(i)return i[1]?this.latexBlock(e,n,i[0].length-1):i[2]?this.latexSection(e,n,i[0].length-1):this.openingBracketBlock(e,"{",n,i.index);var i=this.foldingStopMarker.exec(r);if(i)return i[1]?this.latexBlock(e,n,i[0].length-1):this.closingBracketBlock(e,"}",n,i.index+i[0].length)},this.latexBlock=function(e,t,n){var r={"\\begin":1,"\\end":-1},i=new o(e,t,n),u=i.getCurrentToken();if(!u||u.type!=="keyword")return;var a=u.value,f=r[a],l=function(){var e=i.stepForward(),t=e.type=="lparen"?i.stepForward().value:"";return f===-1&&(i.stepBackward(),t&&i.stepBackward()),t},c=[l()],h=f===-1?i.getCurrentTokenColumn():e.getLine(t).length,p=t;i.step=f===-1?i.stepBackward:i.stepForward;while(u=i.step()){if(u.type!=="keyword")continue;var d=r[u.value];if(!d)continue;var v=l();if(d===f)c.unshift(v);else if(c.shift()!==v||!c.length)break}if(c.length)return;var t=i.getCurrentTokenRow();return f===-1?new s(t,e.getLine(t).length,p,h):(i.stepBackward(),new s(p,h,t,i.getCurrentTokenColumn()))},this.latexSection=function(e,t,n){var r=["\\subsection","\\section","\\begin","\\end"],i=new o(e,t,n),u=i.getCurrentToken();if(!u||u.type!="keyword")return;var a=r.indexOf(u.value),f=0,l=t;while(u=i.stepForward()){if(u.type!=="keyword")continue;var c=r.indexOf(u.value);if(c>=2){f||(l=i.getCurrentTokenRow()-1),f+=c==2?1:-1;if(f<0)break}else if(c>=a)break}f||(l=i.getCurrentTokenRow()-1);while(l>t&&!/\S/.test(e.getLine(l)))l--;return new s(t,e.getLine(t).length,l,e.getLine(l).length)}}.call(u.prototype)})
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | Gistub::Application.configure do
3 | # Settings specified here will take precedence over those in config/application.rb
4 |
5 | # Code is not reloaded between requests
6 | config.cache_classes = true
7 |
8 | # Eager load code on boot. This eager loads most of Rails and
9 | # your application in memory, allowing both thread web servers
10 | # and those relying on copy on write to perform better.
11 | # Rake tasks automatically ignore this option for performance.
12 | config.eager_load = true
13 |
14 | # Full error reports are disabled and caching is turned on
15 | config.consider_all_requests_local = false
16 | config.action_controller.perform_caching = true
17 |
18 | # Disable Rails's static asset server (Apache or nginx will already do this)
19 | config.serve_static_assets = true
20 |
21 | # Compress JavaScripts and CSS
22 | config.assets.compress = true
23 |
24 | # Don't fallback to assets pipeline if a precompiled asset is missed
25 | config.assets.compile = false
26 |
27 | # Generate digests for assets URLs
28 | config.assets.digest = true
29 |
30 | # Defaults to nil and saved in location specified by config.assets.prefix
31 | # config.assets.manifest = YOUR_PATH
32 | config.assets.version = '1.0'
33 |
34 | # Specifies the header that your server uses for sending files
35 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
36 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
37 |
38 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
39 | # config.force_ssl = true
40 |
41 | # See everything in the log (default is :info)
42 | # config.log_level = :debug
43 |
44 | # Prepend all log lines with the following tags
45 | # config.log_tags = [ :subdomain, :uuid ]
46 |
47 | # Use a different logger for distributed setups
48 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
49 |
50 | # Use a different cache store in production
51 | # config.cache_store = :mem_cache_store
52 |
53 | # Enable serving of images, stylesheets, and JavaScripts from an asset server
54 | # config.action_controller.asset_host = "http://assets.example.com"
55 |
56 | # Disable delivery errors, bad email addresses will be ignored
57 | # config.action_mailer.raise_delivery_errors = false
58 |
59 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
60 | # the I18n.default_locale when a translation can not be found)
61 | config.i18n.fallbacks = true
62 |
63 | # Send deprecation notices to registered listeners
64 | config.active_support.deprecation = :notify
65 |
66 | # Log the query plan for queries taking more than this (works
67 | # with SQLite, MySQL, and PostgreSQL)
68 | # config.active_record.auto_explain_threshold_in_seconds = 0.5
69 |
70 | # Use default logging formatter so that PID and timestamp are not suppressed.
71 | config.log_formatter = ::Logger::Formatter.new
72 |
73 | end
74 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-sh.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/sh",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/sh_highlight_rules","ace/range"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./sh_highlight_rules").ShHighlightRules,u=e("../range").Range,a=function(){var e=new o;this.$tokenizer=new s(e.getRules()),this.$keywordList=e.$keywordList};r.inherits(a,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.$tokenizer.getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[\:]\s*$/);o&&(r+=n)}return r};var e={pass:1,"return":1,raise:1,"break":1,"continue":1};this.checkOutdent=function(t,n,r){if(r!=="\r\n"&&r!=="\r"&&r!=="\n")return!1;var i=this.$tokenizer.getLineTokens(n.trim(),t).tokens;if(!i)return!1;do var s=i.pop();while(s&&(s.type=="comment"||s.type=="text"&&s.value.match(/^\s+$/)));return s?s.type=="keyword"&&e[s.value]:!1},this.autoOutdent=function(e,t,n){n+=1;var r=this.$getIndent(t.getLine(n)),i=t.getTabString();r.slice(-i.length)==i&&t.remove(new u(n,r.length-i.length,n,r.length))}}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/sh_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=t.reservedKeywords="!|{|}|case|do|done|elif|else|esac|fi|for|if|in|then|until|while|&|;|export|local|read|typeset|unset|elif|select|set",o=t.languageConstructs="[|]|alias|bg|bind|break|builtin|cd|command|compgen|complete|continue|dirs|disown|echo|enable|eval|exec|exit|fc|fg|getopts|hash|help|history|jobs|kill|let|logout|popd|printf|pushd|pwd|return|set|shift|shopt|source|suspend|test|times|trap|type|ulimit|umask|unalias|wait",u=function(){var e=this.createKeywordMapper({keyword:s,"support.function.builtin":o,"invalid.deprecated":"debugger"},"identifier"),t="(?:(?:[1-9]\\d*)|(?:0))",n="(?:\\.\\d+)",r="(?:\\d+)",i="(?:(?:"+r+"?"+n+")|(?:"+r+"\\.))",u="(?:(?:"+i+"|"+r+")"+")",a="(?:"+u+"|"+i+")",f="(?:&"+r+")",l="[a-zA-Z][a-zA-Z0-9_]*",c="(?:(?:\\$"+l+")|(?:"+l+"=))",h="(?:\\$(?:SHLVL|\\$|\\!|\\?))",p="(?:"+l+"\\s*\\(\\))";this.$rules={start:[{token:"constant",regex:/\\./},{token:["text","comment"],regex:/(^|\s)(#.*)$/},{token:"string",regex:'"',push:[{token:"constant.language.escape",regex:/\\(?:[$abeEfnrtv\\'"]|x[a-fA-F\d]{1,2}|u[a-fA-F\d]{4}([a-fA-F\d]{4})?|c.|\d{1,3})/},{token:"constant",regex:/\$\w+/},{token:"string",regex:'"',next:"pop"},{defaultToken:"string"}]},{token:"variable.language",regex:h},{token:"variable",regex:c},{token:"support.function",regex:p},{token:"support.function",regex:f},{token:"string",start:"'",end:"'"},{token:"constant.numeric",regex:a},{token:"constant.numeric",regex:t+"\\b"},{token:e,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|~|<|>|<=|=>|=|!="},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"}]},this.normalizeRules()};r.inherits(u,i),t.ShHighlightRules=u})
--------------------------------------------------------------------------------
/spec/models/gist_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe Gist do
5 |
6 | it 'is available' do
7 | gist = create(:gist)
8 | expect(gist.user).not_to be_nil
9 | end
10 |
11 | it 'returns latest_history' do
12 | history1 = create(:gist_history)
13 | history2 = create(:gist_history, :gist => history1.gist)
14 |
15 | gist = history1.gist
16 | expect(gist.latest_history).to eq(history2)
17 | end
18 |
19 | it 'returns forks' do
20 | gist = create(:gist)
21 | fork1 = create(:gist, :source_gist => gist)
22 | fork2 = create(:gist, :source_gist => gist)
23 |
24 | expect(gist.forks.size).to eq(2)
25 | expect(gist.forks.map { |f| f.id }).to eq([fork2.id, fork1.id])
26 | end
27 |
28 | it 'finds already forked gist' do
29 | user = create(:user)
30 | gist = create(:gist)
31 |
32 | expect(Gist.find_already_forked(gist.id, user.id)).to be_nil
33 |
34 | forked = create(:gist, :source_gist => gist, :user => user)
35 | expect(Gist.find_already_forked(gist.id, user.id).id).to eq(forked.id)
36 | end
37 |
38 | it 'returns my gists' do
39 | user = create(:user)
40 | create(:gist, :user => user)
41 | create(:gist, :user => user)
42 | create(:gist, :user => user)
43 |
44 | mine = Gist.find_my_recent_gists(user.id)
45 | expect(mine.size).to eq(3)
46 | end
47 |
48 | it 'finds my gist even if private' do
49 | user = create(:user)
50 |
51 | public_gist = create(:gist, :is_public => true, :user => user)
52 | found = Gist.find_my_gist_even_if_private(public_gist.id, user.id)
53 | expect(found).not_to be_nil
54 |
55 | private_gist = create(:gist, :is_public => false, :user => user)
56 | found = Gist.find_my_gist_even_if_private(private_gist.id, user.id)
57 | expect(found).not_to be_nil
58 |
59 | other_user = create(:user)
60 | not_found = Gist.find_my_gist_even_if_private(private_gist.id, other_user.id)
61 | expect(not_found).to be_nil
62 | end
63 |
64 | it 'finds commentable gist' do
65 | user = create(:user)
66 |
67 | public_gist = create(:gist)
68 | found = Gist.find_commentable_gist(public_gist.id, user.id)
69 | expect(found).not_to be_nil
70 |
71 | my_private_gist = create(:gist, :is_public => false, :user => user)
72 | found = Gist.find_commentable_gist(my_private_gist.id, user.id)
73 | expect(found).not_to be_nil
74 |
75 | other_user = create(:user)
76 | private_gist = create(:gist, :is_public => false, :user => other_user)
77 | not_found = Gist.find_commentable_gist(private_gist.id, user.id)
78 | expect(not_found).to be_nil
79 | end
80 |
81 | it 'search something' do
82 | user = create(:user)
83 | public_gist = create(:gist)
84 |
85 | query = public_gist.title
86 | current_user_id = user.id
87 | page = 1
88 | found_gists = Gist.search(query, current_user_id, page)
89 | expect(found_gists.size).to be > 0
90 | end
91 |
92 | it 'includes private gists' do
93 | result = Gist.include_private()
94 | expect(result).not_to be_nil
95 | end
96 |
97 | end
98 |
--------------------------------------------------------------------------------
/app/views/gists/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%
2 | model_name = Gist
3 | @gist ||= Gist.new
4 | @gist_history ||= GistHistory.new
5 | %>
6 | <% if is_anonymous_gist_allowed %>
7 |
8 | <%= simple_form_for @gist do |f| %>
9 |
10 | <%= render :partial => 'common/flash_error' %>
11 |
12 |
13 | <%= f.input :title, :label => false, :placeholder => 'Gist description...', :input_html => {:class => 'span8'} %>
14 |
15 |
16 |
17 |
18 | <% @gist_history.gist_files.each do |gist_file| %>
19 | <%= render :partial => 'gists/gist_files_input', :locals => { :gist_file => gist_file } %>
20 | <% end %>
21 |
22 | <%= render :partial => 'gists/gist_files_input', :locals => { :gist_file => nil } %>
23 |
24 |
25 |
26 |
29 |
30 |
31 | <% if current_user.present? and @gist.id.nil? %>
32 | <%= check_box_tag 'is_public', true, @gist.is_public %>
33 | <% else %>
34 | <%= check_box_tag 'is_public', true, @gist.user_id.nil? ? true : @gist.is_public, :disabled => true %>
35 | <% end %>
36 | Create as a public Gist
37 |
38 |
39 |
40 | <%= f.submit 'Submit', :class => 'btn btn-primary' %>
41 |
42 | <% end %>
43 |
44 |
90 |
91 | <% else %>
92 |
93 | Please <%= link_to 'sign in', signin_path %> to create a gist.
94 |
95 | <% end %>
96 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-rdoc.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/rdoc",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/rdoc_highlight_rules","ace/mode/matching_brace_outdent"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./text_highlight_rules").TextHighlightRules,u=e("./rdoc_highlight_rules").RDocHighlightRules,a=e("./matching_brace_outdent").MatchingBraceOutdent,f=function(e){this.$tokenizer=new s((new u).getRules()),this.$outdent=new a};r.inherits(f,i),function(){this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)}}.call(f.prototype),t.Mode=f}),ace.define("ace/mode/rdoc_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules","ace/mode/latex_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=e("./latex_highlight_rules"),u=function(){this.$rules={start:[{token:"comment",regex:"%.*$"},{token:"text",regex:"\\\\[$&%#\\{\\}]"},{token:"keyword",regex:"\\\\(?:name|alias|method|S3method|S4method|item|code|preformatted|kbd|pkg|var|env|option|command|author|email|url|source|cite|acronym|href|code|preformatted|link|eqn|deqn|keyword|usage|examples|dontrun|dontshow|figure|if|ifelse|Sexpr|RdOpts|inputencoding|usepackage)\\b",next:"nospell"},{token:"keyword",regex:"\\\\(?:[a-zA-z0-9]+|[^a-zA-z0-9])"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],nospell:[{token:"comment",regex:"%.*$",next:"start"},{token:"nospell.text",regex:"\\\\[$&%#\\{\\}]"},{token:"keyword",regex:"\\\\(?:name|alias|method|S3method|S4method|item|code|preformatted|kbd|pkg|var|env|option|command|author|email|url|source|cite|acronym|href|code|preformatted|link|eqn|deqn|keyword|usage|examples|dontrun|dontshow|figure|if|ifelse|Sexpr|RdOpts|inputencoding|usepackage)\\b"},{token:"keyword",regex:"\\\\(?:[a-zA-z0-9]+|[^a-zA-z0-9])",next:"start"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])]"},{token:"paren.keyword.operator",regex:"}",next:"start"},{token:"nospell.text",regex:"\\s+"},{token:"nospell.text",regex:"\\w+"}]}};r.inherits(u,s),t.RDocHighlightRules=u}),ace.define("ace/mode/latex_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"keyword",regex:"\\\\(?:[^a-zA-Z]|[a-zA-Z]+)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"string",regex:"\\$(?:(?:\\\\.)|(?:[^\\$\\\\]))*?\\$"},{token:"comment",regex:"%.*$"}]}};r.inherits(s,i),t.LatexHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i})
--------------------------------------------------------------------------------
/app/views/gists/show.html.erb:
--------------------------------------------------------------------------------
1 | <% content_for :title do %><%= @gist.title %><% end %>
2 |
3 |
4 | <%= render :partial => 'common/flash_notice' %>
5 |
6 |
7 | <% if @gist.is_public %>
8 | <%= @gist.title %>
9 |
10 | <% favorite_users(@gist).each do |user| %>
11 |
12 | <% end %>
13 |
14 | <% else %>
15 | <%= @gist.title %>
16 | <% end %>
17 |
18 |
19 |
20 | <% if current_user.present? %>
21 | <% if is_already_favorited(@gist) %>
22 |
23 | <%= link_to 'Dislike', gist_favorite_path(@gist, find_my_favorite(@gist)), :method => :delete %>
24 | <% else %>
25 |
26 | <%= link_to 'Like', gist_favorites_path(@gist), :method => :post %>
27 | <% end %>
28 |
29 | <% end %>
30 | <% if @gist.user_id != current_user.try(:id) %>
31 | <%= link_to 'Fork', gist_fork_path(@gist), :method => :post %>
32 |
33 | <% end %>
34 | <% if (@gist.user_id.nil? && is_anonymous_gist_allowed) || (@gist.user_id.present? && @gist.user_id == current_user.try(:id)) %>
35 | <%= link_to 'Edit', edit_gist_path(@gist) %>
36 |
37 | <% end %>
38 | <% if current_user.present? and @gist.user_id == current_user.try(:id) %>
39 | <%= link_to 'Delete', gist_path(@gist), :method => :delete, :data => {:confirm => 'Are you sure?'} %>
40 | <% elsif (@gist.user_id.nil? && is_anonymous_gist_allowed) %>
41 | <%= link_to 'Delete', gist_path(@gist), :method => :delete, :data => {:confirm => 'Are you sure?'} %>
42 | <% end %>
43 |
44 |
45 |
46 |
47 | <% @gist_history.gist_files.each do |file| %>
48 |
49 | <%= file.name %>
50 |
51 | <%= link_to 'Raw', show_raw_file_gist_path(@gist, file) %>
52 |
53 |
54 | <% if file.name =~ /.+((\.md)|(\.markdown))$/ %>
55 | <% if Gistub::Application.config.gistub_auto_link %>
56 |
<%= raw auto_link markdown(sanitize file.body) %>
57 | <% else %>
58 |
<%= raw markdown(sanitize file.body) %>
59 | <% end %>
60 | <% else %>
61 |
<%= file.body %>
62 | <% end %>
63 | <% end %>
64 |
65 |
66 |
Comments
67 |
68 | <%= render :partial => 'comments/list' %>
69 | <%= render :partial => 'comments/form' %>
70 |
71 |
76 |
77 |
78 |
79 | <%= render :partial => 'gists/history' %>
80 | <%= render :partial => 'gists/fork_of' %>
81 | <%= render :partial => 'gists/forks' %>
82 |
83 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require File.expand_path('../boot', __FILE__)
3 |
4 | require 'rails/all'
5 |
6 | if defined?(Bundler)
7 | # If you precompile assets before deploying to production, use this line
8 | Bundler.require(*Rails.groups(:assets => %w(development test)))
9 | # If you want your assets lazily compiled in production, use this line
10 | # Bundler.require(:default, :assets, Rails.env)
11 | end
12 |
13 | module Gistub
14 | class Application < Rails::Application
15 |
16 | config.action_view.sanitized_allowed_tags = ['script']
17 |
18 | # Settings in config/environments/* take precedence over those specified here.
19 | # Application configuration should go into files in config/initializers
20 | # -- all .rb files in that directory are automatically loaded.
21 |
22 | # Custom directories with classes and modules you want to be autoloadable.
23 | # config.autoload_paths += %W(#{config.root}/extras)
24 |
25 | # Only load the plugins named here, in the order given (default is alphabetical).
26 | # :all can be used as a placeholder for all plugins not explicitly named.
27 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
28 |
29 | # Activate observers that should always be running.
30 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
31 |
32 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
33 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
34 | # config.time_zone = 'Central Time (US & Canada)'
35 |
36 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
37 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
38 | # config.i18n.default_locale = :de
39 | I18n.enforce_available_locales = false
40 |
41 | # Configure the default encoding used in templates for Ruby 1.9.
42 | config.encoding = "utf-8"
43 |
44 | # Configure sensitive parameters which will be filtered from the log file.
45 | config.filter_parameters += [:password]
46 |
47 | # Enable escaping HTML in JSON.
48 | config.active_support.escape_html_entities_in_json = true
49 |
50 | # Use SQL instead of Active Record's schema dumper when creating the database.
51 | # This is necessary if your schema can't be completely dumped by the schema dumper,
52 | # like if you have constraints or database-specific column types
53 | # config.active_record.schema_format = :sql
54 |
55 | # Enable the asset pipeline
56 | config.assets.enabled = true
57 |
58 | # Version of your assets, change this if you want to expire all your assets
59 | config.assets.version = '1.0'
60 |
61 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
62 | # config.assets.precompile += %w( search.js )
63 | config.assets.precompile += [
64 | "header.js",
65 | "fontawesome-webfont.ttf",
66 | "fontawesome-webfont.eot",
67 | "fontawesome-webfont.svg",
68 | "fontawesome-webfont.woff"
69 | ]
70 |
71 | # Enabling rails_autolink
72 | config.gistub_auto_link = ENV['GISTUB_AUTO_LINK'] == 'true'
73 |
74 | # Enabling anonymous post
75 | config.gistub_allows_anonymous = ENV['GISTUB_ALLOWS_ANONYMOUS'] != 'false'
76 |
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-batchfile.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/batchfile",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/batchfile_highlight_rules","ace/mode/folding/cstyle"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./batchfile_highlight_rules").BatchFileHighlightRules,u=e("./folding/cstyle").FoldMode,a=function(){var e=new o;this.foldingRules=new u,this.$tokenizer=new s(e.getRules())};r.inherits(a,i),function(){this.lineCommentStart="::",this.blockComment=""}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/batchfile_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"keyword.command.dosbatch",regex:"\\b(?:append|assoc|at|attrib|break|cacls|cd|chcp|chdir|chkdsk|chkntfs|cls|cmd|color|comp|compact|convert|copy|date|del|dir|diskcomp|diskcopy|doskey|echo|endlocal|erase|fc|find|findstr|format|ftype|graftabl|help|keyb|label|md|mkdir|mode|more|move|path|pause|popd|print|prompt|pushd|rd|recover|ren|rename|replace|restore|rmdir|set|setlocal|shift|sort|start|subst|time|title|tree|type|ver|verify|vol|xcopy)\\b",caseInsensitive:!0},{token:"keyword.control.statement.dosbatch",regex:"\\b(?:goto|call|exit)\\b",caseInsensitive:!0},{token:"keyword.control.conditional.if.dosbatch",regex:"\\bif\\s+not\\s+(?:exist|defined|errorlevel|cmdextversion)\\b",caseInsensitive:!0},{token:"keyword.control.conditional.dosbatch",regex:"\\b(?:if|else)\\b",caseInsensitive:!0},{token:"keyword.control.repeat.dosbatch",regex:"\\bfor\\b",caseInsensitive:!0},{token:"keyword.operator.dosbatch",regex:"\\b(?:EQU|NEQ|LSS|LEQ|GTR|GEQ)\\b"},{token:["doc.comment","comment"],regex:"(?:^|\\b)(rem)($|\\s.*$)",caseInsensitive:!0},{token:"comment.line.colons.dosbatch",regex:"::.*$"},{include:"variable"},{token:"punctuation.definition.string.begin.shell",regex:'"',push:[{token:"punctuation.definition.string.end.shell",regex:'"',next:"pop"},{include:"variable"},{defaultToken:"string.quoted.double.dosbatch"}]},{token:"keyword.operator.pipe.dosbatch",regex:"[|]"},{token:"keyword.operator.redirect.shell",regex:"&>|\\d*>&\\d*|\\d*(?:>>|>|<)|\\d*<&|\\d*<>"}],variable:[{token:"constant.numeric",regex:"%%\\w+"},{token:["markup.list","constant.other","markup.list"],regex:"(%)(\\w+)(%?)"}]},this.normalizeRules()};s.metaData={name:"Batch File",scopeName:"source.dosbatch",fileTypes:["bat"]},r.inherits(s,i),t.BatchFileHighlightRules=s}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i){var s=i.index;return i[1]?this.openingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s+i[0].length,1)}if(t!=="markbeginend")return;var i=r.match(this.foldingStopMarker);if(i){var s=i.index+i[0].length;return i[1]?this.closingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s,-1)}}}.call(o.prototype)})
--------------------------------------------------------------------------------
/CHAGELOG:
--------------------------------------------------------------------------------
1 | Gistub CHANGELOG
2 | ---
3 |
4 | v 1.3.7
5 | - Rails 4.0.13
6 |
7 | v 1.3.6
8 | - Title improvement by @netmarkjp
9 | - Rails 4.0.5
10 |
11 | v 1.3.5
12 | - Markdown support in comments by @cb372
13 | - Disabled scripts in markdown bodies
14 | - Rails 4.0.3
15 |
16 | v 1.3.4
17 | - Removed email from profile page
18 | - Added GISTUB_APP_NAME environment variable to customize header
19 |
20 | v 1.3.3
21 | - #28 Link to latest raw file when single file (feedback by @rngtng)
22 | - Improved error pages
23 | - Fixed fork error after omniauth callbacks
24 | - Bumped some dependencies
25 |
26 | v 1.3.2
27 | - #26 Show Gravatar icons using email provided by OmniAuth by @shuhei
28 |
29 | v 1.3.1
30 | - #25 Prevented gist timestamp from overlapping with code box by @shuhei
31 |
32 | v 1.3.0
33 | - Bumped Rails from 3.2 to 4.0.2
34 | - Added GISTUB_SECRET_TOKEN, GISTUB_SECRET_KEY_BASE environment variables support
35 |
36 | v 1.2.7
37 | - Added GISTUB_AUTO_LINK, GISTUB_ALLOWS_ANONYMOUS environment variables support
38 |
39 | v 1.2.6
40 | - Fixed the issue that font-awesome icons don't appear
41 |
42 | v 1.2.5
43 | - Introduced GISTUB_OPENID_IDENTIFIER env value to specify auth server instead of Google OpenID
44 |
45 | v 1.2.4
46 | - Changed source, git repo access from git protocol to https protocol
47 |
48 | v 1.2.3
49 | - Enabled using tags in comments
50 |
51 | v 1.2.2
52 | - Fixed the issue ace editor doesn't work for several new files
53 | - Made the ids of gist file inputs and textareas unique by @shuhei
54 |
55 | v 1.2.1
56 | - Removed git submodule and added ace js files to app/assets/javascripts
57 |
58 | v 1.2.0
59 | - Add ace editor for gist file body by @shuhei
60 |
61 | v 1.1.21
62 | - Improved CSS for google-code-prettify
63 |
64 | v 1.1.20
65 | - Changed user page's gist/fav order to desc
66 | - Bumped google-code-prettify to the lastest version
67 |
68 | v 1.0.19
69 | - Added coveralls
70 |
71 | v 1.0.18
72 | - Started using Code Climate
73 |
74 | v 1.0.17
75 | - Fixed #17 Disabling anonymous posts
76 | - deafult filename if absent (refs #15)
77 |
78 | v 1.0.14
79 | - Added client side validation for gist creation thanks to @pellegrino
80 |
81 | v 1.0.13
82 | - Working on Ruby 2.0.0 and JRuby(again) thanks to @pellegrino
83 |
84 | v 1.0.12
85 | - Changed kaminari paging windows size because of broken layout for over 200 gists
86 |
87 | v 1.0.11
88 | - Enabled using rails_autolink
89 |
90 | v 1.0.10
91 | - Bumped Rails version to 3.2.13
92 | - Markdown support
93 |
94 | v 1.0.9
95 | - Bumped Rails version to 3.2.12
96 |
97 | v 1.0.8
98 | - Show the nickname of user who likes the gist by @tototoshi
99 |
100 | v 1.0.7
101 | - Fixed issue #10 Icons are not displayed after updating twitter-bootstrap-rails
102 |
103 | v 1.0.6
104 | - Fixed search paging
105 |
106 | v 1.0.5
107 | - Fixed issue #3 Searching gists
108 |
109 | v 1.0.4
110 | - Added likes, comments for list pages
111 | - Fixed issue #9 Fails to display gist if commented users are dropped
112 |
113 | v 1.0.3
114 | - Fixed appearance
115 | - Fixed an error at user page
116 |
117 | v 1.0.2
118 | - Fixed issue #8 Use not /tmp dir but tmp dir under rails root
119 |
120 | v 1.0.1
121 | - Fixed issue #4 font-resizing
122 |
123 | v 1.0.0
124 | - Initial versioning
125 | - Fixed root path issue #7 thanks to @fujiwara
126 |
127 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-snippets.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/snippets",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/folding/coffee"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./text_highlight_rules").TextHighlightRules,u=function(){var e="SELECTION|CURRENT_WORD|SELECTED_TEXT|CURRENT_LINE|LINE_INDEX|LINE_NUMBER|SOFT_TABS|TAB_SIZE|FILENAME|FILEPATH|FULLNAME";this.$rules={start:[{token:"constant.language.escape",regex:/\\[\$}`\\]/},{token:"keyword",regex:"\\$(?:TM_)?(?:"+e+")\\b"},{token:"variable",regex:"\\$\\w+"},{onMatch:function(e,t,n){return n[1]?n[1]++:n.unshift(t,1),this.tokenName},tokenName:"markup.list",regex:"\\${",next:"varDecl"},{onMatch:function(e,t,n){return n[1]?(n[1]--,n[1]||n.splice(0,2),this.tokenName):"text"},tokenName:"markup.list",regex:"}"},{token:"doc.comment",regex:/^\${2}-{5,}$/}],varDecl:[{regex:/\d+\b/,token:"constant.numeric"},{token:"keyword",regex:"(?:TM_)?(?:"+e+")\\b"},{token:"variable",regex:"\\w+"},{regex:/:/,token:"punctuation.operator",next:"start"},{regex:/\//,token:"string.regex",next:"regexp"},{regex:"",next:"start"}],regexp:[{regex:/\\./,token:"escape"},{regex:/\[/,token:"regex.start",next:"charClass"},{regex:"/",token:"string.regex",next:"format"},{token:"string.regex",regex:"."}],charClass:[{regex:"\\.",token:"escape"},{regex:"\\]",token:"regex.end",next:"regexp"},{token:"string.regex",regex:"."}],format:[{regex:/\\[ulULE]/,token:"keyword"},{regex:/\$\d+/,token:"variable"},{regex:"/[gim]*:?",token:"string.regex",next:"start"},{token:"string",regex:"."}]}};r.inherits(u,o),t.SnippetHighlightRules=u;var a=function(){this.$rules={start:[{token:"text",regex:"^\\t",next:"sn-start"},{token:"invalid",regex:/^ \s*/},{token:"comment",regex:/^#.*/},{token:"constant.language.escape",regex:"^regex ",next:"regex"},{token:"constant.language.escape",regex:"^(trigger|endTrigger|name|snippet|guard|endGuard|tabTrigger|key)\\b"}],regex:[{token:"text",regex:"\\."},{token:"keyword",regex:"/"},{token:"empty",regex:"$",next:"start"}]},this.embedRules(u,"sn-",[{token:"text",regex:"^\\t",next:"sn-start"},{onMatch:function(e,t,n){return n.splice(n.length),this.tokenName},tokenName:"text",regex:"^(?! )",next:"start"}])};r.inherits(a,o),t.SnippetGroupHighlightRules=a;var f=e("./folding/coffee").FoldMode,l=function(){var e=new a;this.foldingRules=new f,this.$tokenizer=new s(e.getRules())};r.inherits(l,i),function(){this.$indentWithTabs=!0}.call(l.prototype),t.Mode=l}),ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&ul){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u true
5 |
6 | belongs_to :user
7 | belongs_to :source_gist, class_name: Gist
8 |
9 | has_many :gist_histories, -> { order(:updated_at => :desc) }
10 | has_many :comments, -> { order(:updated_at => :asc) }
11 | has_many :favorites, -> { order(:updated_at => :asc) }
12 |
13 | default_scope {
14 | order(:id)
15 | .where(is_public: true)
16 | .includes(:user)
17 | .includes(:gist_histories)
18 | .includes(:comments)
19 | .includes(:favorites)
20 | }
21 |
22 | scope :recent, lambda { order(:created_at).reverse_order }
23 |
24 | def self.search(query, current_user_id, page)
25 | keywords = query.split("\s")
26 | like_parts = keywords.map { |keyword| "%#{keyword}%" }
27 |
28 | gist_ids_from_gists = find_gist_ids_from_gists(like_parts, current_user_id)
29 | gist_ids_from_files = find_gist_ids_from_gist_files(like_parts, current_user_id)
30 | ids = (gist_ids_from_gists + gist_ids_from_files).uniq
31 |
32 | find_visible_gists_in(ids, current_user_id, page)
33 | end
34 |
35 | def latest_history
36 | gist_histories.first
37 | end
38 |
39 | def forks
40 | Gist.recent.where(:source_gist_id => id)
41 | end
42 |
43 | def self.include_private
44 | unscoped
45 | .includes(:user)
46 | .includes(:gist_histories)
47 | .includes(:comments)
48 | .includes(:favorites)
49 | end
50 |
51 | def self.find_already_forked(source_gist_id, user_id)
52 | Gist.where(source_gist_id: source_gist_id, user_id: user_id).first
53 | end
54 |
55 | def self.find_my_recent_gists(user_id)
56 | Gist.include_private.where(user_id: user_id).recent
57 | end
58 |
59 | def self.find_my_gist_even_if_private(id, user_id)
60 | if user_id.nil?
61 | where(id: id).first
62 | else
63 | my_gist = reduce(where(id: id, user_id: user_id))
64 | public_gist = reduce(where(id: id, is_public: true))
65 | include_private.where(my_gist.or(public_gist)).first
66 | end
67 | end
68 |
69 | def self.find_commentable_gist(id, user_id)
70 | public_gist = where(id: id).first
71 | if public_gist.present?
72 | public_gist
73 | else
74 | find_my_gist_even_if_private(id, user_id)
75 | end
76 | end
77 |
78 | private
79 |
80 | def self.reduce(where)
81 | where.where_values.reduce(:and)
82 | end
83 |
84 | def self.find_gist_ids_from_gists(like_parts, current_user_id)
85 | g = Gist.arel_table
86 |
87 | query = g[:is_public].eq(true).or(g[:user_id].eq(current_user_id))
88 |
89 | query = query.and(g[:title].matches(like_parts.first))
90 | query = like_parts.drop(1).reduce(query) { |q, like_part|
91 | q.and(g[:title].matches(like_part))
92 | }
93 |
94 | Gist.include_private.where(query).pluck(:id)
95 | end
96 |
97 | def self.find_gist_ids_from_gist_files(like_parts, current_user_id)
98 | gf = GistFile.arel_table
99 |
100 | query = gf[:name].matches(like_parts.first).or(gf[:body].matches(like_parts.first))
101 | query = like_parts.drop(1).reduce(query) { |q, like_part|
102 | q.and(gf[:name].matches(like_part).or(gf[:body].matches(like_part)))
103 | }
104 |
105 | GistFile.where(query).joins(:gist_history).pluck('gist_histories.gist_id').uniq
106 | end
107 |
108 | def self.find_visible_gists_in(ids, current_user_id, page)
109 | g = Gist.arel_table
110 | Gist.include_private
111 | .where(g[:is_public].eq(true).or(g[:user_id].eq(current_user_id)))
112 | .where(id: ids)
113 | .order(:created_at => :desc)
114 | .page(page).per(10)
115 | end
116 |
117 | end
118 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-yaml.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/yaml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/yaml_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/coffee"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./yaml_highlight_rules").YamlHighlightRules,u=e("./matching_brace_outdent").MatchingBraceOutdent,a=e("./folding/coffee").FoldMode,f=function(){this.$tokenizer=new s((new o).getRules()),this.$outdent=new u,this.foldingRules=new a};r.inherits(f,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)}}.call(f.prototype),t.Mode=f}),ace.define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w.*?)(\:(?:\s+|$))/},{token:["meta.tag","keyword"],regex:/(\w+?)(\s*\:(?:\s+|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"[\\|>]\\w*",next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/[+\-]?[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?\b/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"invalid.illegal",regex:"\\/\\/.*$"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"}],qqstring:[{token:"string",regex:"(?=(?:(?:\\\\.)|(?:[^:]))*?:)",next:"start"},{token:"string",regex:".+"}]}};r.inherits(s,i),t.YamlHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&ul){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u >|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"},{token:"text",regex:"\\s+"}],qqstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:'"{3}',next:"start"},{defaultToken:"string"}],qstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:"'{3}",next:"start"},{defaultToken:"string"}],qqstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"start"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"start"},{defaultToken:"string"}]}};r.inherits(s,i),t.PythonHighlightRules=s}),ace.define("ace/mode/folding/pythonic",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode"],function(e,t,n){var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=t.FoldMode=function(e){this.foldingStartMarker=new RegExp("([\\[{])(?:\\s*)$|("+e+")(?:\\s*)(?:#.*)?$")};r.inherits(s,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i)return i[1]?this.openingBracketBlock(e,i[1],n,i.index):i[2]?this.indentationBlock(e,n,i.index+i[2].length):this.indentationBlock(e,n)}}.call(s.prototype)})
--------------------------------------------------------------------------------
/spec/helpers/application_helper_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 | require 'spec_helper'
3 |
4 | describe ApplicationHelper do
5 |
6 | let(:user) { create(:user) }
7 | let(:gist) { create(:gist, :user => user) }
8 |
9 | describe "anonymous gist" do
10 | # from ApplicationController
11 | def current_user
12 | nil
13 | end
14 | def anonymous_allowed
15 | false
16 | end
17 |
18 | it 'is_anonymous_gist_allowed' do
19 | expect(is_anonymous_gist_allowed).to be_false
20 | end
21 |
22 | end
23 |
24 | describe "-- if current_user is nil --" do
25 | # from ApplicationController
26 | def current_user
27 | nil
28 | end
29 | def anonymous_allowed
30 | true
31 | end
32 |
33 | it 'is_anonymous_gist_allowed' do
34 | expect(is_anonymous_gist_allowed).to be_true
35 | end
36 |
37 | it 'get nil from #my_gists' do
38 | expect(my_gists).to be_nil
39 | end
40 | it 'get nil from #my_favorite_gists' do
41 | expect(my_gists).to be_nil
42 | end
43 | it 'get nil from #find_my_favorite' do
44 | expect(find_my_favorite(gist)).to be_nil
45 | end
46 | it 'get false from #is_already_favorited' do
47 | expect(is_already_favorited(gist)).to eq(false)
48 | end
49 |
50 | it 'get something from #recent_gists' do
51 | expect(recent_gists).not_to be_nil
52 | end
53 | it 'get something from #favorite_users' do
54 | expect(favorite_users(gist)).not_to be_nil
55 | end
56 | end
57 |
58 | describe "-- if current_user is NOT null --" do
59 | def current_user
60 | user
61 | end
62 |
63 | it 'get something from #my_gists' do
64 | expect(my_gists).not_to be_nil
65 | end
66 | it 'get something from #my_favorite_gists' do
67 | expect(my_favorite_gists).not_to be_nil
68 | end
69 | it 'get nil from #find_my_favorite' do
70 | fav = Favorite.create(:gist_id => gist.id, :user_id => user.id)
71 | expect(find_my_favorite(gist)).to eq(fav)
72 | end
73 | it 'get false from #is_already_favorited' do
74 | Favorite.create(:gist_id => gist.id, :user_id => user.id)
75 | expect(is_already_favorited(gist)).to eq(true)
76 | end
77 | end
78 |
79 | describe 'markdown' do
80 | it "doesn't interpret the body when it fails" do
81 | if defined?(Kramdown)
82 | # JRuby
83 | allow_any_instance_of(Kramdown::Document).to receive(:to_html) { raise Kramdown::Error }
84 | else
85 | # MRI
86 | allow_any_instance_of(Qiita::Markdown::Processor).to receive(:call) { raise "Some error" }
87 | end
88 |
89 | md_body = "Simulating error thrown by Kramdown"
90 |
91 | expect(markdown(md_body)).to eq(md_body)
92 | end
93 |
94 | it 'works' do
95 | md_body = <foo
113 |
114 | Something!
115 |
116 | Bar
117 |
118 |
119 | a
120 | b
121 | c
122 |
123 | EOF
124 | else
125 | # MRI
126 | <
129 | foo
130 |
131 | Something!
132 |
133 |
134 | Bar
135 |
136 |
137 | a
138 | b
139 | c
140 |
141 | EOF
142 | end
143 |
144 | expect(result).to eq(expected)
145 | end
146 | end
147 |
148 | describe 'gravatar_image' do
149 | it 'returns nil for user without email' do
150 | u = create(:user, :email => nil)
151 | result = gravatar_image(u)
152 | expect(result).to be_nil
153 | end
154 |
155 | it 'works' do
156 | u = create(:user, :nickname => 'Foo', :email => 'foo@bar.com')
157 | options = {
158 | :size => 25,
159 | :d => 'http://test.com/default.jpg'
160 | }
161 | result = gravatar_image(u, options)
162 | expected = ' '
163 | expect(result).to eq(expected)
164 | end
165 | end
166 | end
167 |
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-tcl.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/tcl",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/folding/cstyle","ace/mode/tcl_highlight_rules","ace/mode/matching_brace_outdent","ace/range"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./folding/cstyle").FoldMode,u=e("./tcl_highlight_rules").TclHighlightRules,a=e("./matching_brace_outdent").MatchingBraceOutdent,f=e("../range").Range,l=function(){this.$tokenizer=new s((new u).getRules()),this.$outdent=new a,this.foldingRules=new o};r.inherits(l,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.$tokenizer.getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[]\s*$/);o&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)}}.call(l.prototype),t.Mode=l}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i){var s=i.index;return i[1]?this.openingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s+i[0].length,1)}if(t!=="markbeginend")return;var i=r.match(this.foldingStopMarker);if(i){var s=i.index+i[0].length;return i[1]?this.closingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s,-1)}}}.call(o.prototype)}),ace.define("ace/mode/tcl_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*\\\\$",next:"commentfollow"},{token:"comment",regex:"#.*$"},{token:"support.function",regex:"[\\\\]$",next:"splitlineStart"},{token:"text",regex:'[\\\\](?:["]|[{]|[}]|[[]|[]]|[$]|[])'},{token:"text",regex:"^|[^{][;][^}]|[/\r/]",next:"commandItem"},{token:"string",regex:'[ ]*["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'[ ]*["]',next:"qqstring"},{token:"variable.instance",regex:"[$]",next:"variable"},{token:"support.function",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|{\\*}|;|::"},{token:"identifier",regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"paren.lparen",regex:"[[{]",next:"commandItem"},{token:"paren.lparen",regex:"[(]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],commandItem:[{token:"comment",regex:"#.*\\\\$",next:"commentfollow"},{token:"comment",regex:"#.*$",next:"start"},{token:"string",regex:'[ ]*["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"variable.instance",regex:"[$]",next:"variable"},{token:"support.function",regex:"(?:[:][:])[a-zA-Z0-9_/]+(?:[:][:])",next:"commandItem"},{token:"support.function",regex:"[a-zA-Z0-9_/]+(?:[:][:])",next:"commandItem"},{token:"support.function",regex:"(?:[:][:])",next:"commandItem"},{token:"paren.rparen",regex:"[\\])}]"},{token:"support.function",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|{\\*}|;|::"},{token:"keyword",regex:"[a-zA-Z0-9_/]+",next:"start"}],commentfollow:[{token:"comment",regex:".*\\\\$",next:"commentfollow"},{token:"comment",regex:".+",next:"start"}],splitlineStart:[{token:"text",regex:"^.",next:"start"}],variable:[{token:"variable.instance",regex:"[a-zA-Z_\\d]+(?:[(][a-zA-Z_\\d]+[)])?",next:"start"},{token:"variable.instance",regex:"{?[a-zA-Z_\\d]+}?",next:"start"}],qqstring:[{token:"string",regex:'(?:[^\\\\]|\\\\.)*?["]',next:"start"},{token:"string",regex:".+"}]}};r.inherits(s,i),t.TclHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-makefile.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/makefile",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/makefile_highlight_rules","ace/mode/folding/coffee"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./makefile_highlight_rules").MakefileHighlightRules,u=e("./folding/coffee").FoldMode,a=function(){var e=new o;this.foldingRules=new u,this.$tokenizer=new s(e.getRules()),this.$keywordList=e.$keywordList};r.inherits(a,i),function(){this.lineCommentStart="#",this.$indentWithTabs=!0}.call(a.prototype),t.Mode=a}),ace.define("ace/mode/makefile_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules","ace/mode/sh_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=e("./sh_highlight_rules"),o=function(){var e=this.createKeywordMapper({keyword:s.reservedKeywords,"support.function.builtin":s.languageConstructs,"invalid.deprecated":"debugger"},"string");this.$rules={start:[{token:"string.interpolated.backtick.makefile",regex:"`",next:"shell-start"},{token:"punctuation.definition.comment.makefile",regex:/#(?=.)/,next:"comment"},{token:["keyword.control.makefile"],regex:"^(?:\\s*\\b)(\\-??include|ifeq|ifneq|ifdef|ifndef|else|endif|vpath|export|unexport|define|endef|override)(?:\\b)"},{token:["entity.name.function.makefile","text"],regex:"^([^\\t ]+(?:\\s[^\\t ]+)*:)(\\s*.*)"}],comment:[{token:"punctuation.definition.comment.makefile",regex:/.+\\/},{token:"punctuation.definition.comment.makefile",regex:".+",next:"start"}],"shell-start":[{token:e,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"string",regex:"\\w+"},{token:"string.interpolated.backtick.makefile",regex:"`",next:"start"}]}};r.inherits(o,i),t.MakefileHighlightRules=o}),ace.define("ace/mode/sh_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=t.reservedKeywords="!|{|}|case|do|done|elif|else|esac|fi|for|if|in|then|until|while|&|;|export|local|read|typeset|unset|elif|select|set",o=t.languageConstructs="[|]|alias|bg|bind|break|builtin|cd|command|compgen|complete|continue|dirs|disown|echo|enable|eval|exec|exit|fc|fg|getopts|hash|help|history|jobs|kill|let|logout|popd|printf|pushd|pwd|return|set|shift|shopt|source|suspend|test|times|trap|type|ulimit|umask|unalias|wait",u=function(){var e=this.createKeywordMapper({keyword:s,"support.function.builtin":o,"invalid.deprecated":"debugger"},"identifier"),t="(?:(?:[1-9]\\d*)|(?:0))",n="(?:\\.\\d+)",r="(?:\\d+)",i="(?:(?:"+r+"?"+n+")|(?:"+r+"\\.))",u="(?:(?:"+i+"|"+r+")"+")",a="(?:"+u+"|"+i+")",f="(?:&"+r+")",l="[a-zA-Z][a-zA-Z0-9_]*",c="(?:(?:\\$"+l+")|(?:"+l+"=))",h="(?:\\$(?:SHLVL|\\$|\\!|\\?))",p="(?:"+l+"\\s*\\(\\))";this.$rules={start:[{token:"constant",regex:/\\./},{token:["text","comment"],regex:/(^|\s)(#.*)$/},{token:"string",regex:'"',push:[{token:"constant.language.escape",regex:/\\(?:[$abeEfnrtv\\'"]|x[a-fA-F\d]{1,2}|u[a-fA-F\d]{4}([a-fA-F\d]{4})?|c.|\d{1,3})/},{token:"constant",regex:/\$\w+/},{token:"string",regex:'"',next:"pop"},{defaultToken:"string"}]},{token:"variable.language",regex:h},{token:"variable",regex:c},{token:"support.function",regex:p},{token:"support.function",regex:f},{token:"string",start:"'",end:"'"},{token:"constant.numeric",regex:a},{token:"constant.numeric",regex:t+"\\b"},{token:e,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|~|<|>|<=|=>|=|!="},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"}]},this.normalizeRules()};r.inherits(u,i),t.ShHighlightRules=u}),ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++nl){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u>|\\||\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"},{token:"text",regex:"\\s+"}]}};r.inherits(s,i),t.MushCodeRules=s})
--------------------------------------------------------------------------------
/app/assets/javascripts/ace/mode-r.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/mode/r",["require","exports","module","ace/range","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/r_highlight_rules","ace/mode/matching_brace_outdent","ace/unicode"],function(e,t,n){var r=e("../range").Range,i=e("../lib/oop"),s=e("./text").Mode,o=e("../tokenizer").Tokenizer,u=e("./text_highlight_rules").TextHighlightRules,a=e("./r_highlight_rules").RHighlightRules,f=e("./matching_brace_outdent").MatchingBraceOutdent,l=e("../unicode"),c=function(){this.$tokenizer=new o((new a).getRules()),this.$outdent=new f};i.inherits(c,s),function(){this.lineCommentStart="#"}.call(c.prototype),t.Mode=c}),ace.define("ace/mode/r_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules","ace/mode/tex_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=e("./tex_highlight_rules").TexHighlightRules,u=function(){var e=i.arrayToMap("function|if|in|break|next|repeat|else|for|return|switch|while|try|tryCatch|stop|warning|require|library|attach|detach|source|setMethod|setGeneric|setGroupGeneric|setClass".split("|")),t=i.arrayToMap("NULL|NA|TRUE|FALSE|T|F|Inf|NaN|NA_integer_|NA_real_|NA_character_|NA_complex_".split("|"));this.$rules={start:[{token:"comment.sectionhead",regex:"#+(?!').*(?:----|====|####)\\s*$"},{token:"comment",regex:"#+'",next:"rd-start"},{token:"comment",regex:"#.*$"},{token:"string",regex:'["]',next:"qqstring"},{token:"string",regex:"[']",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+[Li]?\\b"},{token:"constant.numeric",regex:"\\d+L\\b"},{token:"constant.numeric",regex:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b"},{token:"constant.numeric",regex:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b"},{token:"constant.language.boolean",regex:"(?:TRUE|FALSE|T|F)\\b"},{token:"identifier",regex:"`.*?`"},{onMatch:function(n){return e[n]?"keyword":t[n]?"constant.language":n=="..."||n.match(/^\.\.\d+$/)?"variable.language":"identifier"},regex:"[a-zA-Z.][a-zA-Z0-9._]*\\b"},{token:"keyword.operator",regex:"%%|>=|<=|==|!=|\\->|<\\-|\\|\\||&&|=|\\+|\\-|\\*|/|\\^|>|<|!|&|\\||~|\\$|:"},{token:"keyword.operator",regex:"%.*?%"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]};var n=(new o("comment")).getRules();for(var r=0;r|\\b(?:e(?:lse|xport)|d(?:o|efault)|t(?:ry|hen)|finally|import(?:\\s*all)?|const|var|let|new|catch(?:\\s*"+r+")?))\\s*$"),i.getNextLineIndent=function(e,t,r){var i,s;return i=this.$getIndent(t),s=this.$tokenizer.getLineTokens(t,e).tokens,(!s.length||s[s.length-1].type!=="comment")&&e==="start"&&n.test(t)&&(i+=r),i},i.toggleCommentLines=function(t,n,r,i){var s,o,u,a,f,l;s=/^(\s*)#/,o=new(e("../range").Range)(0,0,0,0);for(u=r;u<=i;++u)a=u,(f=s.test(l=n.getLine(a)))?l=l.replace(s,"$1"):l=l.replace(/^\s*/,"$"),o.end.row=o.start.row=a,o.end.column=l.length+1,n.replace(o,l);return 1-f*2},i.checkOutdent=function(e,t,n){var r;return(r=this.$outdent)!=null?r.checkOutdent(t,n):void 8},i.autoOutdent=function(e,t,n){var r;return(r=this.$outdent)!=null?r.autoOutdent(t,n):void 8},o}(e("../mode/text").Mode),s="(?![$\\w]|-[A-Za-z]|\\s*:(?![:=]))",o={token:"string",regex:".+"},i.Rules={start:[{token:"keyword",regex:"(?:t(?:h(?:is|row|en)|ry|ypeof!?)|c(?:on(?:tinue|st)|a(?:se|tch)|lass)|i(?:n(?:stanceof)?|mp(?:ort(?:\\s+all)?|lements)|[fs])|d(?:e(?:fault|lete|bugger)|o)|f(?:or(?:\\s+own)?|inally|unction)|s(?:uper|witch)|e(?:lse|x(?:tends|port)|val)|a(?:nd|rguments)|n(?:ew|ot)|un(?:less|til)|w(?:hile|ith)|o[fr]|return|break|let|var|loop)"+s},{token:"constant.language",regex:"(?:true|false|yes|no|on|off|null|void|undefined)"+s},{token:"invalid.illegal",regex:"(?:p(?:ackage|r(?:ivate|otected)|ublic)|i(?:mplements|nterface)|enum|static|yield)"+s},{token:"language.support.class",regex:"(?:R(?:e(?:gExp|ferenceError)|angeError)|S(?:tring|yntaxError)|E(?:rror|valError)|Array|Boolean|Date|Function|Number|Object|TypeError|URIError)"+s},{token:"language.support.function",regex:"(?:is(?:NaN|Finite)|parse(?:Int|Float)|Math|JSON|(?:en|de)codeURI(?:Component)?)"+s},{token:"variable.language",regex:"(?:t(?:hat|il|o)|f(?:rom|allthrough)|it|by|e)"+s},{token:"identifier",regex:r+"\\s*:(?![:=])"},{token:"variable",regex:r},{token:"keyword.operator",regex:"(?:\\.{3}|\\s+\\?)"},{token:"keyword.variable",regex:"(?:@+|::|\\.\\.)",next:"key"},{token:"keyword.operator",regex:"\\.\\s*",next:"key"},{token:"string",regex:"\\\\\\S[^\\s,;)}\\]]*"},{token:"string.doc",regex:"'''",next:"qdoc"},{token:"string.doc",regex:'"""',next:"qqdoc"},{token:"string",regex:"'",next:"qstring"},{token:"string",regex:'"',next:"qqstring"},{token:"string",regex:"`",next:"js"},{token:"string",regex:"<\\[",next:"words"},{token:"string.regex",regex:"//",next:"heregex"},{token:"comment.doc",regex:"/\\*",next:"comment"},{token:"comment",regex:"#.*"},{token:"string.regex",regex:"\\/(?:[^[\\/\\n\\\\]*(?:(?:\\\\.|\\[[^\\]\\n\\\\]*(?:\\\\.[^\\]\\n\\\\]*)*\\])[^[\\/\\n\\\\]*)*)\\/[gimy$]{0,4}",next:"key"},{token:"constant.numeric",regex:"(?:0x[\\da-fA-F][\\da-fA-F_]*|(?:[2-9]|[12]\\d|3[0-6])r[\\da-zA-Z][\\da-zA-Z_]*|(?:\\d[\\d_]*(?:\\.\\d[\\d_]*)?|\\.\\d[\\d_]*)(?:e[+-]?\\d[\\d_]*)?[\\w$]*)"},{token:"lparen",regex:"[({[]"},{token:"rparen",regex:"[)}\\]]",next:"key"},{token:"keyword.operator",regex:"\\S+"},{token:"text",regex:"\\s+"}],heregex:[{token:"string.regex",regex:".*?//[gimy$?]{0,4}",next:"start"},{token:"string.regex",regex:"\\s*#{"},{token:"comment.regex",regex:"\\s+(?:#.*)?"},{token:"string.regex",regex:"\\S+"}],key:[{token:"keyword.operator",regex:"[.?@!]+"},{token:"identifier",regex:r,next:"start"},{token:"text",regex:".",next:"start"}],comment:[{token:"comment.doc",regex:".*?\\*/",next:"start"},{token:"comment.doc",regex:".+"}],qdoc:[{token:"string",regex:".*?'''",next:"key"},o],qqdoc:[{token:"string",regex:'.*?"""',next:"key"},o],qstring:[{token:"string",regex:"[^\\\\']*(?:\\\\.[^\\\\']*)*'",next:"key"},o],qqstring:[{token:"string",regex:'[^\\\\"]*(?:\\\\.[^\\\\"]*)*"',next:"key"},o],js:[{token:"string",regex:"[^\\\\`]*(?:\\\\.[^\\\\`]*)*`",next:"key"},o],words:[{token:"string",regex:".*?\\]>",next:"key"},o]}}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i})
--------------------------------------------------------------------------------