├── .gitignore ├── .powrc ├── .rspec ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── app ├── assets │ ├── images │ │ ├── favicon.ico │ │ └── rails.png │ ├── javascripts │ │ ├── application.js │ │ ├── bootstrap.js.coffee │ │ └── jquery.timeago.js │ └── stylesheets │ │ ├── application.css │ │ ├── bootstrap_and_overrides.css.less │ │ ├── pygments.css │ │ └── syntax.css ├── controllers │ ├── application_controller.rb │ ├── blobs_controller.rb │ └── gists_controller.rb ├── helpers │ ├── application_helper.rb │ └── gists_helper.rb ├── mailers │ └── .gitkeep ├── models │ ├── .gitkeep │ ├── gist.rb │ ├── gist_blob.rb │ ├── gist_repo.rb │ ├── highlighted_source.rb │ ├── repo_log.rb │ ├── repo_tree.rb │ ├── repo_writer.rb │ └── save_gist.rb ├── presenters │ └── gist_blob_presenter.rb └── views │ ├── gists │ ├── _form.html.erb │ ├── _gist_blob.html.erb │ ├── _revisions.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ └── layouts │ ├── _sidebar.html.erb │ └── application.html.erb ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── backtrace_silencers.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── secret_token.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml └── routes.rb ├── db ├── migrate │ ├── 20121012110908_create_gists_table.rb │ └── 20121101194121_add_gist_description.rb └── seeds.rb ├── doc └── README_FOR_APP ├── lib ├── assets │ └── .gitkeep └── tasks │ └── .gitkeep ├── log └── .gitkeep ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico └── robots.txt ├── repos └── .gitkeep ├── script └── rails ├── spec ├── fixtures │ └── test_repo.git │ │ ├── COMMIT_EDITMSG │ │ ├── HEAD │ │ ├── config │ │ ├── hooks │ │ └── pre-commit │ │ ├── index │ │ ├── logs │ │ ├── HEAD │ │ └── refs │ │ │ └── heads │ │ │ └── master │ │ ├── objects │ │ ├── 82 │ │ │ └── d1aa8da25e22d908c44bde9d122fea688ba3a0 │ │ ├── 86 │ │ │ └── 709acd61353d08a1d3ff15f7fc8b554c34a378 │ │ ├── 2d │ │ │ └── 357899dd88b475ca46e627e99883f723da8c8c │ │ ├── 8a │ │ │ └── a2aed323077103c91369c23e886c6bae2d987d │ │ ├── bb │ │ │ └── ae578e76aad577054201545aeacde508b49596 │ │ └── c1 │ │ │ └── 8d3d5324e98d7bb2e5e5934e4ef6c3046a7ca1 │ │ └── refs │ │ └── heads │ │ └── master ├── integration_spec_helper.rb ├── models │ ├── gist_blob_spec.rb │ ├── gist_repo_spec.rb │ ├── gist_spec.rb │ ├── highlighed_source_spec.rb │ ├── repo_log_spec.rb │ ├── repo_tree_spec.rb │ ├── repo_writer_spec.rb │ └── save_gist_spec.rb ├── requests │ └── gists_spec.rb └── spec_helper.rb └── vendor ├── assets ├── javascripts │ └── .gitkeep └── stylesheets │ └── .gitkeep └── plugins └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | # Ignore bundler config 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | 13 | # Ignore all logfiles and tempfiles. 14 | /log/*.log 15 | /tmp 16 | 17 | repos_* 18 | -------------------------------------------------------------------------------- /.powrc: -------------------------------------------------------------------------------- 1 | if [ -f "$rvm_path/scripts/rvm" ] && [ -f ".rvmrc" ]; then 2 | source "$rvm_path/scripts/rvm" 3 | source ".rvmrc" 4 | fi 5 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.0.0-p247 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '~> 3.2.12' 4 | 5 | # Bundle edge Rails instead: 6 | # gem 'rails', :git => 'git://github.com/rails/rails.git' 7 | 8 | gem 'sqlite3' 9 | 10 | gem 'rugged', '0.17.0.b6' 11 | gem 'pygments.rb' 12 | 13 | 14 | 15 | # Gems used only for assets and not required 16 | # in production environments by default. 17 | group :assets do 18 | gem 'sass-rails', '~> 3.2.3' 19 | gem 'coffee-rails', '~> 3.2.1' 20 | 21 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes 22 | gem 'therubyracer' 23 | 24 | gem 'uglifier', '>= 1.0.3' 25 | 26 | gem 'font-awesome-rails' 27 | end 28 | 29 | gem 'jquery-rails' 30 | gem 'jquery-ui-rails' 31 | gem 'twitter-bootstrap-rails' 32 | 33 | gem 'kaminari' 34 | gem 'bootstrap_kaminari', :git => 'https://github.com/gmarik/bootstrap_kaminari' 35 | 36 | group :test, :development do 37 | gem 'rspec-rails' 38 | 39 | gem "capybara" 40 | gem "poltergeist" 41 | end 42 | 43 | 44 | # To use ActiveModel has_secure_password 45 | # gem 'bcrypt-ruby', '~> 3.0.0' 46 | 47 | # To use Jbuilder templates for JSON 48 | # gem 'jbuilder' 49 | 50 | # Use unicorn as the app server 51 | # gem 'unicorn' 52 | 53 | # Deploy with Capistrano 54 | # gem 'capistrano' 55 | 56 | # To use debugger 57 | # gem 'debugger' 58 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/gmarik/bootstrap_kaminari 3 | revision: 961d174918377d89571e3df69c257d9168ea2883 4 | specs: 5 | bootstrap_kaminari (0.0.2) 6 | kaminari (>= 0.12.4) 7 | rails (>= 3.1.0) 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | actionmailer (3.2.12) 13 | actionpack (= 3.2.12) 14 | mail (~> 2.4.4) 15 | actionpack (3.2.12) 16 | activemodel (= 3.2.12) 17 | activesupport (= 3.2.12) 18 | builder (~> 3.0.0) 19 | erubis (~> 2.7.0) 20 | journey (~> 1.0.4) 21 | rack (~> 1.4.5) 22 | rack-cache (~> 1.2) 23 | rack-test (~> 0.6.1) 24 | sprockets (~> 2.2.1) 25 | activemodel (3.2.12) 26 | activesupport (= 3.2.12) 27 | builder (~> 3.0.0) 28 | activerecord (3.2.12) 29 | activemodel (= 3.2.12) 30 | activesupport (= 3.2.12) 31 | arel (~> 3.0.2) 32 | tzinfo (~> 0.3.29) 33 | activeresource (3.2.12) 34 | activemodel (= 3.2.12) 35 | activesupport (= 3.2.12) 36 | activesupport (3.2.12) 37 | i18n (~> 0.6) 38 | multi_json (~> 1.0) 39 | addressable (2.3.2) 40 | arel (3.0.2) 41 | builder (3.0.4) 42 | capybara (1.1.2) 43 | mime-types (>= 1.16) 44 | nokogiri (>= 1.3.3) 45 | rack (>= 1.0.0) 46 | rack-test (>= 0.5.4) 47 | selenium-webdriver (~> 2.0) 48 | xpath (~> 0.1.4) 49 | childprocess (0.3.6) 50 | ffi (~> 1.0, >= 1.0.6) 51 | coffee-rails (3.2.2) 52 | coffee-script (>= 2.2.0) 53 | railties (~> 3.2.0) 54 | coffee-script (2.2.0) 55 | coffee-script-source 56 | execjs 57 | coffee-script-source (1.3.3) 58 | commonjs (0.2.6) 59 | diff-lcs (1.1.3) 60 | erubis (2.7.0) 61 | eventmachine (1.0.0) 62 | execjs (1.4.0) 63 | multi_json (~> 1.0) 64 | faye-websocket (0.4.6) 65 | eventmachine (>= 0.12.0) 66 | ffi (1.1.5) 67 | font-awesome-rails (0.4.1) 68 | railties (~> 3.1) 69 | hike (1.2.1) 70 | http_parser.rb (0.5.3) 71 | i18n (0.6.4) 72 | journey (1.0.4) 73 | jquery-rails (2.1.3) 74 | railties (>= 3.1.0, < 5.0) 75 | thor (~> 0.14) 76 | jquery-ui-rails (2.0.2) 77 | jquery-rails 78 | railties (>= 3.1.0) 79 | json (1.7.7) 80 | kaminari (0.14.1) 81 | actionpack (>= 3.0.0) 82 | activesupport (>= 3.0.0) 83 | less (2.2.2) 84 | commonjs (~> 0.2.6) 85 | less-rails (2.2.5) 86 | actionpack (>= 3.1) 87 | less (~> 2.2.0) 88 | libv8 (3.16.14.3) 89 | libwebsocket (0.1.5) 90 | addressable 91 | mail (2.4.4) 92 | i18n (>= 0.4.0) 93 | mime-types (~> 1.16) 94 | treetop (~> 1.4.8) 95 | mime-types (1.21) 96 | multi_json (1.6.1) 97 | nokogiri (1.5.5) 98 | poltergeist (1.0.1) 99 | capybara (~> 1.1) 100 | childprocess (~> 0.3) 101 | faye-websocket (~> 0.4, >= 0.4.4) 102 | http_parser.rb (~> 0.5.3) 103 | multi_json (~> 1.0) 104 | polyglot (0.3.3) 105 | posix-spawn (0.3.6) 106 | pygments.rb (0.3.2) 107 | posix-spawn (~> 0.3.6) 108 | yajl-ruby (~> 1.1.0) 109 | rack (1.4.5) 110 | rack-cache (1.2) 111 | rack (>= 0.4) 112 | rack-ssl (1.3.3) 113 | rack 114 | rack-test (0.6.2) 115 | rack (>= 1.0) 116 | rails (3.2.12) 117 | actionmailer (= 3.2.12) 118 | actionpack (= 3.2.12) 119 | activerecord (= 3.2.12) 120 | activeresource (= 3.2.12) 121 | activesupport (= 3.2.12) 122 | bundler (~> 1.0) 123 | railties (= 3.2.12) 124 | railties (3.2.12) 125 | actionpack (= 3.2.12) 126 | activesupport (= 3.2.12) 127 | rack-ssl (~> 1.3.2) 128 | rake (>= 0.8.7) 129 | rdoc (~> 3.4) 130 | thor (>= 0.14.6, < 2.0) 131 | rake (10.0.3) 132 | rdoc (3.12.2) 133 | json (~> 1.4) 134 | ref (1.0.5) 135 | rspec (2.11.0) 136 | rspec-core (~> 2.11.0) 137 | rspec-expectations (~> 2.11.0) 138 | rspec-mocks (~> 2.11.0) 139 | rspec-core (2.11.1) 140 | rspec-expectations (2.11.3) 141 | diff-lcs (~> 1.1.3) 142 | rspec-mocks (2.11.3) 143 | rspec-rails (2.11.0) 144 | actionpack (>= 3.0) 145 | activesupport (>= 3.0) 146 | railties (>= 3.0) 147 | rspec (~> 2.11.0) 148 | rubyzip (0.9.9) 149 | rugged (0.17.0.b6) 150 | sass (3.2.1) 151 | sass-rails (3.2.5) 152 | railties (~> 3.2.0) 153 | sass (>= 3.1.10) 154 | tilt (~> 1.3) 155 | selenium-webdriver (2.25.0) 156 | childprocess (>= 0.2.5) 157 | libwebsocket (~> 0.1.3) 158 | multi_json (~> 1.0) 159 | rubyzip 160 | sprockets (2.2.2) 161 | hike (~> 1.2) 162 | multi_json (~> 1.0) 163 | rack (~> 1.0) 164 | tilt (~> 1.1, != 1.3.0) 165 | sqlite3 (1.3.6) 166 | therubyracer (0.12.0) 167 | libv8 (~> 3.16.14.0) 168 | ref 169 | thor (0.17.0) 170 | tilt (1.3.4) 171 | treetop (1.4.12) 172 | polyglot 173 | polyglot (>= 0.3.1) 174 | twitter-bootstrap-rails (2.1.4) 175 | actionpack (>= 3.1) 176 | less-rails (~> 2.2.3) 177 | railties (>= 3.1) 178 | therubyracer (>= 0.10.2) 179 | tzinfo (0.3.36) 180 | uglifier (1.3.0) 181 | execjs (>= 0.3.0) 182 | multi_json (~> 1.0, >= 1.0.2) 183 | xpath (0.1.4) 184 | nokogiri (~> 1.3) 185 | yajl-ruby (1.1.0) 186 | 187 | PLATFORMS 188 | ruby 189 | 190 | DEPENDENCIES 191 | bootstrap_kaminari! 192 | capybara 193 | coffee-rails (~> 3.2.1) 194 | font-awesome-rails 195 | jquery-rails 196 | jquery-ui-rails 197 | kaminari 198 | poltergeist 199 | pygments.rb 200 | rails (~> 3.2.12) 201 | rspec-rails 202 | rugged (= 0.17.0.b6) 203 | sass-rails (~> 3.2.3) 204 | sqlite3 205 | therubyracer 206 | twitter-bootstrap-rails 207 | uglifier (>= 1.0.3) 208 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Maryan Hratson 2 | 3 | Gistie is an Open Source project licensed under the terms of 4 | the LGPLv3 license. Please see 5 | for license text. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gistie 2 | ================ 3 | 4 | [Gistie] is an open source [Git]-based pastebin implementation that enables sharing snippets(aka Gist) using simple web UI. 5 | Every Gist is a *Git repository* thus **versioned** and **cloneable**. Heavily inspired by gist.github.com 6 | 7 | ![Gistie](https://lh5.googleusercontent.com/-WY05yzcmD1c/UKBtiwfq-QI/AAAAAAAAHps/GWb5Us0eLGs/s797/gistie.png) 8 | 9 | Features 10 | --------------- 11 | 12 | as of `v0.1`: 13 | 14 | - Create, Edit, Delete Gists 15 | - Revision browsing 16 | - Cloneable (served by git-daemon) 17 | - public by default 18 | - Raw view 19 | - syntax highlight with pygments (filename based detection) 20 | 21 | 22 | 23 | TODO 24 | --------------- 25 | 26 | - Fork 27 | - Markup(markdown) rendering 28 | - Search 29 | - Binary support 30 | - Inline image 31 | - Accounts 32 | - Private gists 33 | - Commenting 34 | - "Code Review" 35 | 36 | 37 | Installation 38 | --------------- 39 | 40 | [Gistie] is based on Ruby on Rails, Sqlite, Libgit2 and Pygments(requires Python installed). 41 | 42 | 43 | 1. `git clone https://github.com/gmarik/Gistie` 44 | 2. `cd Gistie && bundle install` 45 | 3. `rake db:create db:migrate` 46 | 4. `rails server` 47 | 48 | 49 | Making repos cloneable 50 | -------------- 51 | 52 | 53 | run 54 | 55 | git-daemon --user=nobody --export-all --base-path=/path/to/Gistie/repos_production 56 | 57 | 58 | 59 | Testing 60 | --------------- 61 | 62 | 1. `cd Gistie` 63 | 2. `rake db:test:clone_structure` 64 | 3. `rspec spec` 65 | 66 | 67 | License 68 | --------------- 69 | 70 | Please see LICENSE for licensing details. 71 | 72 | 73 | Author 74 | --------------- 75 | 76 | Maryan Hratson aka [@gmarik](http://github.com/gmarik) 77 | 78 | - contact: [@gmarik](http://twitter.com/gmarik) 79 | 80 | [Gistie]:http://github.com/gmarik/Gistie 81 | 82 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Gistie::Application.load_tasks 8 | -------------------------------------------------------------------------------- /app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /app/assets/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/app/assets/images/rails.png -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require jquery.effects.highlight 16 | //= require twitter/bootstrap 17 | //= require_tree . 18 | 19 | 20 | $(function() { 21 | 22 | $('abbr.timeago').timeago() 23 | 24 | 25 | // Gist 26 | 27 | function rmBlob() { 28 | // TODO: implment "undo" 29 | $(this).parents('.control-group:first'). 30 | find('*[name]'). 31 | attr('disabled', 'disabled'). 32 | end(). 33 | hide() 34 | 35 | } 36 | 37 | function addBlob() { 38 | // TODO: implment "undo" 39 | var t = $('form .empty') 40 | var template = t.html() 41 | var blob = $(template).insertBefore(t) 42 | $(blob). 43 | effect('highlight', {}, 3000) 44 | } 45 | 46 | $('form span[rel=rmBlob]').click(rmBlob) 47 | $('form span[rel=addBlob]').click(addBlob) 48 | 49 | }) 50 | -------------------------------------------------------------------------------- /app/assets/javascripts/bootstrap.js.coffee: -------------------------------------------------------------------------------- 1 | jQuery -> 2 | $("a[rel=popover]").popover() 3 | $(".tooltip").tooltip() 4 | $("a[rel=tooltip]").tooltip() -------------------------------------------------------------------------------- /app/assets/javascripts/jquery.timeago.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Timeago is a jQuery plugin that makes it easy to support automatically 3 | * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago"). 4 | * 5 | * @name timeago 6 | * @version 0.11.4 7 | * @requires jQuery v1.2.3+ 8 | * @author Ryan McGeary 9 | * @license MIT License - http://www.opensource.org/licenses/mit-license.php 10 | * 11 | * For usage and examples, visit: 12 | * http://timeago.yarp.com/ 13 | * 14 | * Copyright (c) 2008-2012, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org) 15 | */ 16 | (function($) { 17 | $.timeago = function(timestamp) { 18 | if (timestamp instanceof Date) { 19 | return inWords(timestamp); 20 | } else if (typeof timestamp === "string") { 21 | return inWords($.timeago.parse(timestamp)); 22 | } else if (typeof timestamp === "number") { 23 | return inWords(new Date(timestamp)); 24 | } else { 25 | return inWords($.timeago.datetime(timestamp)); 26 | } 27 | }; 28 | var $t = $.timeago; 29 | 30 | $.extend($.timeago, { 31 | settings: { 32 | refreshMillis: 60000, 33 | allowFuture: false, 34 | strings: { 35 | prefixAgo: null, 36 | prefixFromNow: null, 37 | suffixAgo: "ago", 38 | suffixFromNow: "from now", 39 | seconds: "less than a minute", 40 | minute: "about a minute", 41 | minutes: "%d minutes", 42 | hour: "about an hour", 43 | hours: "about %d hours", 44 | day: "a day", 45 | days: "%d days", 46 | month: "about a month", 47 | months: "%d months", 48 | year: "about a year", 49 | years: "%d years", 50 | wordSeparator: " ", 51 | numbers: [] 52 | } 53 | }, 54 | inWords: function(distanceMillis) { 55 | var $l = this.settings.strings; 56 | var prefix = $l.prefixAgo; 57 | var suffix = $l.suffixAgo; 58 | if (this.settings.allowFuture) { 59 | if (distanceMillis < 0) { 60 | prefix = $l.prefixFromNow; 61 | suffix = $l.suffixFromNow; 62 | } 63 | } 64 | 65 | var seconds = Math.abs(distanceMillis) / 1000; 66 | var minutes = seconds / 60; 67 | var hours = minutes / 60; 68 | var days = hours / 24; 69 | var years = days / 365; 70 | 71 | function substitute(stringOrFunction, number) { 72 | var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction; 73 | var value = ($l.numbers && $l.numbers[number]) || number; 74 | return string.replace(/%d/i, value); 75 | } 76 | 77 | var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) || 78 | seconds < 90 && substitute($l.minute, 1) || 79 | minutes < 45 && substitute($l.minutes, Math.round(minutes)) || 80 | minutes < 90 && substitute($l.hour, 1) || 81 | hours < 24 && substitute($l.hours, Math.round(hours)) || 82 | hours < 42 && substitute($l.day, 1) || 83 | days < 30 && substitute($l.days, Math.round(days)) || 84 | days < 45 && substitute($l.month, 1) || 85 | days < 365 && substitute($l.months, Math.round(days / 30)) || 86 | years < 1.5 && substitute($l.year, 1) || 87 | substitute($l.years, Math.round(years)); 88 | 89 | var separator = $l.wordSeparator === undefined ? " " : $l.wordSeparator; 90 | return $.trim([prefix, words, suffix].join(separator)); 91 | }, 92 | parse: function(iso8601) { 93 | var s = $.trim(iso8601); 94 | s = s.replace(/\.\d+/,""); // remove milliseconds 95 | s = s.replace(/-/,"/").replace(/-/,"/"); 96 | s = s.replace(/T/," ").replace(/Z/," UTC"); 97 | s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400 98 | return new Date(s); 99 | }, 100 | datetime: function(elem) { 101 | var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title"); 102 | return $t.parse(iso8601); 103 | }, 104 | isTime: function(elem) { 105 | // jQuery's `is()` doesn't play well with HTML5 in IE 106 | return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); 107 | } 108 | }); 109 | 110 | $.fn.timeago = function() { 111 | var self = this; 112 | self.each(refresh); 113 | 114 | var $s = $t.settings; 115 | if ($s.refreshMillis > 0) { 116 | setInterval(function() { self.each(refresh); }, $s.refreshMillis); 117 | } 118 | return self; 119 | }; 120 | 121 | function refresh() { 122 | var data = prepareData(this); 123 | if (!isNaN(data.datetime)) { 124 | $(this).text(inWords(data.datetime)); 125 | } 126 | return this; 127 | } 128 | 129 | function prepareData(element) { 130 | element = $(element); 131 | if (!element.data("timeago")) { 132 | element.data("timeago", { datetime: $t.datetime(element) }); 133 | var text = $.trim(element.text()); 134 | if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) { 135 | element.attr("title", text); 136 | } 137 | } 138 | return element.data("timeago"); 139 | } 140 | 141 | function inWords(date) { 142 | return $t.inWords(distance(date)); 143 | } 144 | 145 | function distance(date) { 146 | return (new Date().getTime() - date.getTime()); 147 | } 148 | 149 | // fix for IE6 suckage 150 | document.createElement("abbr"); 151 | document.createElement("time"); 152 | }(jQuery)); 153 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | * 14 | *= require 'font-awesome' 15 | */ 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/bootstrap_and_overrides.css.less: -------------------------------------------------------------------------------- 1 | @import "twitter/bootstrap/bootstrap"; 2 | @import "twitter/bootstrap/responsive"; 3 | 4 | // Set the correct sprite paths 5 | @iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings.png"); 6 | @iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white.png"); 7 | 8 | // Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines) 9 | // Note: If you use asset_path() here, your compiled boostrap_and_overrides.css will not 10 | // have the proper paths. So for now we use the absolute path. 11 | @fontAwesomeEotPath: asset-path("fontawesome-webfont.eot"); 12 | @fontAwesomeWoffPath: asset-path("fontawesome-webfont.woff"); 13 | @fontAwesomeTtfPath: asset-path("fontawesome-webfont.ttf"); 14 | @fontAwesomeSvgPath: asset-path("fontawesome-webfont.svg"); 15 | 16 | // Font Awesome 17 | @import "fontawesome"; 18 | 19 | // Your custom LESS stylesheets goes here 20 | // 21 | // Since bootstrap was imported above you have access to its mixins which 22 | // you may use and inherit here 23 | // 24 | // If you'd like to override bootstrap's own variables, you can do so here as well 25 | // See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation 26 | // 27 | // Example: 28 | // @linkColor: #ff0000; 29 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #f8f8f8; } 3 | .highlight .c { color: #408080; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #008000; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #BC7A00 } /* Comment.Preproc */ 9 | .highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ 11 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 14 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 15 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 16 | .highlight .go { color: #888888 } /* Generic.Output */ 17 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 20 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 21 | .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { color: #008000 } /* Keyword.Pseudo */ 25 | .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #B00040 } /* Keyword.Type */ 27 | .highlight .m { color: #666666 } /* Literal.Number */ 28 | .highlight .s { color: #BA2121 } /* Literal.String */ 29 | .highlight .na { color: #7D9029 } /* Name.Attribute */ 30 | .highlight .nb { color: #008000 } /* Name.Builtin */ 31 | .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #880000 } /* Name.Constant */ 33 | .highlight .nd { color: #AA22FF } /* Name.Decorator */ 34 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 35 | .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 36 | .highlight .nf { color: #0000FF } /* Name.Function */ 37 | .highlight .nl { color: #A0A000 } /* Name.Label */ 38 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 39 | .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ 40 | .highlight .nv { color: #19177C } /* Name.Variable */ 41 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .highlight .mf { color: #666666 } /* Literal.Number.Float */ 44 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */ 45 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */ 46 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */ 47 | .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ 48 | .highlight .sc { color: #BA2121 } /* Literal.String.Char */ 49 | .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 50 | .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ 51 | .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 52 | .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ 53 | .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 54 | .highlight .sx { color: #008000 } /* Literal.String.Other */ 55 | .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ 56 | .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ 57 | .highlight .ss { color: #19177C } /* Literal.String.Symbol */ 58 | .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ 59 | .highlight .vc { color: #19177C } /* Name.Variable.Class */ 60 | .highlight .vg { color: #19177C } /* Name.Variable.Global */ 61 | .highlight .vi { color: #19177C } /* Name.Variable.Instance */ 62 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ 63 | -------------------------------------------------------------------------------- /app/assets/stylesheets/syntax.css: -------------------------------------------------------------------------------- 1 | 2 | .syntax * { 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | .sha, 8 | .syntax pre { 9 | font-size: 80%; 10 | font-family: Monaco, "Courier New", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", monospace; 11 | } 12 | 13 | .syntax pre { 14 | white-space: pre; 15 | line-height: 1.3em; 16 | 17 | padding: 1em 0.5em; 18 | } 19 | 20 | .syntax > table { 21 | border: 0 22 | } 23 | 24 | .syntax > table th pre { 25 | color: #AAA !important; 26 | text-align: right; 27 | } 28 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end 4 | -------------------------------------------------------------------------------- /app/controllers/blobs_controller.rb: -------------------------------------------------------------------------------- 1 | class BlobsController < ApplicationController 2 | def show 3 | @gist = Gist.find(params[:gist_id]) 4 | render text: @gist.repo.lookup(params[:id]).read_raw.data, :layout => false, :content_type => 'text/plain' 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/gists_controller.rb: -------------------------------------------------------------------------------- 1 | class GistsController < ApplicationController 2 | respond_to :html 3 | 4 | def index 5 | @gists = Gist.recent.page(params[:page]) 6 | end 7 | 8 | def show 9 | @gist = Gist.find(params[:id]) 10 | @tree = @gist.tree(params[:revision].presence) 11 | end 12 | 13 | def new 14 | @gist = Gist.new 15 | end 16 | 17 | def edit 18 | @gist = Gist.find(params[:id]) 19 | end 20 | 21 | def create 22 | @gist = Gist.new(params[:gist]) 23 | 24 | if @gist.save_and_commit 25 | redirect_to @gist, notice: 'Gist was successfully created.' 26 | else 27 | render action: "new" 28 | end 29 | end 30 | 31 | def update 32 | @gist = Gist.find(params[:id]) 33 | 34 | if @gist.save_and_commit(params[:gist]) 35 | redirect_to @gist, notice: 'Gist was successfully updated.' 36 | else 37 | render action: "edit" 38 | end 39 | end 40 | 41 | def destroy 42 | @gist = Gist.find(params[:id]) 43 | 44 | @gist.destroy 45 | 46 | redirect_to gists_url, notice: 'Gist was successfully deleted' 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def font_awesome_icon(class_name, text = '') 3 | %Q[ #{text}].html_safe 4 | end 5 | 6 | alias_method :i, :font_awesome_icon 7 | 8 | def public_clone_url(gist) 9 | hostname = if defined?(HOSTNAME) then HOSTNAME else request.host end 10 | "git://#{hostname}/#{gist.id}.git" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/helpers/gists_helper.rb: -------------------------------------------------------------------------------- 1 | module GistsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/app/mailers/.gitkeep -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/app/models/.gitkeep -------------------------------------------------------------------------------- /app/models/gist.rb: -------------------------------------------------------------------------------- 1 | class Gist < ActiveRecord::Base 2 | 3 | # TODO: rename this to gist_files instead gist_blobs 4 | # as blob is just content without filename 5 | attr_accessor :gist_blobs, :gist_blobs_attributes 6 | 7 | attr_accessible :gist_blobs_attributes, :description 8 | 9 | validate :non_blank, :unique_names 10 | 11 | scope :recent, -> { order('created_at desc') } 12 | 13 | def non_blank 14 | blank? and errors.add(:gist_blobs, "Can't be blank") 15 | end 16 | 17 | def unique_names 18 | dups = dup_names(gist_blobs).map {|name, count| %Q["#{name}": #{count}] } 19 | dups.empty? or 20 | errors.add(:gist_blobs, "duplicate names: #{dups.join(',')}") 21 | end 22 | 23 | def dup_names(blobs) 24 | duplicates = blobs.map(&:name). 25 | group_by {|b| b }. 26 | values. 27 | map {|b| [b.first, b.size]}. 28 | select {|name, count| count > 1} 29 | end 30 | 31 | private :dup_names 32 | 33 | def blank? 34 | gist_blobs.blank? 35 | end 36 | 37 | def gist_blobs_attributes 38 | @gist_blobs_attributes || [] 39 | end 40 | 41 | def gist_blobs_attributes=(attrs) 42 | self.gist_blobs = attrs.map do |attr| 43 | GistBlob.new(attr) 44 | end.reject(&:blank?) 45 | end 46 | 47 | # Git integration 48 | def gist_blobs 49 | @gist_blobs ||= begin 50 | if new_record? then [] 51 | else gist_read 52 | end 53 | end 54 | end 55 | 56 | def save_and_commit(*args) 57 | begin 58 | save_and_commit!(*args) 59 | true 60 | rescue ActiveRecord::RecordInvalid => e 61 | # TODO: log e 62 | false 63 | end 64 | end 65 | 66 | def save_and_commit!(update_attrs = {}) 67 | SaveGist.new(self).(update_attrs) 68 | end 69 | 70 | def repo_name 71 | id.to_s 72 | end 73 | 74 | def repo 75 | if new_record? then nil 76 | else @repo ||= GistRepo.named(repo_name) 77 | end 78 | end 79 | 80 | def init_repo 81 | GistRepo.init_named_repo(repo_name) 82 | end 83 | 84 | def gist_read 85 | repo.tree(repo.head).map(&method(:gist_blob)) 86 | end 87 | 88 | def gist_blob(entry) 89 | # TODO: make consitent 90 | params = {name: entry.name, blob: entry.content} 91 | GistBlob.new(params) 92 | end 93 | 94 | def gist_write 95 | repo.write(self.gist_blobs) 96 | end 97 | 98 | def recent_commits(limit = 10) 99 | repo.log.take(limit) 100 | end 101 | 102 | def tree(head) 103 | repo.tree(head || repo.head) 104 | end 105 | 106 | def to_preview_html 107 | GistBlobPresenter.new(self.tree(repo.head).first).pretty_excerpt 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /app/models/gist_blob.rb: -------------------------------------------------------------------------------- 1 | class GistBlob 2 | include ActiveModel::Validations 3 | 4 | attr_accessor :blob, :name#, :description 5 | 6 | validates :blob, { presence: true } 7 | 8 | def initialize(params = {}) 9 | params.each do |k, v| 10 | self.send("#{k}=", v) 11 | end 12 | end 13 | 14 | def name 15 | @name || 'Text' 16 | end 17 | 18 | def blank? 19 | self.blob.blank? 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/models/gist_repo.rb: -------------------------------------------------------------------------------- 1 | class GistRepo 2 | include Rugged 3 | include RepoWriter 4 | 5 | attr_reader :path 6 | 7 | def initialize(path) 8 | @path = path 9 | end 10 | 11 | delegate :lookup, :to => :repo 12 | 13 | def repo 14 | @repo ||= Repository.new(@path) 15 | end 16 | 17 | def head 18 | repo.head.target 19 | end 20 | 21 | def self.init_named_repo(name, bare = true) 22 | Repository.init_at(repo_path(name), bare) 23 | end 24 | 25 | def self.named(name) 26 | new(repo_path(name)) 27 | end 28 | 29 | def self.repo_path(name, root = Rails.configuration.repo_root) 30 | (root + ( name + ".git/")).to_s 31 | end 32 | 33 | def log(opts = {}) 34 | @log ||= RepoLog.new(repo, opts) 35 | end 36 | 37 | def tree(head) 38 | @tree ||= RepoTree.new(repo, head) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/models/highlighted_source.rb: -------------------------------------------------------------------------------- 1 | class HighlightedSource 2 | def initialize(filename, source) 3 | @filename = filename 4 | @source = source 5 | end 6 | 7 | def lexer 8 | 9 | # TODO: spec 10 | # TODO: binary 11 | begin 12 | Pygments.lexer_name_for(filename: @filename) 13 | rescue MentosError => e 14 | Rails.logger.warn("Couldn't find lexer for #@filename: #{e.message}") 15 | 16 | :text 17 | end 18 | end 19 | 20 | def highlight 21 | wrap_lines(pygments) 22 | end 23 | alias_method :call, :highlight 24 | 25 | def to_highlighted_html 26 | highlight.join('') 27 | end 28 | 29 | def to_formatted_html 30 | src = highlight 31 | 32 | lns = line_numbers(src.size - 2).join("\n") 33 | code = src.join('') 34 | 35 | html = <<-HTML.strip_heredoc 36 | 37 | 38 | 39 | 42 | 45 | 46 | 47 |
40 |
#{lns}
41 |
43 | #{code} 44 |
48 | HTML 49 | end 50 | 51 | private 52 | 53 | def line_numbers(count) 54 | t = %Q[%d] 55 | liner = ->(ln){t % [ln, ln, ln]} 56 | 1.upto(count).map(&liner) 57 | end 58 | 59 | def wrap_lines(pygments_html) 60 | from = pygments_html.index('
') + 5
61 |     to = "
".size 62 | 63 | pre = pygments_html[0...from] 64 | post = pygments_html[-to..-1] 65 | lines = pygments_html[from...-to].split("\n") 66 | 67 | wrapper = ->(line) { %Q[
%s
] % line } 68 | 69 | wrapped_lines = lines.map(&wrapper) 70 | 71 | [pre] + wrapped_lines + [post] 72 | end 73 | 74 | def pygments 75 | @source.force_encoding("UTF-8") 76 | Pygments.highlight(@source, :lexer => lexer) 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /app/models/repo_log.rb: -------------------------------------------------------------------------------- 1 | class RepoLog 2 | 3 | class Commit 4 | def initialize(commit) 5 | @commit = commit 6 | end 7 | 8 | def created_at 9 | @commit.time 10 | end 11 | 12 | def pretty_sha 13 | sha[0, 9] 14 | end 15 | 16 | def sha 17 | @commit.oid 18 | end 19 | alias_method :oid, :sha 20 | end 21 | 22 | include Enumerable 23 | 24 | def initialize(repo, opts = {}) 25 | @repo = repo 26 | @target = opts.delete(:target) || @repo.head.target 27 | @walker = walker(@repo, @target) 28 | end 29 | 30 | def each(&block) 31 | to_enum.each(&block) 32 | end 33 | 34 | def walker(repo, target) 35 | walker = Rugged::Walker.new(repo) 36 | walker.sorting(Rugged::SORT_DATE) 37 | walker.push(target) 38 | walker 39 | end 40 | 41 | def to_enum 42 | Enumerator.new do |y| 43 | enum = @walker.to_enum 44 | loop do 45 | y << Commit.new(enum.next) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/models/repo_tree.rb: -------------------------------------------------------------------------------- 1 | class RepoTree 2 | 3 | include Enumerable 4 | 5 | class Blob 6 | 7 | attr_reader :name, :oid, :type, :filemode 8 | 9 | def initialize(tree, params) 10 | @tree = tree 11 | @name, @oid, @type, @filemode = params.values_at(:name, :oid, :type, :filemode) 12 | end 13 | 14 | def data 15 | @tree.lookup(@oid).content 16 | end 17 | 18 | alias_method :content, :data 19 | 20 | end 21 | 22 | attr_reader :repo, :head 23 | 24 | def initialize(repo, head) 25 | @repo = repo 26 | @head = head 27 | end 28 | 29 | def each(&block) 30 | to_enum.each(&block) 31 | end 32 | 33 | def to_enum 34 | Enumerator.new do |y| 35 | enum = tree.to_enum 36 | loop do 37 | entry = enum.next 38 | # TODO: add :tree(directory) support 39 | y << Blob.new(self, entry) if entry[:type] == :blob 40 | end 41 | end 42 | end 43 | 44 | def lookup(sha) 45 | repo.lookup(sha) 46 | end 47 | 48 | private 49 | 50 | def tree 51 | @tree ||= lookup(head).tree 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /app/models/repo_writer.rb: -------------------------------------------------------------------------------- 1 | module RepoWriter 2 | 3 | attr_reader :repo 4 | 5 | NothingToCommitError = Class.new(StandardError) 6 | 7 | def write(blobs) 8 | # TODO: extract 9 | author = { 10 | email: "gistie@gistie.com", 11 | time: Time.now, 12 | name: "gistie" 13 | } 14 | 15 | named_blob_oids = write_blobs(blobs) 16 | tree_oid = write_tree(named_blob_oids) 17 | commit_oid = write_commit(tree_oid, author) 18 | # this prevents duplicated commits 19 | # even though commit is writen it just becomes an orphan 20 | # until Garbage Collected 21 | # TODO: think of better implementation avoiding orphan commit object 22 | raise NothingToCommitError.new(repo.path) if duplicate?(commit_oid) 23 | ref = set_master(commit_oid) 24 | end 25 | 26 | def duplicate?(commit_oid) 27 | commit = repo.lookup(commit_oid) 28 | return false if commit.parents.empty? 29 | parent_commit = commit.parents.first 30 | commit.tree.oid == parent_commit.tree.oid 31 | end 32 | 33 | def write_blob(blob) 34 | oid = repo.write(blob, :blob) 35 | end 36 | 37 | def write_blobs(blobs) 38 | blobs.map { |b| [b.name, write_blob(b.blob)] } 39 | end 40 | 41 | def write_tree(blob_named_sha1s, builder = Rugged::Tree::Builder.new) 42 | blob_named_sha1s.each do |name, oid| 43 | builder << { 44 | type: :blob, 45 | name: name, 46 | oid: oid, 47 | filemode: 33188 48 | } 49 | end 50 | 51 | tree_sha1 = builder.write(repo) 52 | end 53 | 54 | def write_commit(tree_sha1, author, parents = commit_parent, commit = Rugged::Commit) 55 | commit_sha1 = commit.create(repo, 56 | author: author, 57 | message: "Commit\n\n", 58 | committer: author, 59 | parents: parents, 60 | tree: tree_sha1 61 | ) 62 | end 63 | 64 | def commit_parent 65 | if repo.empty? then [] 66 | else [repo.head.target] 67 | end 68 | end 69 | 70 | def set_master(commit_oid) 71 | ref_name = "refs/heads/master" 72 | r = Rugged::Reference 73 | if repo.empty? 74 | r.create(repo, ref_name, commit_oid) 75 | else 76 | ref = r.lookup(repo, ref_name) 77 | ref.target = commit_oid 78 | end 79 | commit_oid 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /app/models/save_gist.rb: -------------------------------------------------------------------------------- 1 | class SaveGist 2 | def initialize(gist) 3 | @gist = gist 4 | end 5 | 6 | def call(update_attributes = {}) 7 | @gist.transaction do 8 | @gist.assign_attributes(update_attributes) 9 | 10 | init_repo = @gist.new_record? 11 | @gist.save! 12 | # gist has to be persited at this point 13 | # as gist.id is used to generate repo's name 14 | @gist.init_repo if init_repo 15 | begin 16 | @gist.gist_write #async? 17 | rescue RepoWriter::NothingToCommitError => e 18 | # TODO: log it 19 | Rails.logger.warn("Nothing to commit") 20 | end 21 | end 22 | @gist 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/presenters/gist_blob_presenter.rb: -------------------------------------------------------------------------------- 1 | class GistBlobPresenter 2 | 3 | delegate :name, :content, :data, :filemode, :oid, :to => :gist_blob 4 | 5 | attr_reader :gist_blob 6 | 7 | def initialize(blob) 8 | @gist_blob = blob 9 | end 10 | 11 | def highlight(filename, src) 12 | HighlightedSource.new(name, src).to_formatted_html.html_safe 13 | end 14 | 15 | def pretty_excerpt(limit = 4) 16 | src = content.split("\n").take(limit).join("\n") 17 | highlight(name, src) 18 | end 19 | 20 | def pretty_content 21 | highlight(name, content) 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /app/views/gists/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(@gist) do |f| %> 2 |
3 | <% if @gist.errors.any? %> 4 |
5 | <%# TODO improve error display %> 6 | <%= pluralize(@gist.errors.count, "error") %> prohibited this gist from being saved: 7 | 8 |
    9 | <% @gist.errors.full_messages.each do |msg| %> 10 |
  • <%= msg %>
  • 11 | <% end %> 12 |
13 |
14 | <% end %> 15 | 16 |
17 |
18 | <%= f.text_field 'description', title: 'gist description', placeholder: 'description', class: 'span9 input-xlarge' -%> 19 |
20 |
21 | 22 |
23 | 24 | <% @gist.gist_blobs.each do |gb| %> 25 | <%= render partial: 'gist_blob', object: gb, locals: {f: f } %> 26 | <% end %> 27 | 28 | <% s = 'display:none;' unless @gist.gist_blobs.empty? %> 29 | 30 |
31 | <%= render partial: 'gist_blob', object: GistBlob.new, locals: {f: f} %> 32 |
33 | 34 |
35 |
36 |   37 | 38 |
39 |
40 | 41 |
42 | 43 | 44 |
45 | <%= f.submit class:'btn btn-primary'%> 46 |
47 | <% end %> 48 | -------------------------------------------------------------------------------- /app/views/gists/_gist_blob.html.erb: -------------------------------------------------------------------------------- 1 | <% n = "gist[gist_blobs_attributes][]" -%> 2 | 3 | <%= f.fields_for(gist_blob) do |gf| -%> 4 | 5 |
6 |
7 |
8 | <%= gf.text_field 'name', :name => "#{n}[name]", class: 'span8' -%> 9 | 10 |
11 | <%= gf.text_area 'blob', :name => "#{n}[blob]", class: 'span9' -%> 12 |
13 |
14 | 15 | <%- end -%> 16 | -------------------------------------------------------------------------------- /app/views/gists/_revisions.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Revisions

3 | 11 |
12 | -------------------------------------------------------------------------------- /app/views/gists/edit.html.erb: -------------------------------------------------------------------------------- 1 |

<%= i('book') %>Edit Gist #<%= @gist.id %>

2 | 3 | <%= render 'form' %> 4 | -------------------------------------------------------------------------------- /app/views/gists/index.html.erb: -------------------------------------------------------------------------------- 1 |

Recent gists

2 | 3 | <% if @gists.empty? %> 4 | 5 | be the first to <%= link_to "create one", [:new, :gist] %> 6 | 7 | <% else %> 8 | 9 | <% @gists.each do |gist| %> 10 | 11 |
12 |
13 |

14 | <%= link_to i('book', "##{gist.id}"), gist %> 15 | <%= gist.description %> 16 |

17 |
18 | 19 |
20 | <%= time.to_s %> 21 |
22 | 23 |
24 | <%= gist.to_preview_html %> 25 |
26 | 27 |
28 | <% end %> 29 | 30 | <%= paginate @gists, theme: 'bootstrap', first: false, last: false %> 31 | 32 | <% end %> 33 | -------------------------------------------------------------------------------- /app/views/gists/new.html.erb: -------------------------------------------------------------------------------- 1 |

<%= i('book') %> New gist

2 | 3 | 4 | <%= render "form" %> 5 | -------------------------------------------------------------------------------- /app/views/gists/show.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <% content_for :sidebar do %> 3 | <%= render partial: 'revisions', locals: { gist: @gist } %> 4 | <% end %> 5 | 6 |

<%= i('book') %> Gist #<%= @gist.id %>

7 | 8 | 9 | 10 |
11 |
12 | 13 | <%= link_to i('edit', "Edit"), edit_gist_path(@gist), class: 'btn btn-primary' %> 14 | 15 | 18 | 19 | 23 | 24 |
25 |
26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
description <%= @gist.description %>
public clone url <%= url %>
41 | 42 | <%- @tree.each do |b| %> 43 | 44 | <% gb = GistBlobPresenter.new(b) %> 45 | 46 |
47 |
48 |
<%= i('file', b.name) %>
49 |
<%= link_to('raw', gist_raw_url(@gist.id, b.oid, b.name)) %>
50 |
51 |
<%= gb.pretty_content %>
52 | 53 |
54 | 55 | <% end %> 56 | 57 |
58 | 59 | -------------------------------------------------------------------------------- /app/views/layouts/_sidebar.html.erb: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= content_for?(:title) ? yield(:title) : "Gistie" %> 8 | <%= csrf_meta_tags %> 9 | 10 | 11 | 14 | 15 | <%= stylesheet_link_tag "application", :media => "all" %> 16 | 17 | 18 | 19 | <%= favicon_link_tag 'images/apple-touch-icon-144x144-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '144x144' %> 20 | 21 | 22 | 23 | <%= favicon_link_tag 'images/apple-touch-icon-114x114-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '114x114' %> 24 | 25 | 26 | 27 | <%= favicon_link_tag 'images/apple-touch-icon-72x72-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '72x72' %> 28 | 29 | 30 | 31 | <%= favicon_link_tag 'images/apple-touch-icon-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png' %> 32 | 33 | 34 | 35 | <%= favicon_link_tag 'favicon.ico', :rel => 'shortcut icon' %> 36 | 37 | 92 | 93 | 94 | 95 | 117 | 118 | 119 | 120 |
121 | 122 |
123 |
124 | <%= bootstrap_flash %> 125 |
126 |
127 | 128 |
129 |
130 | <%= yield %> 131 |
132 |
133 | <% if content_for? :sidebar %> 134 | 137 | <% end %> 138 |
139 |
140 | 141 |
142 | 143 | 146 | 147 |
148 | 149 | 151 | 152 | <%= javascript_include_tag "application" %> 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Gistie::Application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | # Pick the frameworks you want: 4 | require "active_record/railtie" 5 | require "action_controller/railtie" 6 | require "action_mailer/railtie" 7 | require "active_resource/railtie" 8 | require "sprockets/railtie" 9 | # require "rails/test_unit/railtie" 10 | 11 | if defined?(Bundler) 12 | # If you precompile assets before deploying to production, use this line 13 | Bundler.require(*Rails.groups(:assets => %w(development test))) 14 | # If you want your assets lazily compiled in production, use this line 15 | # Bundler.require(:default, :assets, Rails.env) 16 | end 17 | 18 | module Gistie 19 | class Application < Rails::Application 20 | # Settings in config/environments/* take precedence over those specified here. 21 | # Application configuration should go into files in config/initializers 22 | # -- all .rb files in that directory are automatically loaded. 23 | 24 | # Custom directories with classes and modules you want to be autoloadable. 25 | # config.autoload_paths += %W(#{config.root}/extras) 26 | 27 | # Only load the plugins named here, in the order given (default is alphabetical). 28 | # :all can be used as a placeholder for all plugins not explicitly named. 29 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 30 | 31 | # Activate observers that should always be running. 32 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 33 | 34 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 35 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 36 | # config.time_zone = 'Central Time (US & Canada)' 37 | 38 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 39 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 40 | # config.i18n.default_locale = :de 41 | 42 | # Configure the default encoding used in templates for Ruby 1.9. 43 | config.encoding = "utf-8" 44 | 45 | # Configure sensitive parameters which will be filtered from the log file. 46 | config.filter_parameters += [:password] 47 | 48 | # Enable escaping HTML in JSON. 49 | config.active_support.escape_html_entities_in_json = true 50 | 51 | # Use SQL instead of Active Record's schema dumper when creating the database. 52 | # This is necessary if your schema can't be completely dumped by the schema dumper, 53 | # like if you have constraints or database-specific column types 54 | # config.active_record.schema_format = :sql 55 | 56 | # Enforce whitelist mode for mass assignment. 57 | # This will create an empty whitelist of attributes available for mass-assignment for all models 58 | # in your app. As such, your models will need to explicitly whitelist or blacklist accessible 59 | # parameters by using an attr_accessible or attr_protected declaration. 60 | config.active_record.whitelist_attributes = true 61 | 62 | # Enable the asset pipeline 63 | config.assets.enabled = true 64 | 65 | # Version of your assets, change this if you want to expire all your assets 66 | config.assets.version = '1.0' 67 | 68 | # default repo_path 69 | config.repo_root = Rails.root + "repos_#{Rails.env}" 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | Gistie::Application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Gistie::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger 20 | config.active_support.deprecation = :log 21 | 22 | # Only use best-standards-support built into browsers 23 | config.action_dispatch.best_standards_support = :builtin 24 | 25 | # Raise exception on mass assignment protection for Active Record models 26 | config.active_record.mass_assignment_sanitizer = :strict 27 | 28 | # Log the query plan for queries taking more than this (works 29 | # with SQLite, MySQL, and PostgreSQL) 30 | config.active_record.auto_explain_threshold_in_seconds = 0.5 31 | 32 | # Do not compress assets 33 | config.assets.compress = false 34 | 35 | # Expands the lines which load the assets 36 | config.assets.debug = true 37 | end 38 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Gistie::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # Code is not reloaded between requests 5 | config.cache_classes = true 6 | 7 | # Full error reports are disabled and caching is turned on 8 | config.consider_all_requests_local = false 9 | config.action_controller.perform_caching = true 10 | 11 | # Disable Rails's static asset server (Apache or nginx will already do this) 12 | config.serve_static_assets = false 13 | 14 | # Compress JavaScripts and CSS 15 | config.assets.compress = true 16 | 17 | # Don't fallback to assets pipeline if a precompiled asset is missed 18 | config.assets.compile = false 19 | 20 | # Generate digests for assets URLs 21 | config.assets.digest = true 22 | 23 | # Defaults to nil and saved in location specified by config.assets.prefix 24 | # config.assets.manifest = YOUR_PATH 25 | 26 | # Specifies the header that your server uses for sending files 27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 29 | 30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 31 | # config.force_ssl = true 32 | 33 | # See everything in the log (default is :info) 34 | # config.log_level = :debug 35 | 36 | # Prepend all log lines with the following tags 37 | # config.log_tags = [ :subdomain, :uuid ] 38 | 39 | # Use a different logger for distributed setups 40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 41 | 42 | # Use a different cache store in production 43 | # config.cache_store = :mem_cache_store 44 | 45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server 46 | # config.action_controller.asset_host = "http://assets.example.com" 47 | 48 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 49 | # config.assets.precompile += %w( search.js ) 50 | 51 | # Disable delivery errors, bad email addresses will be ignored 52 | # config.action_mailer.raise_delivery_errors = false 53 | 54 | # Enable threaded mode 55 | # config.threadsafe! 56 | 57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 58 | # the I18n.default_locale when a translation can not be found) 59 | config.i18n.fallbacks = true 60 | 61 | # Send deprecation notices to registered listeners 62 | config.active_support.deprecation = :notify 63 | 64 | # Log the query plan for queries taking more than this (works 65 | # with SQLite, MySQL, and PostgreSQL) 66 | # config.active_record.auto_explain_threshold_in_seconds = 0.5 67 | end 68 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Gistie::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Configure static asset server for tests with Cache-Control for performance 11 | config.serve_static_assets = true 12 | config.static_cache_control = "public, max-age=3600" 13 | 14 | # Log error messages when you accidentally call methods on nil 15 | config.whiny_nils = true 16 | 17 | # Show full error reports and disable caching 18 | config.consider_all_requests_local = true 19 | config.action_controller.perform_caching = false 20 | 21 | # Raise exceptions instead of rendering exception templates 22 | config.action_dispatch.show_exceptions = false 23 | 24 | # Disable request forgery protection in test environment 25 | config.action_controller.allow_forgery_protection = false 26 | 27 | # Tell Action Mailer not to deliver emails to the real world. 28 | # The :test delivery method accumulates sent emails in the 29 | # ActionMailer::Base.deliveries array. 30 | config.action_mailer.delivery_method = :test 31 | 32 | # Raise exception on mass assignment protection for Active Record models 33 | config.active_record.mass_assignment_sanitizer = :strict 34 | 35 | # Print deprecation notices to the stderr 36 | config.active_support.deprecation = :stderr 37 | end 38 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | # 12 | # These inflection rules are supported but not enabled by default: 13 | # ActiveSupport::Inflector.inflections do |inflect| 14 | # inflect.acronym 'RESTful' 15 | # end 16 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | Gistie::Application.config.secret_token = '2cee6080a66ddc525a1e7a7a4c00d8932b8aada4f200e454aa5ff72df91af41ba5c294786970ad6138ef7a3cd42b0cf4d5111662bf26a377b7204f0e38d6e849' 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Gistie::Application.config.session_store :cookie_store, key: '_gistie_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # Gistie::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Gistie::Application.routes.draw do 2 | # The priority is based upon order of creation: 3 | # first created -> highest priority. 4 | # 5 | 6 | # match '/gists/:gist_id/blobs/:id/:filename' => 'blobs#show' 7 | 8 | 9 | resources :gists do 10 | scope format: 'text' do 11 | match 'blobs/:id/*filename' => 'blobs#show', as: :raw, filename: /.*/, format: false 12 | end 13 | end 14 | 15 | root :to => 'gists#index' 16 | 17 | # See how all your routes lay out with "rake routes" 18 | 19 | # This is a legacy wild controller route that's not recommended for RESTful applications. 20 | # Note: This route will make all actions in every controller accessible via GET requests. 21 | # match ':controller(/:action(/:id))(.:format)' 22 | end 23 | -------------------------------------------------------------------------------- /db/migrate/20121012110908_create_gists_table.rb: -------------------------------------------------------------------------------- 1 | class CreateGistsTable < ActiveRecord::Migration 2 | def change 3 | create_table :gists do |t| 4 | t.timestamps 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20121101194121_add_gist_description.rb: -------------------------------------------------------------------------------- 1 | class AddGistDescription < ActiveRecord::Migration 2 | def change 3 | add_column :gists, :description, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/lib/assets/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/log/.gitkeep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /repos/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/repos/.gitkeep -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | a second commit with file.txt 2 | 3 | # Please enter the commit message for your changes. Lines starting 4 | # with '#' will be ignored, and an empty message aborts the commit. 5 | # On branch master 6 | # Changes to be committed: 7 | # (use "git reset HEAD ..." to unstage) 8 | # 9 | # new file: file.txt 10 | # 11 | # Untracked files not listed (use -u option to show untracked files) 12 | diff --git a/file.txt b/file.txt 13 | new file mode 100644 14 | index 0000000..82d1aa8 15 | --- /dev/null 16 | +++ b/file.txt 17 | @@ -0,0 +1 @@ 18 | +a file.txt 19 | -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | ignorecase = true 7 | [user] 8 | email = gmarik@gmail.com 9 | name = gmarik 10 | -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | txtred='' # Red 4 | txtrst='' # Reset 5 | 6 | user=$(git config --local user.name) 7 | email=$(git config --local user.email) 8 | 9 | if [ "${user}" == "" -a "${name}" == "" ]; then 10 | echo "${txtred}" 11 | 12 | echo "You don't have user/email configured for this repo, please configure!" 13 | 14 | U=$(git config --global user.name) 15 | E=$(git config --global user.email) 16 | 17 | echo "Using default '$U' <$E>" 18 | 19 | `git config user.email $E` 20 | `git config user.name $U` 21 | 22 | echo ${txtrst} 23 | 24 | exit 1 25 | fi 26 | 27 | -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/spec/fixtures/test_repo.git/index -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/logs/HEAD: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 86709acd61353d08a1d3ff15f7fc8b554c34a378 gmarik 1350361844 -0500 commit (initial): Initial commit 2 | 86709acd61353d08a1d3ff15f7fc8b554c34a378 c18d3d5324e98d7bb2e5e5934e4ef6c3046a7ca1 gmarik 1350361878 -0500 commit: a second commit with file.txt 3 | -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/logs/refs/heads/master: -------------------------------------------------------------------------------- 1 | 0000000000000000000000000000000000000000 86709acd61353d08a1d3ff15f7fc8b554c34a378 gmarik 1350361844 -0500 commit (initial): Initial commit 2 | 86709acd61353d08a1d3ff15f7fc8b554c34a378 c18d3d5324e98d7bb2e5e5934e4ef6c3046a7ca1 gmarik 1350361878 -0500 commit: a second commit with file.txt 3 | -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/objects/2d/357899dd88b475ca46e627e99883f723da8c8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/spec/fixtures/test_repo.git/objects/2d/357899dd88b475ca46e627e99883f723da8c8c -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/objects/82/d1aa8da25e22d908c44bde9d122fea688ba3a0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/spec/fixtures/test_repo.git/objects/82/d1aa8da25e22d908c44bde9d122fea688ba3a0 -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/objects/86/709acd61353d08a1d3ff15f7fc8b554c34a378: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/spec/fixtures/test_repo.git/objects/86/709acd61353d08a1d3ff15f7fc8b554c34a378 -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/objects/8a/a2aed323077103c91369c23e886c6bae2d987d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/spec/fixtures/test_repo.git/objects/8a/a2aed323077103c91369c23e886c6bae2d987d -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/objects/bb/ae578e76aad577054201545aeacde508b49596: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/spec/fixtures/test_repo.git/objects/bb/ae578e76aad577054201545aeacde508b49596 -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/objects/c1/8d3d5324e98d7bb2e5e5934e4ef6c3046a7ca1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/spec/fixtures/test_repo.git/objects/c1/8d3d5324e98d7bb2e5e5934e4ef6c3046a7ca1 -------------------------------------------------------------------------------- /spec/fixtures/test_repo.git/refs/heads/master: -------------------------------------------------------------------------------- 1 | c18d3d5324e98d7bb2e5e5934e4ef6c3046a7ca1 2 | -------------------------------------------------------------------------------- /spec/integration_spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'capybara/rails' 4 | require 'capybara/rspec' 5 | require 'capybara/poltergeist' 6 | 7 | 8 | # Use capybara-webkit 9 | Capybara.javascript_driver = :poltergeist 10 | 11 | -------------------------------------------------------------------------------- /spec/models/gist_blob_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe GistBlob do 4 | subject(:gist_file) { GistBlob.new } 5 | 6 | describe 'Validations' do 7 | 8 | context "invalid" do 9 | 10 | it {should be_invalid} 11 | it {should have(1).error_on(:blob) } 12 | end 13 | 14 | context "valid" do 15 | subject(:gist_file) { 16 | GistBlob.new(blob: "Holla") 17 | } 18 | 19 | it {should be_valid} 20 | it {should have(0).errors } 21 | end 22 | 23 | end 24 | 25 | describe "Defaults" do 26 | its(:name) { should == 'Text' } 27 | end 28 | 29 | describe ".new" do 30 | 31 | subject(:gist_file) do 32 | GistBlob.new({blob: "Holla", name: "name.md"}) 33 | end 34 | 35 | it { should be_valid } 36 | its(:name) { should == "name.md" } 37 | its(:blob) { should == "Holla" } 38 | 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/models/gist_repo_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe GistRepo do 4 | 5 | def r(*args) 6 | GistRepo.new(*args) 7 | end 8 | 9 | subject { r(fixture_repo_path) } 10 | 11 | describe '.new' do 12 | it 'accepts path' do 13 | r(fixture_repo_path).path. 14 | should == fixture_repo_path 15 | end 16 | end 17 | 18 | its(:path) { should == fixture_repo_path } 19 | its(:repo) { should be_a(Rugged::Repository) } 20 | 21 | it { should be_a(RepoWriter) } 22 | 23 | describe '.named' do 24 | subject(:repo) { GistRepo.named('hello') } 25 | its(:path){ should == GistRepo.repo_path('hello') } 26 | end 27 | 28 | describe '.init_named_repo' do 29 | 30 | it "initializes named repository at given path" do 31 | 32 | Rugged::Repository. 33 | should_receive(:init_at). 34 | with(GistRepo.repo_path('a_name'), true) 35 | 36 | GistRepo.init_named_repo('a_name') 37 | end 38 | 39 | end 40 | 41 | describe '#log' do 42 | 43 | subject(:log) { GistRepo.new(fixture_repo_path).log } 44 | 45 | it { should be_a(RepoLog) } 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/models/gist_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Gist do 4 | 5 | describe 'Validations' do 6 | 7 | context 'blank' do 8 | subject(:blank_gist) { Gist.new } 9 | 10 | it {should be_blank} 11 | it {should be_invalid} 12 | it {should have(1).error_on(:gist_blobs) } 13 | end 14 | 15 | context 'non blank' do 16 | subject(:gist) do 17 | Gist.new(gist_blobs_attributes: [{blob: "holla!"}]) 18 | end 19 | 20 | it { should_not be_blank } 21 | it { should be_valid } 22 | end 23 | 24 | describe 'uniq names' do 25 | subject(:gist) do 26 | gist_blobs = [{ blob: '1', name: '1'}, { blob: '2', name: '1'}] 27 | g = Gist.new(gist_blobs_attributes: gist_blobs) 28 | end 29 | 30 | it { should have(1).errors_on(:gist_blobs) } 31 | end 32 | 33 | describe 'blank blobs' do 34 | subject(:gist) do 35 | gist_blobs = [{ blob: '', name: '1'}, { blob: '', name: '1'}] 36 | g = Gist.new(gist_blobs_attributes: gist_blobs) 37 | end 38 | 39 | it { should have(1).errors_on(:gist_blobs) } 40 | it { should be_blank } 41 | end 42 | 43 | end 44 | 45 | describe '.gist_blobs' do 46 | context 'new Gist with blob_attributes set' do 47 | subject(:gist) do 48 | Gist.new(gist_blobs_attributes: [{blob: "Holla"}]) 49 | end 50 | 51 | it { should be_valid } 52 | it { should have(1).gist_blobs } 53 | end 54 | 55 | context 'new Gist' do 56 | subject(:gist) { Gist.new } 57 | 58 | it { should be_invalid } 59 | it { should have(0).gist_blobs } 60 | end 61 | 62 | end 63 | 64 | describe '.create' do 65 | it 'creates gist' do 66 | lambda do 67 | Gist.create!(gist_blobs_attributes: [{blob: "Holla"}]) 68 | end.should change(Gist, :count).by(1) 69 | end 70 | end 71 | 72 | describe '.init_repo' do 73 | let (:gist) { 74 | g = Gist.new 75 | g.stub!(repo_name: :a_name) 76 | g 77 | } 78 | 79 | it 'initializes repo' do 80 | GistRepo.should_receive(:init_named_repo).with(:a_name) 81 | gist.init_repo 82 | end 83 | 84 | end 85 | 86 | # TODO: doesn't belong here 87 | describe '.dup_names' do 88 | let(:gist_blob) { {name: 'file', blob: 'holla!'} } 89 | subject(:gist) { Gist.new(gist_blobs_attributes: [gist_blob, gist_blob]) } 90 | 91 | it 'returns duplicates' do 92 | subject.send(:dup_names, gist.gist_blobs).should == [['file', 2]] 93 | end 94 | end 95 | 96 | # TODO: refactor 97 | # - violates single responsibility 98 | # - tests for return values and integration with SaveGist 99 | describe '.save_and_commit!' do 100 | let (:gist) { Gist.new } 101 | 102 | context 'valid record' do 103 | subject(:gist) 104 | 105 | before :each do 106 | SaveGist.should_receive(:new).with(gist). 107 | and_return(save = mock(:save)) 108 | 109 | save.should_receive(:call). 110 | and_return(gist) 111 | end 112 | 113 | it 'raises no error' do 114 | -> { gist.save_and_commit! }.should_not raise_error 115 | end 116 | 117 | its(:save_and_commit) { should be_true } 118 | end 119 | 120 | 121 | context 'invalid record' do 122 | subject { gist } 123 | it { should be_invalid } 124 | 125 | its(:save_and_commit) { should be_false } 126 | its('save_and_commit!') do 127 | proc { subject.save_and_commit! }. 128 | should raise_error(ActiveRecord::RecordInvalid) 129 | end 130 | end 131 | end 132 | 133 | 134 | describe 'destroy_with_repo' do 135 | it 'destroys record' 136 | it 'removes repository' 137 | end 138 | end 139 | -------------------------------------------------------------------------------- /spec/models/highlighed_source_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe HighlightedSource do 4 | 5 | subject(:code) { 6 | HighlightedSource.new('test.rb', <<-RUBY) 7 | puts 'hi' 8 | puts 'world' 9 | RUBY 10 | } 11 | 12 | 13 | it "highlights code" do 14 | subject.to_highlighted_html.should == <<-HTML.strip_heredoc.chomp 15 |
puts 'hi'
puts 'world'
16 | HTML 17 | end 18 | 19 | 20 | it "highlights numbered code " do 21 | subject.to_formatted_html.should == <<-HTML.strip_heredoc 22 | 23 | 24 | 25 | 29 | 32 | 33 | 34 |
26 |
1
27 | 2
28 |
30 |
puts 'hi'
puts 'world'
31 |
35 | HTML 36 | end 37 | end 38 | 39 | 40 | -------------------------------------------------------------------------------- /spec/models/repo_log_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RepoLog do 4 | 5 | let(:log) { RepoLog.new(fixture_repo) } 6 | subject { log.to_a } 7 | 8 | its(:size) { should > 0 } 9 | 10 | describe RepoLog::Commit do 11 | 12 | subject { log.first } 13 | 14 | its(:sha) { should == 'c18d3d5324e98d7bb2e5e5934e4ef6c3046a7ca1' } 15 | its(:pretty_sha) { should == 'c18d3d532' } 16 | its(:created_at) { should == Time.parse('2012-10-15 23:31:18 -0500') } 17 | 18 | end 19 | 20 | describe 'acts as enum' do 21 | it 'limits with take' do 22 | log.take(1).map(&:pretty_sha). 23 | should == ['c18d3d532'] 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/models/repo_tree_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | 4 | describe RepoTree do 5 | 6 | let(:repo) { RepoTree.new(fixture_repo, 'c18d3d5324e98d7bb2e5e5934e4ef6c3046a7ca1') } 7 | subject { repo } 8 | 9 | its(:repo) { should be_a(Rugged::Repository) } 10 | its(:head) { should == 'c18d3d5324e98d7bb2e5e5934e4ef6c3046a7ca1' } 11 | 12 | it 'has entries' do 13 | repo.to_a.size.should == 2 14 | end 15 | 16 | describe RepoTree::Blob do 17 | 18 | subject { repo.to_a.first } 19 | 20 | it { should be_a(RepoTree::Blob) } 21 | its(:name) { should == 'README.md' } 22 | its(:type) { should == :blob } 23 | its(:filemode) { should == 33188 } 24 | its(:oid) { should == '8aa2aed323077103c91369c23e886c6bae2d987d' } 25 | its(:content) { should == "# A README.md\n" } 26 | end 27 | 28 | 29 | 30 | end 31 | -------------------------------------------------------------------------------- /spec/models/repo_writer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RepoWriter do 4 | 5 | let(:repo_writer) { 6 | Struct.new(:repo) do 7 | include RepoWriter 8 | end.new(temp_repo) 9 | } 10 | 11 | let(:author) { 12 | { 13 | email: "Gistie@Gistie.com", 14 | # important to fix the time as it causes commit SHA to change 15 | time: '2012.10.10 18:00:01'.to_time, 16 | name: "Gistie" 17 | } 18 | } 19 | 20 | let(:a_blob) { mock(blob: "Holla!", name: "README.txt") } 21 | 22 | it { repo_writer.should respond_to(:repo) } 23 | 24 | context 'duplicate commit' do 25 | let(:write) do 26 | -> { repo_writer.write([a_blob]) } 27 | end 28 | 29 | it "raises NothingToCommitError" do 30 | write.() 31 | 32 | -> { write.() }. 33 | should raise_error(RepoWriter::NothingToCommitError) 34 | end 35 | end 36 | 37 | describe '.write_blob' do 38 | it "writes blob" do 39 | repo_writer.write_blob("Holla!"). 40 | should == 'cb05ffd087a6e689a3c465b8bab953d3ff60a3df' 41 | end 42 | end 43 | 44 | describe '.write_blobs' do 45 | it "writes blobs" do 46 | repo_writer.write_blobs([a_blob]). 47 | should == [['README.txt', 'cb05ffd087a6e689a3c465b8bab953d3ff60a3df']] 48 | end 49 | end 50 | 51 | describe '.write_tree' do 52 | it "writes tree repo" do 53 | named_sha1s = repo_writer.write_blobs([a_blob]) 54 | repo_writer.write_tree(named_sha1s). 55 | should == '7edf19d1b0fcb86bdbf38292202bec299321fea3' 56 | end 57 | end 58 | 59 | describe '.write_commit' do 60 | it "writes commit" do 61 | named_sha1s = repo_writer.write_blobs([a_blob]) 62 | toid = repo_writer.write_tree(named_sha1s) 63 | repo_writer.write_commit(toid, author). 64 | should == 'a5772b04cc0f61af73868f36a90e2e669c402e78' 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /spec/models/save_gist_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SaveGist do 4 | let(:gist) do 5 | gist_blobs = [{ blob: 'Holla'}] 6 | Gist.new(gist_blobs_attributes: gist_blobs) 7 | end 8 | 9 | let(:save) { SaveGist.new(gist) } 10 | 11 | let(:save_no_write) { 12 | gist.should_receive(:gist_write) 13 | save 14 | } 15 | 16 | context "create" do 17 | 18 | let(:create) { 19 | save_no_write 20 | } 21 | 22 | it "creates Gist" do 23 | create.should change(Gist, :count).by(1) 24 | end 25 | 26 | it "initializes repo" do 27 | create.should change(gist, :repo).from(nil) 28 | 29 | gist.repo.should be_a(GistRepo) 30 | end 31 | end 32 | 33 | context "update" do 34 | 35 | let(:update) { 36 | gist.stub!(new_record?: false) 37 | save_no_write 38 | } 39 | 40 | it "updates attributes" do 41 | blob = -> { gist.gist_blobs.first.blob } 42 | -> { 43 | update.(gist_blobs_attributes: [blob: "hi"]) 44 | }.should change(&blob). 45 | from("Holla").to("hi") 46 | end 47 | 48 | it "doesn't reinitialize repo" do 49 | gist.should_not_receive(:init_repo) 50 | update.() 51 | end 52 | end 53 | 54 | it "returns gist" do 55 | save_no_write.().should be_valid 56 | end 57 | 58 | 59 | 60 | # This exception is to prevent empty commits 61 | # so when Gist decription gets updated it has to be ignored 62 | context "NothingToCommit" do 63 | 64 | let(:gist) { 65 | mock(:gist, 66 | assign_attributes: true, 67 | new_record?: true, 68 | save!: true, 69 | init_repo: true, 70 | ) 71 | } 72 | 73 | 74 | let(:update) { 75 | gist.should_receive(:transaction).and_yield 76 | gist.should_receive(:gist_write).and_raise(RepoWriter::NothingToCommitError) 77 | SaveGist.new(gist) 78 | } 79 | 80 | it "ignores exception" do 81 | -> { update.() }.should_not raise_error 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/requests/gists_spec.rb: -------------------------------------------------------------------------------- 1 | require 'integration_spec_helper' 2 | 3 | describe GistsController do 4 | 5 | let(:blob_attrs) { {name: 'file.txt', blob: 'something'} } 6 | let(:gist) { Gist.new(gist_blobs_attributes: [blob_attrs]) } 7 | 8 | let(:name_input) { 'gist[gist_blobs_attributes][][name]' } 9 | let(:blob_input) { 'gist[gist_blobs_attributes][][blob]' } 10 | 11 | def create_gist 12 | g = gist.save_and_commit!; g 13 | end 14 | 15 | def fill_gist(name = 'name.md', blob = '#Hello world') 16 | page.fill_in(name_input, with: name) 17 | page.fill_in(blob_input, with: blob) 18 | end 19 | 20 | subject {page} 21 | 22 | describe '.index' do 23 | let(:created_gist) { create_gist } 24 | before(:each) { 25 | created_gist # trigger creation 26 | visit gists_path 27 | } 28 | 29 | it { should have_selector(".gist[data-gist_id='#{created_gist.id}']") } 30 | end 31 | 32 | describe '.show' do 33 | let(:a_gist) { create_gist } 34 | let(:a_blob) { a_gist.gist_blobs.first } 35 | 36 | before(:each) { visit gist_path(a_gist) } 37 | 38 | it { should have_selector("div", :text => a_blob.name) } 39 | xit { should have_selector("data.syntax", :text => a_blob.blob) } 40 | end 41 | 42 | describe '.new' do 43 | before(:each) { visit new_gist_path } 44 | 45 | it { should have_selector("input[name='#{name_input}']") } 46 | it { should have_selector("textarea[name='#{blob_input}']") } 47 | end 48 | 49 | describe '.create' do 50 | before :each do 51 | visit new_gist_path 52 | fill_gist 53 | page.click_button('Create Gist') 54 | end 55 | 56 | it { should have_content('Gist was successfully created') } 57 | it { should have_content('#Hello world') } 58 | it { should have_content('name.md') } 59 | end 60 | 61 | 62 | describe '.edit' do 63 | let(:a_gist) { create_gist } 64 | let(:a_blob) { a_gist.gist_blobs.first } 65 | 66 | before(:each) { visit edit_gist_path(a_gist) } 67 | 68 | it { should have_selector("input[name*=name]", value: a_blob.name ) } 69 | it { should have_selector("textarea[name*=blob]", text: a_blob.blob) } 70 | end 71 | 72 | describe '.update' do 73 | context 'with invalid input' do 74 | xit { should have_content('validation failed') } 75 | end 76 | 77 | context 'with valid input' do 78 | before :each do 79 | gist = create_gist 80 | visit edit_gist_path(gist) 81 | fill_gist('new_name.txt', blob = 'hey') 82 | page.click_button('Update Gist') 83 | end 84 | 85 | it { should have_content('Gist was successfully updated') } 86 | it { should have_content('hey') } 87 | it { should have_content('new_name.txt') } 88 | end 89 | end 90 | 91 | describe '.destroy' do 92 | before :each do 93 | gist = create_gist 94 | visit gist_path(gist) 95 | 96 | page.click_link('Destroy') 97 | end 98 | 99 | it { should have_content('Gist was successfully deleted') } 100 | end 101 | 102 | end 103 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] ||= 'test' 2 | require File.expand_path("../../config/environment", __FILE__) 3 | require 'rspec/rails' 4 | require 'rspec/autorun' 5 | 6 | # Requires supporting ruby files with custom matchers and macros, etc, 7 | # in spec/support/ and its subdirectories. 8 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 9 | 10 | def temp_repo 11 | dir = Dir.mktmpdir 'dir' 12 | Rugged::Repository.init_at(dir, true) 13 | end 14 | 15 | def fixture_repo_path(repo_name = 'test_repo') 16 | Rails.root.join('spec/fixtures/').join(repo_name + '.git/').to_s 17 | end 18 | 19 | def fixture_repo 20 | Rugged::Repository.new(fixture_repo_path) 21 | end 22 | 23 | Cleanup = ->(spec) do 24 | git_repos = Rails.configuration.repo_root + '*.git/' 25 | Dir[git_repos].each do |dir| 26 | FileUtils.rm_rf(dir) 27 | end 28 | end 29 | 30 | # Set root 31 | # Rails.configuration.repo_root = Dir.mktmpdir('test_dir') 32 | 33 | RSpec.configure do |config| 34 | config.mock_with :rspec 35 | # TODO: 36 | # config.mock_with :rr 37 | 38 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 39 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 40 | 41 | # cleanup after 42 | # TODO: refactor into more granular control 43 | # as this is a sign of a mess 44 | config.after(:each, &Cleanup) 45 | 46 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 47 | # examples within a transaction, remove the following line or assign false 48 | # instead of true. 49 | config.use_transactional_fixtures = true 50 | 51 | # If true, the base class of anonymous controllers will be inferred 52 | # automatically. This will be the default behavior in future versions of 53 | # rspec-rails. 54 | config.infer_base_class_for_anonymous_controllers = false 55 | 56 | # Run specs in random order to surface order dependencies. If you find an 57 | # order dependency and want to debug it, you can fix the order by providing 58 | # the seed, which is printed after each run. 59 | # --seed 1234 60 | config.order = "random" 61 | end 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/vendor/assets/javascripts/.gitkeep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/vendor/assets/stylesheets/.gitkeep -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarik/Gistie/1f94a80dcc4614392e96edcb56f8db9570a52850/vendor/plugins/.gitkeep --------------------------------------------------------------------------------