├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.rdoc ├── Rakefile ├── app ├── assets │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── application.js.coffee │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── comment.js.coffee │ │ │ ├── comment_box.js.coffee │ │ │ ├── comment_form.js.coffee │ │ │ ├── comment_list.js.coffee │ │ │ ├── comment_notification_list.js.coffee │ │ │ ├── markdown_editor.js.coffee │ │ │ ├── post_favorite_button.js.coffee │ │ │ └── post_rating_buttons.js.coffee │ │ └── routes.js.erb │ └── stylesheets │ │ ├── application.css │ │ ├── backend.css │ │ ├── backend │ │ └── main.css.sass │ │ ├── frontend │ │ └── main.css.sass │ │ └── posts.scss ├── controllers │ ├── admin │ │ ├── application_controller.rb │ │ ├── main_controller.rb │ │ └── post_versions_controller.rb │ ├── application_controller.rb │ ├── comments_controller.rb │ ├── concerns │ │ └── .keep │ ├── password_resets_controller.rb │ ├── post_versions_controller.rb │ ├── posts_controller.rb │ ├── sessions_controller.rb │ └── users_controller.rb ├── helpers │ ├── admin │ │ └── application_helper.rb │ ├── application_helper.rb │ └── posts_helper.rb ├── mailers │ ├── .keep │ ├── application_mailer.rb │ └── user_mailer.rb ├── models │ ├── .keep │ ├── comment.rb │ ├── concerns │ │ └── .keep │ ├── post.rb │ ├── post_version.rb │ ├── post_vote.rb │ └── user.rb ├── serializers │ ├── comment_serializer.rb │ ├── post_serializer.rb │ └── user_front_data_serializer.rb ├── services │ └── markdown_parser.rb └── views │ ├── _vue_templates.html.haml │ ├── admin │ ├── main │ │ └── index.html.haml │ └── post_versions │ │ ├── edit_decline.html.haml │ │ ├── index.html.haml │ │ └── show.html.haml │ ├── layouts │ ├── admin.html.haml │ ├── application.html.haml │ └── mailer.html.haml │ ├── password_resets │ ├── edit.html.haml │ └── new.html.haml │ ├── post_versions │ ├── edit.html.haml │ └── show.html.haml │ ├── posts │ ├── edit.html.haml │ ├── index.html.haml │ ├── new.html.haml │ ├── posts_list.html.haml │ └── show.html.haml │ ├── sessions │ ├── _form.html.haml │ └── new.html.haml │ ├── shared │ ├── _flash_messages.html.haml │ ├── _menu.html.haml │ └── _post_markdown_editor.html.haml │ ├── user_mailer │ ├── activation_needed_email.html.haml │ ├── activation_success_email.html.haml │ └── reset_password_email.html.haml │ ├── users │ ├── _form.html.haml │ ├── edit.html.haml │ ├── edit.html.haml~ │ └── new.html.haml │ └── vue_templates │ ├── comments │ ├── _comment_box_tpl.html.haml │ ├── _comment_form_tpl.html.haml │ ├── _comment_list_tpl.html.haml │ ├── _comment_notification_list_tpl.html.haml │ └── _comment_tpl.html.haml │ └── shared │ ├── _post_favorite_button_tpl.html.haml │ └── _post_rating_buttons_tpl.html.haml ├── bin ├── bundle ├── rails ├── rake └── setup ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml.example ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── serializer_config.rb │ ├── session_store.rb │ ├── simple_form.rb │ ├── sorcery.rb │ └── wrap_parameters.rb ├── locales │ ├── en.yml │ └── simple_form.en.yml ├── routes.rb └── secrets.yml.example ├── db ├── migrate │ ├── 20151031194302_create_posts.rb │ ├── 20151103095347_create_comments.rb │ ├── 20151111154655_sorcery_core.rb │ ├── 20151111154656_sorcery_remember_me.rb │ ├── 20151112094518_sorcery_user_activation.rb │ ├── 20151112123948_add_is_admin_to_users.rb │ ├── 20151112210217_sorcery_reset_password.rb │ ├── 20151114124649_change_users_fields.rb │ ├── 20151203181509_add_tree_to_comments.rb │ ├── 20160120135250_change_posts_fields.rb │ ├── 20160121170807_remove_source_from_posts.rb │ ├── 20160125104823_add_favorite_posts_to_users.rb │ ├── 20160126092912_add_user_ref_to_posts.rb │ ├── 20160128113543_create_post_votes.rb │ ├── 20160204091437_create_post_versions.rb │ ├── 20160204092842_add_post_version_ref_to_posts.rb │ ├── 20160204125131_add_inherit_version_id_to_post_versions.rb │ └── 20160204171926_remove_fields_from_posts.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep ├── tasks │ └── .keep └── templates │ └── haml │ └── scaffold │ └── _form.html.haml ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico └── robots.txt ├── spec ├── factories │ └── common.rb ├── models │ ├── post_spec.rb │ ├── post_version_spec.rb │ └── user_spec.rb ├── rails_helper.rb ├── services │ └── markdown_parser_spec.rb └── spec_helper.rb └── vendor └── assets ├── javascripts ├── .keep ├── notify.min.js └── vue-strap.min.js └── stylesheets └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-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 all logfiles and tempfiles. 11 | /log/* 12 | !/log/.keep 13 | /tmp 14 | 15 | /.idea 16 | /public/system 17 | /public/assets 18 | 19 | database.yml 20 | secrets.yml 21 | 22 | # Ignore dotfiles 23 | .ruby-version 24 | .ruby-gemset 25 | .projectile 26 | .vagrant 27 | .rspec -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | source 'https://rails-assets.org' do 4 | gem 'rails-assets-vue' 5 | gem 'rails-assets-underscore' 6 | gem 'rails-assets-vue-resource' 7 | gem 'rails-assets-autosize' 8 | end 9 | 10 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 11 | gem 'rails', '4.2.5.1' 12 | # Use postgresql as the database for Active Record 13 | gem 'pg' 14 | # Use SCSS for stylesheets 15 | gem 'sass-rails', '~> 5.0' 16 | gem 'bootstrap-sass' 17 | gem 'haml-rails' 18 | gem 'js-routes' 19 | # Use Uglifier as compressor for JavaScript assets 20 | gem 'uglifier', '>= 1.3.0' 21 | # Use CoffeeScript for .coffee assets and views 22 | gem 'coffee-rails', '~> 4.1.0' 23 | # See https://github.com/rails/execjs#readme for more supported runtimes 24 | # gem 'therubyracer', platforms: :ruby 25 | 26 | gem 'sorcery' 27 | gem 'gon' 28 | gem 'active_model_serializers' 29 | gem 'simple_form' 30 | 31 | gem 'redcarpet' 32 | gem 'aasm' 33 | # Use jquery as the JavaScript library 34 | gem 'jquery-rails' 35 | 36 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 37 | gem 'jbuilder', '~> 2.0' 38 | # bundle exec rake doc:rails generates the API under doc/api. 39 | gem 'sdoc', '~> 0.4.0', group: :doc 40 | 41 | # Use ActiveModel has_secure_password 42 | # gem 'bcrypt', '~> 3.1.7' 43 | 44 | # Use Unicorn as the app server 45 | # gem 'unicorn' 46 | 47 | # Use Capistrano for deployment 48 | # gem 'capistrano-rails', group: :development 49 | 50 | group :development, :test do 51 | gem 'pry-rails' 52 | gem 'rspec-rails' 53 | gem 'factory_girl_rails' 54 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 55 | gem 'byebug' 56 | gem 'letter_opener' 57 | end 58 | 59 | group :development do 60 | # Access an IRB console on exception pages or by using <%= console %> in views 61 | gem 'web-console', '~> 2.0' 62 | 63 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 64 | gem 'spring' 65 | end 66 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | remote: https://rails-assets.org/ 4 | specs: 5 | aasm (4.7.0) 6 | actionmailer (4.2.5.1) 7 | actionpack (= 4.2.5.1) 8 | actionview (= 4.2.5.1) 9 | activejob (= 4.2.5.1) 10 | mail (~> 2.5, >= 2.5.4) 11 | rails-dom-testing (~> 1.0, >= 1.0.5) 12 | actionpack (4.2.5.1) 13 | actionview (= 4.2.5.1) 14 | activesupport (= 4.2.5.1) 15 | rack (~> 1.6) 16 | rack-test (~> 0.6.2) 17 | rails-dom-testing (~> 1.0, >= 1.0.5) 18 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 19 | actionview (4.2.5.1) 20 | activesupport (= 4.2.5.1) 21 | builder (~> 3.1) 22 | erubis (~> 2.7.0) 23 | rails-dom-testing (~> 1.0, >= 1.0.5) 24 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 25 | active_model_serializers (0.9.4) 26 | activemodel (>= 3.2) 27 | activejob (4.2.5.1) 28 | activesupport (= 4.2.5.1) 29 | globalid (>= 0.3.0) 30 | activemodel (4.2.5.1) 31 | activesupport (= 4.2.5.1) 32 | builder (~> 3.1) 33 | activerecord (4.2.5.1) 34 | activemodel (= 4.2.5.1) 35 | activesupport (= 4.2.5.1) 36 | arel (~> 6.0) 37 | activesupport (4.2.5.1) 38 | i18n (~> 0.7) 39 | json (~> 1.7, >= 1.7.7) 40 | minitest (~> 5.1) 41 | thread_safe (~> 0.3, >= 0.3.4) 42 | tzinfo (~> 1.1) 43 | addressable (2.4.0) 44 | arel (6.0.3) 45 | autoprefixer-rails (6.3.1) 46 | execjs 47 | json 48 | bcrypt (3.1.10) 49 | binding_of_caller (0.7.2) 50 | debug_inspector (>= 0.0.1) 51 | bootstrap-sass (3.3.6) 52 | autoprefixer-rails (>= 5.2.1) 53 | sass (>= 3.3.4) 54 | builder (3.2.2) 55 | byebug (8.2.1) 56 | coderay (1.1.0) 57 | coffee-rails (4.1.1) 58 | coffee-script (>= 2.2.0) 59 | railties (>= 4.0.0, < 5.1.x) 60 | coffee-script (2.4.1) 61 | coffee-script-source 62 | execjs 63 | coffee-script-source (1.10.0) 64 | concurrent-ruby (1.0.0) 65 | debug_inspector (0.0.2) 66 | diff-lcs (1.2.5) 67 | erubis (2.7.0) 68 | execjs (2.6.0) 69 | factory_girl (4.5.0) 70 | activesupport (>= 3.0.0) 71 | factory_girl_rails (4.5.0) 72 | factory_girl (~> 4.5.0) 73 | railties (>= 3.0.0) 74 | faraday (0.9.2) 75 | multipart-post (>= 1.2, < 3) 76 | globalid (0.3.6) 77 | activesupport (>= 4.1.0) 78 | gon (6.0.1) 79 | actionpack (>= 3.0) 80 | json 81 | multi_json 82 | request_store (>= 1.0) 83 | haml (4.0.7) 84 | tilt 85 | haml-rails (0.9.0) 86 | actionpack (>= 4.0.1) 87 | activesupport (>= 4.0.1) 88 | haml (>= 4.0.6, < 5.0) 89 | html2haml (>= 1.0.1) 90 | railties (>= 4.0.1) 91 | html2haml (2.0.0) 92 | erubis (~> 2.7.0) 93 | haml (~> 4.0.0) 94 | nokogiri (~> 1.6.0) 95 | ruby_parser (~> 3.5) 96 | i18n (0.7.0) 97 | jbuilder (2.4.0) 98 | activesupport (>= 3.0.0, < 5.1) 99 | multi_json (~> 1.2) 100 | jquery-rails (4.1.0) 101 | rails-dom-testing (~> 1.0) 102 | railties (>= 4.2.0) 103 | thor (>= 0.14, < 2.0) 104 | js-routes (1.2.2) 105 | railties (>= 3.2) 106 | sprockets-rails 107 | json (1.8.3) 108 | jwt (1.5.2) 109 | launchy (2.4.3) 110 | addressable (~> 2.3) 111 | letter_opener (1.4.1) 112 | launchy (~> 2.2) 113 | loofah (2.0.3) 114 | nokogiri (>= 1.5.9) 115 | mail (2.6.3) 116 | mime-types (>= 1.16, < 3) 117 | method_source (0.8.2) 118 | mime-types (2.99) 119 | mini_portile2 (2.0.0) 120 | minitest (5.8.4) 121 | multi_json (1.11.2) 122 | multi_xml (0.5.5) 123 | multipart-post (2.0.0) 124 | nokogiri (1.6.7.2) 125 | mini_portile2 (~> 2.0.0.rc2) 126 | oauth (0.4.7) 127 | oauth2 (1.0.0) 128 | faraday (>= 0.8, < 0.10) 129 | jwt (~> 1.0) 130 | multi_json (~> 1.3) 131 | multi_xml (~> 0.5) 132 | rack (~> 1.2) 133 | pg (0.18.4) 134 | pry (0.10.3) 135 | coderay (~> 1.1.0) 136 | method_source (~> 0.8.1) 137 | slop (~> 3.4) 138 | pry-rails (0.3.4) 139 | pry (>= 0.9.10) 140 | rack (1.6.4) 141 | rack-test (0.6.3) 142 | rack (>= 1.0) 143 | rails (4.2.5.1) 144 | actionmailer (= 4.2.5.1) 145 | actionpack (= 4.2.5.1) 146 | actionview (= 4.2.5.1) 147 | activejob (= 4.2.5.1) 148 | activemodel (= 4.2.5.1) 149 | activerecord (= 4.2.5.1) 150 | activesupport (= 4.2.5.1) 151 | bundler (>= 1.3.0, < 2.0) 152 | railties (= 4.2.5.1) 153 | sprockets-rails 154 | rails-assets-autosize (3.0.15) 155 | rails-assets-underscore (1.8.3) 156 | rails-assets-vue (1.0.15) 157 | rails-assets-vue-resource (0.7.0) 158 | rails-deprecated_sanitizer (1.0.3) 159 | activesupport (>= 4.2.0.alpha) 160 | rails-dom-testing (1.0.7) 161 | activesupport (>= 4.2.0.beta, < 5.0) 162 | nokogiri (~> 1.6.0) 163 | rails-deprecated_sanitizer (>= 1.0.1) 164 | rails-html-sanitizer (1.0.3) 165 | loofah (~> 2.0) 166 | railties (4.2.5.1) 167 | actionpack (= 4.2.5.1) 168 | activesupport (= 4.2.5.1) 169 | rake (>= 0.8.7) 170 | thor (>= 0.18.1, < 2.0) 171 | rake (10.5.0) 172 | rdoc (4.2.1) 173 | redcarpet (3.3.4) 174 | request_store (1.3.0) 175 | rspec-core (3.4.1) 176 | rspec-support (~> 3.4.0) 177 | rspec-expectations (3.4.0) 178 | diff-lcs (>= 1.2.0, < 2.0) 179 | rspec-support (~> 3.4.0) 180 | rspec-mocks (3.4.1) 181 | diff-lcs (>= 1.2.0, < 2.0) 182 | rspec-support (~> 3.4.0) 183 | rspec-rails (3.4.0) 184 | actionpack (>= 3.0, < 4.3) 185 | activesupport (>= 3.0, < 4.3) 186 | railties (>= 3.0, < 4.3) 187 | rspec-core (~> 3.4.0) 188 | rspec-expectations (~> 3.4.0) 189 | rspec-mocks (~> 3.4.0) 190 | rspec-support (~> 3.4.0) 191 | rspec-support (3.4.1) 192 | ruby_parser (3.7.3) 193 | sexp_processor (~> 4.1) 194 | sass (3.4.21) 195 | sass-rails (5.0.4) 196 | railties (>= 4.0.0, < 5.0) 197 | sass (~> 3.1) 198 | sprockets (>= 2.8, < 4.0) 199 | sprockets-rails (>= 2.0, < 4.0) 200 | tilt (>= 1.1, < 3) 201 | sdoc (0.4.1) 202 | json (~> 1.7, >= 1.7.7) 203 | rdoc (~> 4.0) 204 | sexp_processor (4.6.1) 205 | simple_form (3.2.1) 206 | actionpack (> 4, < 5.1) 207 | activemodel (> 4, < 5.1) 208 | slop (3.6.0) 209 | sorcery (0.9.1) 210 | bcrypt (~> 3.1) 211 | oauth (~> 0.4, >= 0.4.4) 212 | oauth2 (>= 0.8.0) 213 | spring (1.6.2) 214 | sprockets (3.5.2) 215 | concurrent-ruby (~> 1.0) 216 | rack (> 1, < 3) 217 | sprockets-rails (3.0.0) 218 | actionpack (>= 4.0) 219 | activesupport (>= 4.0) 220 | sprockets (>= 3.0.0) 221 | thor (0.19.1) 222 | thread_safe (0.3.5) 223 | tilt (2.0.2) 224 | tzinfo (1.2.2) 225 | thread_safe (~> 0.1) 226 | uglifier (2.7.2) 227 | execjs (>= 0.3.0) 228 | json (>= 1.8.0) 229 | web-console (2.2.1) 230 | activemodel (>= 4.0) 231 | binding_of_caller (>= 0.7.2) 232 | railties (>= 4.0) 233 | sprockets-rails (>= 2.0, < 4.0) 234 | 235 | PLATFORMS 236 | ruby 237 | 238 | DEPENDENCIES 239 | aasm 240 | active_model_serializers 241 | bootstrap-sass 242 | byebug 243 | coffee-rails (~> 4.1.0) 244 | factory_girl_rails 245 | gon 246 | haml-rails 247 | jbuilder (~> 2.0) 248 | jquery-rails 249 | js-routes 250 | letter_opener 251 | pg 252 | pry-rails 253 | rails (= 4.2.5.1) 254 | rails-assets-autosize! 255 | rails-assets-underscore! 256 | rails-assets-vue! 257 | rails-assets-vue-resource! 258 | redcarpet 259 | rspec-rails 260 | sass-rails (~> 5.0) 261 | sdoc (~> 0.4.0) 262 | simple_form 263 | sorcery 264 | spring 265 | uglifier (>= 1.3.0) 266 | web-console (~> 2.0) 267 | 268 | BUNDLED WITH 269 | 1.11.2 270 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | Simple Vue.js and Rails Application 4 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/application.js.coffee: -------------------------------------------------------------------------------- 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 any plugin's vendor/assets/javascripts directory 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 | # compiled file. 9 | # 10 | # Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | # about supported directives. 12 | # 13 | #= require jquery 14 | #= require jquery_ujs 15 | #= require vue 16 | #= require vue-resource 17 | #= require underscore 18 | #= require js-routes 19 | #= require autosize 20 | #= require notify.min 21 | #= require_tree . 22 | 23 | $(document).ready -> 24 | Vue.http.headers.common['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') 25 | 26 | window.root_vue = new Vue( 27 | el: 'body' 28 | 29 | data: -> 30 | notifyStylesMap: { 31 | success: 'success' 32 | notice: 'info' 33 | warning: 'warn' 34 | alert: 'error' 35 | } 36 | 37 | methods: 38 | notify: (response_data) -> 39 | noty_style = _.first(_.keys(response_data)) 40 | 41 | $.notify response_data[noty_style], @notifyStylesMap[noty_style] 42 | ) 43 | 44 | # Auto resize textarea 45 | autosize document.querySelectorAll('textarea') 46 | -------------------------------------------------------------------------------- /app/assets/javascripts/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/app/assets/javascripts/components/.gitkeep -------------------------------------------------------------------------------- /app/assets/javascripts/components/comment.js.coffee: -------------------------------------------------------------------------------- 1 | window.Comment = Vue.extend( 2 | template: '#comment-tpl' 3 | 4 | props: 5 | comment: 6 | type: Object 7 | required: true 8 | 9 | data: -> 10 | replied: false 11 | 12 | methods: 13 | showForm: -> 14 | @replied = !@replied 15 | 16 | events: 17 | 'signal:hideForm': -> 18 | @replied = false 19 | ) 20 | 21 | Vue.component('comment', window.Comment) 22 | -------------------------------------------------------------------------------- /app/assets/javascripts/components/comment_box.js.coffee: -------------------------------------------------------------------------------- 1 | window.CommentBox = Vue.extend( 2 | template: '#comment-box-tpl' 3 | 4 | props: 5 | postId: 6 | type: String 7 | required: true 8 | 9 | data: -> 10 | comments: [] 11 | 12 | computed: 13 | postCommentsUrl: -> 14 | Zvample.post_comments_path(@postId) 15 | 16 | commentsQuantity: -> 17 | @comments.length 18 | 19 | ready: -> 20 | @$http.get(@postCommentsUrl).then (response) -> 21 | @comments = response.data 22 | 23 | events: 24 | 'signal:addComment': (child) -> 25 | formData = { comment: { body: child.body, parent_id: child.parentId } } 26 | 27 | @$http.post(@postCommentsUrl, formData).then (response) -> 28 | @$nextTick -> 29 | switch response.data.status 30 | when 422 31 | child.$set('notifications', response.data.errors) 32 | else 33 | @comments = response.data 34 | child.$set('body', undefined) 35 | @$broadcast('signal:hideForm') 36 | ) 37 | 38 | Vue.component('comment-box', window.CommentBox) 39 | -------------------------------------------------------------------------------- /app/assets/javascripts/components/comment_form.js.coffee: -------------------------------------------------------------------------------- 1 | window.CommentForm = Vue.extend( 2 | template: '#comment-form-tpl' 3 | 4 | props: 5 | parentId: 6 | type: Number 7 | default: undefined 8 | 9 | data: -> 10 | body: "" # TODO: do NOT assign text field to undefined!! 11 | notifications: [] 12 | loggedIn: gon.is_logged_in 13 | isUserBanned: gon.is_user_banned 14 | 15 | methods: 16 | addComment: (e) -> 17 | if this.loggedIn and !this.isUserBanned 18 | @$dispatch('signal:addComment', @) 19 | ) 20 | 21 | Vue.component('comment-form', window.CommentForm) 22 | -------------------------------------------------------------------------------- /app/assets/javascripts/components/comment_list.js.coffee: -------------------------------------------------------------------------------- 1 | window.CommentList = Vue.extend( 2 | template: '#comment-list-tpl' 3 | 4 | data: -> 5 | maxCommentDepth: 5 6 | 7 | props: ['comments'] 8 | ) 9 | 10 | Vue.component('comment-list', window.CommentList) 11 | -------------------------------------------------------------------------------- /app/assets/javascripts/components/comment_notification_list.js.coffee: -------------------------------------------------------------------------------- 1 | window.CommentNotificationList = Vue.extend( 2 | template: '#comment-notification-list-tpl' 3 | 4 | props: ['notifications'] 5 | ) 6 | 7 | Vue.component('comment-notification-list', window.CommentNotificationList) 8 | -------------------------------------------------------------------------------- /app/assets/javascripts/components/markdown_editor.js.coffee: -------------------------------------------------------------------------------- 1 | window.MarkdownEditor = Vue.extend( 2 | template: '#markdown-editor-tpl' 3 | 4 | data: -> 5 | body: '' 6 | html_data: undefined 7 | 8 | methods: 9 | preview: -> 10 | @$http.post(Zvample.preview_posts_path(), { body: @body }).then (response) => 11 | @html_data = response.data 12 | ) 13 | 14 | Vue.component('markdown-editor', window.MarkdownEditor) 15 | -------------------------------------------------------------------------------- /app/assets/javascripts/components/post_favorite_button.js.coffee: -------------------------------------------------------------------------------- 1 | window.PostFavoriteButton = Vue.extend( 2 | template: '#post-favorite-button-tpl' 3 | 4 | props: 5 | post: 6 | type: Object 7 | coerce: (val) -> 8 | JSON.parse(val) 9 | 10 | data: -> 11 | quantity: 12 | @post.favoritesQuantity 13 | isEmpty: 14 | @post.isEmpty 15 | 16 | methods: 17 | add_to_favorites: -> 18 | @$http.put(Zvample.add_to_favorites_posts_path(), {id: @post.id}).then (response) -> 19 | if gon.is_logged_in 20 | @$nextTick -> 21 | @isEmpty = response.data.status 22 | @quantity = response.data.quantity 23 | 24 | root_vue.notify(response.data) 25 | ) 26 | 27 | Vue.component('post-favorite-button', window.PostFavoriteButton) 28 | -------------------------------------------------------------------------------- /app/assets/javascripts/components/post_rating_buttons.js.coffee: -------------------------------------------------------------------------------- 1 | window.PostRatingButtons = Vue.extend( 2 | template: '#post-rating-buttons-tpl' 3 | 4 | props: 5 | post: 6 | type: Object 7 | coerce: (val) -> 8 | JSON.parse(val) 9 | 10 | data: -> 11 | quantity: 12 | @post.votesQuantity 13 | 14 | isVoted: 15 | @post.isVoted 16 | 17 | defaultRate: 18 | @post.defaultRate 19 | 20 | userPost: 21 | @post.userPost 22 | 23 | ratesMap: 24 | { 25 | '-1': 'minus', 26 | '0': 'eye-open', 27 | '1': 'plus' 28 | } 29 | 30 | methods: 31 | vote: (rate) -> 32 | return if @post.userPost 33 | 34 | params = { id: @post.id, rate: rate } 35 | 36 | @$http.put(Zvample.vote_posts_path(), params).then (response) -> 37 | if gon.is_logged_in 38 | @$nextTick -> 39 | @quantity = response.data.quantity 40 | @isVoted = true 41 | @defaultRate = rate 42 | 43 | root_vue.notify(response.data) 44 | ) 45 | 46 | Vue.component('post-rating-buttons', window.PostRatingButtons) 47 | -------------------------------------------------------------------------------- /app/assets/javascripts/routes.js.erb: -------------------------------------------------------------------------------- 1 | <%= JsRoutes.generate(namespace: "Zvample") %> 2 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | *= require ./frontend/main 3 | */ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/backend.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 ./backend 13 | */ 14 | 15 | -------------------------------------------------------------------------------- /app/assets/stylesheets/backend/main.css.sass: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets" 2 | @import "bootstrap" 3 | 4 | .post-panel 5 | @extend .panel-default 6 | 7 | &.panel-body 8 | @extend .panel-body 9 | padding: 3em 10 | 11 | .decline-hint 12 | small 13 | background-color: rgba(178, 227, 220, 1) 14 | -------------------------------------------------------------------------------- /app/assets/stylesheets/frontend/main.css.sass: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets" 2 | @import "bootstrap" 3 | 4 | @media (min-width: 1200px) 5 | .container 6 | width: 970px 7 | 8 | .postForm 9 | padding: 10px 10 | 11 | .login-buttons 12 | margin-right: 0.3em 13 | 14 | .nestedComment 15 | padding-left: 50px 16 | 17 | .comment-header 18 | margin: 1.5em 0 0.5em 2em 19 | 20 | .top-line 21 | padding: 5px 0 5px 0 22 | 23 | .comment-body 24 | margin-left: 3em 25 | 26 | .label-green 27 | @extend .label 28 | background: rgba(73, 138, 7, 0.75) 29 | 30 | .comment-actions 31 | margin-left: 3em 32 | 33 | .reply 34 | a 35 | font-size: 0.9em 36 | 37 | .favorite-button 38 | font-size: 1.3em 39 | 40 | .format-button 41 | border: 1px solid #ccc 42 | background-color: transparent 43 | 44 | .favorites-quantity 45 | font-weight: bold 46 | 47 | .rating-icons 48 | @extend .glyphicon 49 | 50 | &.plus 51 | @extend .glyphicon-plus 52 | color: #5cb85c 53 | 54 | &.minus 55 | @extend .glyphicon-minus 56 | color: #d9534f 57 | 58 | &.eye-open 59 | @extend .glyphicon-eye-open 60 | color: #777 61 | 62 | .comment-notifications 63 | @extend .alert 64 | 65 | &.error 66 | @extend .alert-danger 67 | padding: 1em 68 | -------------------------------------------------------------------------------- /app/assets/stylesheets/posts.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Posts controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/controllers/admin/application_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::ApplicationController < ApplicationController 2 | skip_before_filter :require_login 3 | before_filter :admin_only! 4 | 5 | layout 'admin' 6 | 7 | private 8 | 9 | def admin_only! 10 | return if logged_in? && current_user.is_admin? 11 | redirect_to root_url 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/admin/main_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class MainController < Admin::ApplicationController 3 | def index 4 | 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/admin/post_versions_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class PostVersionsController < Admin::ApplicationController 3 | before_filter :find_post_version!, only: [:show, :approve, :decline, :edit_decline] 4 | 5 | def index 6 | @post_versions = PostVersion.where.not(aasm_state: :draft).order(created_at: :desc) 7 | end 8 | 9 | def show; end 10 | 11 | def approve 12 | if @post_version.approve! 13 | redirect_to admin_post_versions_path 14 | end 15 | end 16 | 17 | def decline 18 | @post_version.assign_attributes(post_version_params) 19 | 20 | if @post_version.decline! 21 | redirect_to admin_post_versions_path 22 | else 23 | render :edit_decline 24 | end 25 | end 26 | 27 | def edit_decline; end 28 | 29 | private 30 | 31 | def find_post_version! 32 | @post_version = PostVersion.find(params[:id]) 33 | end 34 | 35 | def post_version_params 36 | params.require(:post_version).permit(:decline_reason) 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | before_filter :init_js_data 3 | before_filter :require_login 4 | 5 | # Prevent CSRF attacks by raising an exception. 6 | # For APIs, you may want to use :null_session instead. 7 | protect_from_forgery with: :exception 8 | 9 | private 10 | 11 | def init_js_data 12 | gon.is_logged_in = logged_in? 13 | 14 | if logged_in? 15 | gon.current_user = UserFrontDataSerializer.new(current_user) 16 | gon.is_user_banned = !!current_user.deleted_at? 17 | end 18 | end 19 | 20 | def not_authenticated 21 | render json: { alert: t('ui.small_notifications.basic.authentication_error') } 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/comments_controller.rb: -------------------------------------------------------------------------------- 1 | class CommentsController < ApplicationController 2 | skip_before_filter :require_login, only: [:index] 3 | 4 | def index 5 | @post = Post.find(params[:post_id]) 6 | 7 | respond_to do |format| 8 | format.html { redirect_to @post } 9 | format.json do 10 | render json: @post.comments.parent_comments, each_serialize: CommentSerializer 11 | end 12 | end 13 | end 14 | 15 | def create 16 | @post = Post.find(params[:post_id]) 17 | 18 | @comments = @post.comments 19 | 20 | if current_user.deleted_at? 21 | render json: { warning: t('ui.small_notifications.basic.user_banned') } 22 | else 23 | @comment = current_user.comments.build(comment_params) 24 | @comment.post = @post 25 | 26 | if @comment.save 27 | render json: @comments.parent_comments, each_serialize: CommentSerializer 28 | else 29 | render json: { errors: @comment.errors.full_messages, status: 422 } 30 | end 31 | end 32 | end 33 | 34 | private 35 | 36 | def comment_params 37 | params.require(:comment).permit(:body, :parent_id) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/password_resets_controller.rb: -------------------------------------------------------------------------------- 1 | class PasswordResetsController < ApplicationController 2 | skip_before_filter :require_login 3 | 4 | def create 5 | @user = User.where(email: params[:email]).first 6 | 7 | @user.deliver_reset_password_instructions! if @user 8 | 9 | redirect_to root_path, notice: 'Instructions have been sent to your email.' 10 | end 11 | 12 | def edit 13 | @token = params[:id] 14 | @user = User.load_from_reset_password_token(params[:id]) 15 | 16 | if @user.blank? 17 | not_authenticated 18 | return 19 | end 20 | end 21 | 22 | def update 23 | @token = params[:id] 24 | @user = User.load_from_reset_password_token(params[:id]) 25 | 26 | if @user.blank? 27 | not_authenticated 28 | return 29 | end 30 | 31 | # the next line makes the password confirmation validation work 32 | @user.password_confirmation = params[:user][:password_confirmation] 33 | # the next line clears the temporary token and updates the password 34 | if @user.change_password!(params[:user][:password]) 35 | redirect_to(root_path, :notice => 'Password was successfully updated.') 36 | else 37 | render :action => "edit" 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/controllers/post_versions_controller.rb: -------------------------------------------------------------------------------- 1 | class PostVersionsController < ApplicationController 2 | before_filter :require_login 3 | before_filter :find_post_version!, except: :create 4 | 5 | def show; end 6 | 7 | def edit; end 8 | 9 | def create 10 | @post_version = current_user.post_versions.build(post_version_params) 11 | 12 | if @post_version.save 13 | redirect_to post_version_path(@post_version) 14 | else 15 | render 'posts/new' 16 | end 17 | end 18 | 19 | def update 20 | # TODO: show notification 21 | if @post_version.on_moderation? 22 | redirect_to post_version_path(@post_version), notice: 'Can not modify post already was sent on moderation' 23 | return 24 | end 25 | 26 | if @post_version.update_or_create_post_version!(post_version_params) 27 | redirect_to post_version_path(@post_version) 28 | end 29 | end 30 | 31 | def send_to_moderation 32 | if @post_version.send_to_moderation! 33 | redirect_to '/posts/on_moderation' 34 | end 35 | end 36 | 37 | private 38 | 39 | def post_version_params 40 | params.require(:post_version).permit(:title, :body, :context) 41 | end 42 | 43 | def find_post_version! 44 | @post_version = current_user.post_versions.find(params[:id]) 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /app/controllers/posts_controller.rb: -------------------------------------------------------------------------------- 1 | class PostsController < ApplicationController 2 | before_filter :require_login, except: [:index, :show] 3 | before_filter :find_post!, only: [:show, :add_to_favorites, :vote] 4 | 5 | def index 6 | @posts = Post.includes(:post_version).order(created_at: :desc) 7 | end 8 | 9 | def show 10 | @post_json_data = PostSerializer.new(@post, scope: current_user).to_json 11 | end 12 | 13 | def new 14 | @post_version = PostVersion.new 15 | end 16 | 17 | def edit 18 | @post_version = current_user.posts.find(params[:id]).post_version 19 | 20 | if current_version = current_user.post_versions.current_draft(@post_version).first 21 | @post_version = current_version 22 | end 23 | end 24 | 25 | def posts_list 26 | @posts = current_user.post_versions.with_state(params[:state]) 27 | end 28 | 29 | def preview 30 | html_data = MarkdownParser.new(params[:body]).to_html 31 | 32 | render html: html_data 33 | end 34 | 35 | def add_to_favorites 36 | if current_user.add_or_drop_favorites!(@post) 37 | 38 | notice, status = 39 | if current_user.included_in_favorites?(@post) 40 | [t('ui.small_notifications.favorite_added'), false] 41 | else 42 | [t('ui.small_notifications.favorite_removed'), true] 43 | end 44 | else 45 | render nothing: true 46 | end 47 | 48 | render json: { notice: notice, quantity: @post.favorites_quantity, status: status } 49 | end 50 | 51 | def vote 52 | json_data = 53 | if current_user.rate_post(@post, params[:rate]) 54 | { notice: t('ui.small_notifications.post_rating.voted') } 55 | else 56 | { warning: t('ui.small_notifications.post_rating.already_voted') } 57 | end 58 | 59 | render json: json_data.merge(quantity: @post.rating_quantity) 60 | end 61 | 62 | private 63 | 64 | def find_post! 65 | @post = Post.find(params[:id]) 66 | end 67 | 68 | def post_params 69 | params.require(:post).permit(:title, :body) 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | skip_before_filter :require_login, except: :destroy 3 | 4 | def new; end 5 | 6 | def create 7 | if login(session_params[:email], session_params[:password], session_params[:remember_me]) 8 | redirect_back_or_to root_url, success: 'Welcome!' 9 | else 10 | render :new 11 | end 12 | end 13 | 14 | def destroy 15 | logout 16 | redirect_back_or_to root_url, success: 'Success' 17 | end 18 | 19 | private 20 | 21 | def session_params 22 | params.require(:session).permit(:email, :password, :remember_me) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | skip_before_filter :require_login, except: [:edit, :update] 3 | 4 | def new 5 | @user = User.new 6 | end 7 | 8 | def edit 9 | @user = User.find(params[:id]) 10 | end 11 | 12 | def create 13 | @user = User.new(user_params) 14 | 15 | if @user.save 16 | login(params[:user][:email], params[:user][:password]) 17 | 18 | redirect_to root_url, success: 'success' 19 | else 20 | render :new 21 | end 22 | end 23 | 24 | def activate 25 | @user = User.load_from_activation_token(params[:id]) 26 | 27 | if @user && @user.activate! 28 | redirect_to login_path, success: 'Was activated' 29 | else 30 | redirect_to root_path, alert: 'Cannot activate user' 31 | end 32 | end 33 | 34 | private 35 | 36 | def user_params 37 | params.require(:user).permit(:email, :password, :password_confirmation) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/helpers/admin/application_helper.rb: -------------------------------------------------------------------------------- 1 | module Admin::ApplicationHelper 2 | 3 | LABELS_BY_STATES_MAP = { 4 | draft: 'label-default', 5 | declined: 'label-danger', 6 | approved: 'label-success', 7 | on_moderation: 'label-warning' 8 | } 9 | 10 | def label_state_class(obj) 11 | LABELS_BY_STATES_MAP[obj.aasm_state.to_sym] 12 | end 13 | 14 | def current_post_link(post_version) 15 | if post_version.post.present? 16 | link_to "##{post_version.post.id}", post_path(post_version.post) 17 | else 18 | '-' 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | FLASH_MESSAGE_TYPES = [:success, :notice, :alert] 3 | 4 | def is_current_user_admin? 5 | return current_user.is_admin? if logged_in? 6 | nil 7 | end 8 | 9 | def markdown(text) 10 | MarkdownParser.new(text).to_html 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/helpers/posts_helper.rb: -------------------------------------------------------------------------------- 1 | module PostsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/app/mailers/.keep -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ApplicationMailer 2 | 3 | # Subject can be set in your I18n file at config/locales/en.yml 4 | # with the following lookup: 5 | # 6 | # en.user_mailer.activation_needed_email.subject 7 | # 8 | def activation_needed_email(user) 9 | @user = user 10 | # TODO: add I18n 11 | mail(to: @user.email, subject: 'Account Activation') 12 | end 13 | 14 | # Subject can be set in your I18n file at config/locales/en.yml 15 | # with the following lookup: 16 | # 17 | # en.user_mailer.activation_success_email.subject 18 | # 19 | def activation_success_email(user) 20 | @user = user 21 | 22 | mail(to: @user.email, subject: 'You account successful activated') 23 | end 24 | 25 | def reset_password_email(user) 26 | @user = User.find user.id 27 | @url = edit_password_reset_url(@user.reset_password_token) 28 | 29 | mail(to: @user.email, subject: "Your password has been reset") 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/app/models/.keep -------------------------------------------------------------------------------- /app/models/comment.rb: -------------------------------------------------------------------------------- 1 | class Comment < ActiveRecord::Base 2 | after_create :update_comment_hierarchy 3 | 4 | belongs_to :user 5 | belongs_to :post 6 | 7 | validates :body, :post_id, presence: true 8 | validates :body, length: { in: 5..140 } 9 | 10 | scope :parent_comments, -> { where(parent_id: nil) } 11 | 12 | def children 13 | Comment.where(parent_id: id) 14 | end 15 | 16 | def tree_depth 17 | result = ActiveRecord::Base.connection.execute("SELECT NLEVEL('#{tree}')") 18 | result.getvalue(0, 0) 19 | end 20 | 21 | def update_comment_hierarchy 22 | parent_tree = Comment.find_by(id: parent_id).try(:tree) 23 | 24 | ltree = parent_tree ? "#{parent_tree}.#{id}" : id.to_s 25 | 26 | update_column(:tree, ltree) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/post.rb: -------------------------------------------------------------------------------- 1 | class Post < ActiveRecord::Base 2 | belongs_to :user 3 | belongs_to :post_version 4 | 5 | has_many :comments 6 | has_many :post_votes 7 | has_many :user_votes, through: :post_votes, source: :user 8 | 9 | def favorites_quantity 10 | User.favorites_quantity(self.id) 11 | end 12 | 13 | def rating_quantity 14 | self.post_votes.map(&:rate).sum 15 | end 16 | 17 | def owned_by?(user) 18 | self.user == user 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/models/post_version.rb: -------------------------------------------------------------------------------- 1 | class PostVersion < ActiveRecord::Base 2 | include AASM 3 | 4 | STATES_LIST = [:draft, :on_moderation, :declined, :approved] 5 | 6 | belongs_to :user 7 | has_one :post 8 | 9 | validates :title, :body, presence: true 10 | validates :title, length: { in: 5..140 } 11 | validates :body, length: { minimum: 7 } 12 | validates :decline_reason, presence: true, if: :declined? 13 | 14 | STATES_LIST.each do |state| 15 | scope state, -> { where(aasm_state: state).order(created_at: :desc) } 16 | end 17 | 18 | scope :with_state, -> (name) { where(aasm_state: name) } 19 | scope :current_draft, -> (post_version) { where(inherit_version_id: post_version.id).with_state(:draft) } 20 | 21 | aasm do 22 | state :draft, initial: true 23 | state :on_moderation 24 | state :declined 25 | state :approved 26 | 27 | event :send_to_moderation do 28 | transitions from: :draft, to: :on_moderation 29 | end 30 | 31 | event :decline do 32 | transitions from: :on_moderation, to: :declined 33 | end 34 | 35 | event :approve do 36 | success do 37 | create_or_update_post! 38 | end 39 | 40 | transitions from: :on_moderation, to: :approved 41 | end 42 | end 43 | 44 | def create_new_post_version!(params) 45 | params.merge!(inherit_version_id: self.id) if self.approved? 46 | 47 | self.user.post_versions.create!(params) 48 | end 49 | 50 | def update_current_post_version!(params) 51 | self.update!(params) 52 | end 53 | 54 | def update_or_create_post_version!(params) 55 | if self.draft? 56 | update_current_post_version!(params) 57 | else 58 | create_new_post_version!(params) 59 | end 60 | end 61 | 62 | def create_or_update_post! 63 | if self.inherit_version_id > 0 64 | post = self.user.posts.find_by(post_version: self.inherit_version_id) 65 | post.update!(post_version: self) 66 | else 67 | self.user.posts.create!(post_version: self) 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /app/models/post_vote.rb: -------------------------------------------------------------------------------- 1 | class PostVote < ActiveRecord::Base 2 | belongs_to :user 3 | belongs_to :post 4 | end 5 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | authenticates_with_sorcery! 3 | 4 | has_many :posts 5 | has_many :comments 6 | has_many :post_votes 7 | has_many :post_versions 8 | 9 | validates :email, :password, :password_confirmation, presence: true, if: :new_record? 10 | 11 | validates :email, uniqueness: true 12 | 13 | validates :password, length: { minimum: 6 }, if: :new_record? 14 | validates :password, confirmation: true, if: :new_record? 15 | 16 | scope :admins, -> { where(is_admin: true) } 17 | scope :active, -> { where(deleted_at: nil) } 18 | scope :favorites_quantity, -> (post_id) { active.where("favorite_posts @> '{?}'", post_id).count.to_i } 19 | 20 | def add_or_drop_favorites!(post) 21 | if included_in_favorites?(post) 22 | drop_favorites(post) 23 | else 24 | add_favorites(post) 25 | end 26 | 27 | self.save! 28 | end 29 | 30 | def included_in_favorites?(post) 31 | self.favorite_posts.include?(post.id) 32 | end 33 | 34 | def drop_favorites(post) 35 | self.favorite_posts.delete(post.id) 36 | end 37 | 38 | def add_favorites(post) 39 | self.favorite_posts << post.id 40 | end 41 | 42 | def rate_post(post, rate) 43 | return if post.owned_by?(self) 44 | 45 | unless self.post_votes.where(post: post).exists? 46 | self.post_votes.create(post: post, rate: rate) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/serializers/comment_serializer.rb: -------------------------------------------------------------------------------- 1 | class CommentSerializer < ActiveModel::Serializer 2 | attributes :id, :body, :children, :parent_id, :tree_depth, :username, :commented_at 3 | 4 | def children 5 | result = [] 6 | 7 | object.children.each do |children| 8 | result << CommentSerializer.new(children) 9 | end 10 | 11 | result 12 | end 13 | 14 | def username 15 | object.user.email.split(/@/).first 16 | end 17 | 18 | def commented_at 19 | object.created_at.strftime("%d %B %Y %H:%M") 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/serializers/post_serializer.rb: -------------------------------------------------------------------------------- 1 | class PostSerializer < ActiveModel::Serializer 2 | include ActionView::Helpers::AssetTagHelper 3 | 4 | attributes :id, :favorites_quantity, :is_empty, :votes_quantity, :is_voted, :default_rate, :user_post 5 | 6 | def favorites_quantity 7 | object.favorites_quantity 8 | end 9 | 10 | def is_empty 11 | !scope.included_in_favorites?(object) if scope 12 | end 13 | 14 | def votes_quantity 15 | object.rating_quantity 16 | end 17 | 18 | def is_voted 19 | object.user_votes.include?(scope) if scope 20 | end 21 | 22 | def default_rate 23 | object.post_votes.find_by(user: scope).try(:rate) if scope 24 | end 25 | 26 | def user_post 27 | object.owned_by?(scope) if scope 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/serializers/user_front_data_serializer.rb: -------------------------------------------------------------------------------- 1 | class UserFrontDataSerializer < ActiveModel::Serializer 2 | attributes :id, :email, :full_name, :deleted_at 3 | 4 | def full_name 5 | "#{object.first_name} #{object.last_name}" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/services/markdown_parser.rb: -------------------------------------------------------------------------------- 1 | class MarkdownParser 2 | attr_reader :markdown_data 3 | 4 | def initialize(markdown_data) 5 | @markdown_data = markdown_data 6 | end 7 | 8 | def renderer 9 | Redcarpet::Render::HTML.new(filter_html: true, hard_wrap: true) 10 | end 11 | 12 | def to_html 13 | white_list_sanitizer = Rails::Html::WhiteListSanitizer.new 14 | markdown = Redcarpet::Markdown.new(renderer).render(markdown_data) 15 | 16 | white_list_sanitizer.sanitize(markdown).html_safe 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/views/_vue_templates.html.haml: -------------------------------------------------------------------------------- 1 | = render partial: 'vue_templates/comments/comment_box_tpl' 2 | = render partial: 'vue_templates/comments/comment_list_tpl' 3 | = render partial: 'vue_templates/comments/comment_notification_list_tpl' 4 | = render partial: 'vue_templates/comments/comment_form_tpl' 5 | = render partial: 'vue_templates/comments/comment_tpl' 6 | = render partial: 'vue_templates/shared/post_favorite_button_tpl' 7 | = render partial: 'vue_templates/shared/post_rating_buttons_tpl' 8 | -------------------------------------------------------------------------------- /app/views/admin/main/index.html.haml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/app/views/admin/main/index.html.haml -------------------------------------------------------------------------------- /app/views/admin/post_versions/edit_decline.html.haml: -------------------------------------------------------------------------------- 1 | %h2= @post_version.title 2 | 3 | = simple_form_for @post_version, url: decline_admin_post_version_path(@post_version), method: :put do |f| 4 | .form-group 5 | = f.input :decline_reason, as: :text, input_html: { class: 'form-control' } 6 | .form-group 7 | = link_to :back, class: 'btn btn-default' do 8 | %span.glyphicon.glyphicon-chevron-left 9 | Back 10 | = f.button :submit, "Decline", class: 'btn btn-danger' 11 | -------------------------------------------------------------------------------- /app/views/admin/post_versions/index.html.haml: -------------------------------------------------------------------------------- 1 | %table.table 2 | %thead 3 | %tr 4 | %th 5 | = PostVersion.human_attribute_name(:id) 6 | %th 7 | = PostVersion.human_attribute_name(:title) 8 | %th 9 | = PostVersion.human_attribute_name(:aasm_state) 10 | %th 11 | = PostVersion.human_attribute_name(:post) 12 | 13 | %tbody 14 | - @post_versions.each do |post_version| 15 | %tr 16 | %td 17 | = link_to post_version.id, admin_post_version_path(post_version) 18 | %td #{post_version.title} 19 | %td 20 | %span{class: "label #{label_state_class(post_version)}"} 21 | = post_version.aasm_state 22 | 23 | - if post_version.decline_reason? 24 | .decline-hint 25 | %small #{post_version.decline_reason} 26 | %td 27 | = current_post_link(post_version) 28 | -------------------------------------------------------------------------------- /app/views/admin/post_versions/show.html.haml: -------------------------------------------------------------------------------- 1 | .post-version 2 | .panel.panel-default 3 | .panel-heading 4 | .row 5 | .col-md-1 6 | = link_to admin_post_versions_path, class: 'btn btn-default btn-xs' do 7 | %span.glyphicon.glyphicon-chevron-left 8 | 9 | - unless @post_version.declined? 10 | .col-md-4.col-md-offset-5 11 | .col-md-1 12 | = button_to 'Approve', approve_admin_post_version_path(@post_version), method: :put, class: 'btn btn-success btn-xs' 13 | .col-md-1 14 | = link_to 'Decline', edit_decline_admin_post_version_path(@post_version), class: 'btn btn-danger btn-xs' 15 | 16 | .row 17 | .col-md-12 18 | %h3= @post_version.title 19 | 20 | %hr 21 | .row 22 | .col-md-3 23 | %small 24 | %strong 25 | = PostVersion.human_attribute_name(:user) 26 | %mark #{@post_version.user.email} 27 | 28 | .row 29 | .col-md-3 30 | %small 31 | %strong 32 | = PostVersion.human_attribute_name(:created_at) 33 | %mark #{@post_version.created_at} 34 | 35 | .row 36 | .col-md-3 37 | %span{ class: "label #{label_state_class(@post_version)}"} 38 | = @post_version.aasm_state 39 | 40 | - if @post_version.decline_reason? 41 | .decline-hint 42 | %small #{@post_version.decline_reason} 43 | 44 | .post-panel.panel-body 45 | .row 46 | %p= markdown @post_version.body 47 | -------------------------------------------------------------------------------- /app/views/layouts/admin.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ 5 | %title Admin Panel 6 | = include_gon 7 | = stylesheet_link_tag 'backend', media: 'all' 8 | = javascript_include_tag 'backend' 9 | = csrf_meta_tags 10 | %body 11 | %header 12 | %nav.navbar.navbar-default 13 | .navbar-header 14 | = link_to 'Admin Panel', admin_root_url, class: 'navbar-brand' 15 | .collapse.navbar-collapse 16 | .nav.navbar-nav 17 | %li 18 | = link_to 'Post Versions', admin_post_versions_url 19 | 20 | .nav.navbar-nav.navbar-right 21 | = link_to 'Front page', root_url, class: 'navbar-brand' 22 | 23 | .container 24 | = yield 25 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ 5 | %title Zvample 6 | = include_gon 7 | = stylesheet_link_tag 'application', media: 'all' 8 | = javascript_include_tag 'application' 9 | = csrf_meta_tags 10 | %body 11 | %header 12 | %nav.navbar.navbar-default 13 | .navbar-header 14 | = link_to 'Zvample', root_url, class: 'navbar-brand' 15 | 16 | .collapse.navbar-collapse 17 | .nav.navbar-nav 18 | - if is_current_user_admin? 19 | %li 20 | = link_to 'Admin', admin_root_url 21 | %li 22 | = link_to 'Posts', posts_url 23 | 24 | .nav.navbar-nav.navbar-right.login-buttons 25 | - if current_user 26 | .navbar-text 27 | = current_user.email 28 | 29 | = link_to 'Write Post', new_post_path, class: 'btn btn-success navbar-btn' 30 | = link_to 'Edit Profile', edit_user_path(current_user), class: 'btn btn-default navbar-btn' 31 | = link_to 'Logout', :logout, method: :post, class: 'btn btn-danger navbar-btn' 32 | - else 33 | = link_to 'Login', :login, class: 'btn btn-success navbar-btn' 34 | = link_to 'Register', new_user_path, class: 'btn btn-default navbar-btn' 35 | 36 | .container 37 | = yield 38 | 39 | = render partial: '/vue_templates' 40 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.haml: -------------------------------------------------------------------------------- 1 | %hmtl 2 | %body 3 | = yield -------------------------------------------------------------------------------- /app/views/password_resets/edit.html.haml: -------------------------------------------------------------------------------- 1 | = simple_form_for @user, url: password_reset_path(@token), defaults: { { input_html: 'form-control' } } do |f| 2 | .form-group 3 | = f.input :email, as: :email 4 | .form-group 5 | = f.input :password, as: :password 6 | .form-group 7 | = f.input :password_confirmation, as: :password 8 | .form-group 9 | = f.button :submit, class: 'btn btn-default' 10 | -------------------------------------------------------------------------------- /app/views/password_resets/new.html.haml: -------------------------------------------------------------------------------- 1 | = simple_form_for password_resets_path, defaults: { input_html: { class: 'form-control' } } do |f| 2 | .form-group 3 | = f.input :email 4 | .form-group 5 | = f.button :submit, "Reset my password!", class: 'btn btn-default' 6 | -------------------------------------------------------------------------------- /app/views/post_versions/edit.html.haml: -------------------------------------------------------------------------------- 1 | = render 'shared/post_markdown_editor' 2 | -------------------------------------------------------------------------------- /app/views/post_versions/show.html.haml: -------------------------------------------------------------------------------- 1 | %h2 2 | = @post_version.title 3 | 4 | %p 5 | = markdown @post_version.body 6 | 7 | .row 8 | .col-md-1 9 | = link_to 'Edit', edit_post_version_path(@post_version), class: 'btn btn-default' 10 | 11 | .col-md-2 12 | - if @post_version.draft? 13 | = button_to 'Send to moderation', send_to_moderation_post_version_path(@post_version), method: :put, class: 'btn btn-primary' 14 | 15 | -------------------------------------------------------------------------------- /app/views/posts/edit.html.haml: -------------------------------------------------------------------------------- 1 | = render 'shared/post_markdown_editor' 2 | -------------------------------------------------------------------------------- /app/views/posts/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1 Posts 2 | 3 | .row 4 | .col-md-9 5 | - @posts.each do |post| 6 | %h3 7 | = link_to post.post_version.title, post_path(post) 8 | .col-md-3 9 | = render 'shared/menu' 10 | -------------------------------------------------------------------------------- /app/views/posts/new.html.haml: -------------------------------------------------------------------------------- 1 | %h2 Write Post 2 | = render 'shared/post_markdown_editor' 3 | -------------------------------------------------------------------------------- /app/views/posts/posts_list.html.haml: -------------------------------------------------------------------------------- 1 | %ul 2 | - @posts.each do |post| 3 | %li 4 | = link_to post.title, post_version_path(post) 5 | -------------------------------------------------------------------------------- /app/views/posts/show.html.haml: -------------------------------------------------------------------------------- 1 | .row 2 | %article.col-md-12 3 | %header 4 | %h1 5 | = @post.post_version.title 6 | %section 7 | = markdown @post.post_version.body 8 | 9 | %hr 10 | .row 11 | %post-rating-buttons( post = "#{@post_json_data}" ) 12 | %post-favorite-button( post = "#{@post_json_data}" ) 13 | 14 | %hr 15 | %comment-box( post-id = "#{@post.id}" ) 16 | -------------------------------------------------------------------------------- /app/views/sessions/_form.html.haml: -------------------------------------------------------------------------------- 1 | = simple_form_for :session, url: sessions_path, defaults: { input_html: { class: 'form-control' } } do |f| 2 | .form-group 3 | = f.input :email, as: :email 4 | .form-group 5 | = f.input :password, as: :password 6 | .checkbox 7 | = f.input :remember_me, as: :boolean, input_html: { class: '' } 8 | .form-group 9 | = f.button :submit, class: 'btn btn-default' 10 | -------------------------------------------------------------------------------- /app/views/sessions/new.html.haml: -------------------------------------------------------------------------------- 1 | = render 'form' 2 | 3 | = link_to 'Forgot Password?', new_password_reset_path 4 | -------------------------------------------------------------------------------- /app/views/shared/_flash_messages.html.haml: -------------------------------------------------------------------------------- 1 | -# TODO: use vue-strap 2 | - ApplicationHelper::FLASH_MESSAGE_TYPES.each do |message_type| 3 | %p(id = message_type) 4 | = flash[message_type] 5 | -------------------------------------------------------------------------------- /app/views/shared/_menu.html.haml: -------------------------------------------------------------------------------- 1 | %nav.panel.panel-default 2 | %header.panel-heading.text-center 3 | %h2 Menu 4 | 5 | %ul.panel-body.list-group.text-center 6 | %li.list-group-item 7 | = link_to 'posts/draft' do 8 | Drafts 9 | %span.badge 10 | 11 | %li.list-group-item 12 | = link_to 'posts/on_moderation' do 13 | On moderation 14 | %span.badge 15 | 16 | %li.list-group-item 17 | = link_to 'posts/declined' do 18 | Declined 19 | %span.badge 20 | -------------------------------------------------------------------------------- /app/views/shared/_post_markdown_editor.html.haml: -------------------------------------------------------------------------------- 1 | %markdown-editor(inline-template) 2 | = simple_form_for @post_version, defaults: { input_html: { class: 'form-control' } } do |f| 3 | .form-group 4 | = f.input :title 5 | .form-group 6 | = f.input :body, as: :text, input_html: { 'v-model':'body', rows: 5 } 7 | .form-group 8 | = f.button :submit, class: 'btn btn-success' 9 | %a.btn.btn-primary{href:'#', '@click.prevent':'preview'} Preview 10 | 11 | %span{'v-html':'html_data'} 12 | -------------------------------------------------------------------------------- /app/views/user_mailer/activation_needed_email.html.haml: -------------------------------------------------------------------------------- 1 | %p Welcome, #{ @user.email }! 2 | %p Now you need to activate you profile, just visit #{ link_to 'link', activate_user_url(@user.activation_token) } 3 | -------------------------------------------------------------------------------- /app/views/user_mailer/activation_success_email.html.haml: -------------------------------------------------------------------------------- 1 | %p Congratulations, #{ @user.email }! 2 | %p Now you can #{ link_to 'login', login_url } 3 | -------------------------------------------------------------------------------- /app/views/user_mailer/reset_password_email.html.haml: -------------------------------------------------------------------------------- 1 | %p Hello, #{ @user.email } 2 | 3 | %p You have requested to reset your password. 4 | 5 | %p To choose a new password, just follow this #{ link_to 'link', @url } 6 | -------------------------------------------------------------------------------- /app/views/users/_form.html.haml: -------------------------------------------------------------------------------- 1 | = simple_form_for @user, defaults: { input_html: { class: 'form-control' } } do |f| 2 | - if @user.errors.any? 3 | #error_explanation 4 | %h2 5 | #{ pluralize(@user.errors.count, "error") } prohibited 6 | %ul 7 | - @user.errors.full_messages.each do |msg| 8 | %li 9 | = msg 10 | 11 | .form-group 12 | = f.input :email, as: :email 13 | .form-group 14 | = f.input :password, as: :password 15 | .form-group 16 | = f.input :password_confirmation, as: :password 17 | .form-group 18 | = f.button :submit, class: 'btn btn-default' 19 | -------------------------------------------------------------------------------- /app/views/users/edit.html.haml: -------------------------------------------------------------------------------- 1 | = render 'form' 2 | -------------------------------------------------------------------------------- /app/views/users/edit.html.haml~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/app/views/users/edit.html.haml~ -------------------------------------------------------------------------------- /app/views/users/new.html.haml: -------------------------------------------------------------------------------- 1 | = render 'form' 2 | -------------------------------------------------------------------------------- /app/views/vue_templates/comments/_comment_box_tpl.html.haml: -------------------------------------------------------------------------------- 1 | %script( type='text/x-template' id='comment-box-tpl' ) 2 | .well 3 | %comment-form 4 | 5 | %hr 6 | 7 | %h3 8 | Comments 9 | %span.badge {{ commentsQuantity }} 10 | 11 | %comment-list( :comments = 'comments' ) 12 | %br 13 | -------------------------------------------------------------------------------- /app/views/vue_templates/comments/_comment_form_tpl.html.haml: -------------------------------------------------------------------------------- 1 | %script( type='text/x-template' id='comment-form-tpl' ) 2 | %ul 3 | %comment-notification-list(:notifications = 'notifications') 4 | 5 | %div 6 | %pre(v-show = 'body') 7 | {{ body }} 8 | 9 | %div(v-if = '!loggedIn') 10 | Please login bla bla bla template 11 | 12 | = simple_form_for :comment, html: { 'v-on:submit.prevent': 'addComment($event)' } do |f| 13 | .form-group 14 | = f.input :body, as: :text, label: false, input_html: { 'v-model': 'body', ':disabled': "!loggedIn", class: 'form-control', rows: 5 } 15 | .form-group.text-right 16 | = f.button :submit, class: 'btn btn-success' 17 | -------------------------------------------------------------------------------- /app/views/vue_templates/comments/_comment_list_tpl.html.haml: -------------------------------------------------------------------------------- 1 | %script( type='text/x-template' id='comment-list-tpl' ) 2 | .comment-item( v-for='comment in comments' track-by='id' ) 3 | %comment( :comment = 'comment' ) 4 | 5 | %div(:class = "{nestedComment: comment.treeDepth < maxCommentDepth}") 6 | %comment-list( :comments = 'comment.children' ) 7 | 8 | -# %pre 9 | {{ $data | json }} 10 | -------------------------------------------------------------------------------- /app/views/vue_templates/comments/_comment_notification_list_tpl.html.haml: -------------------------------------------------------------------------------- 1 | %script( type='text/x-template' id='comment-notification-list-tpl' ) 2 | %li.comment-notifications.error(v-for='notice in notifications') 3 | %span {{ notice }} 4 | -------------------------------------------------------------------------------- /app/views/vue_templates/comments/_comment_tpl.html.haml: -------------------------------------------------------------------------------- 1 | %script( type='text/x-template' id='comment-tpl' ) 2 | .comment-header 3 | .row.top-line 4 | .col-md-2 5 | %span.label.label-green 6 | {{ comment.username }} 7 | .col-md-3 8 | %time.small 9 | {{ comment.commentedAt }} 10 | .col-md-1.small 11 | = link_to '#{{ comment.id }}', '#' 12 | .col-md-1.col-md-offset-5 13 | %span 14 | 15 | .comment-body 16 | .row 17 | %p 18 | {{ comment.body }} 19 | 20 | .comment-actions 21 | .row.reply 22 | %a{href:'#', '@click.prevent':"showForm"} Reply 23 | 24 | %comment-form(v-if="replied" :parent-id="comment.id") 25 | -------------------------------------------------------------------------------- /app/views/vue_templates/shared/_post_favorite_button_tpl.html.haml: -------------------------------------------------------------------------------- 1 | %script( type='text/x-template' id='post-favorite-button-tpl' ) 2 | .col-md-1 3 | %button.format-button{'@click.prevent':'add_to_favorites'} 4 | %span.glyphicon(:class = "[isEmpty ? 'glyphicon-star-empty' : 'glyphicon-star']") 5 | %span.bages {{ quantity }} 6 | -------------------------------------------------------------------------------- /app/views/vue_templates/shared/_post_rating_buttons_tpl.html.haml: -------------------------------------------------------------------------------- 1 | %script( type='text/x-template' id='post-rating-buttons-tpl') 2 | .col-md-1.text-center( v-show = 'isVoted' ) 3 | %span 4 | %button.format-button{'@click.prevent':'vote(1)'} 5 | %span.rating-icons{':class':'[ ratesMap[defaultRate] ]'} 6 | %span.favorites-quantity {{ quantity }} 7 | .col-md-2.text-center( v-else ) 8 | %span( v-show = 'userPost' ) 9 | %button.format-button{'@click.prevent':'vote(0)'} 10 | %span.rating-icons{':class':'[ ratesMap[0] ]'} 11 | %span.favorites-quantity {{ quantity }} 12 | %span( v-else ) 13 | %button.format-button{'@click.prevent':'vote(-1)'} 14 | %span.glyphicon.glyphicon-minus 15 | %button.format-button{'@click.prevent':'vote(0)'} 16 | %span.glyphicon.glyphicon-eye-open 17 | %button.format-button{'@click.prevent':'vote(1)'} 18 | %span.glyphicon.glyphicon-plus 19 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /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 Rails.application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | require "action_controller/railtie" 9 | require "action_mailer/railtie" 10 | require "action_view/railtie" 11 | require "sprockets/railtie" 12 | # require "rails/test_unit/railtie" 13 | 14 | # Require the gems listed in Gemfile, including any gems 15 | # you've limited to :test, :development, or :production. 16 | Bundler.require(*Rails.groups) 17 | 18 | module Zvample 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 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 25 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 26 | # config.time_zone = 'Central Time (US & Canada)' 27 | 28 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 29 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 30 | # config.i18n.default_locale = :de 31 | 32 | # Do not swallow errors in after_commit/after_rollback callbacks. 33 | config.active_record.raise_in_transactional_callbacks = true 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/database.yml.example: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 8.2 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On OS X with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On OS X with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem 'pg' 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | # For details on connection pooling, see rails configuration guide 21 | # http://guides.rubyonrails.org/configuring.html#database-pooling 22 | pool: 5 23 | 24 | development: 25 | <<: *default 26 | database: dbname_development 27 | 28 | # The specified database role being used to connect to postgres. 29 | # To create additional roles in postgres see `$ createuser --help`. 30 | # When left blank, postgres will use the default role. This is 31 | # the same name as the operating system user that initialized the database. 32 | #username: username 33 | 34 | # The password associated with the postgres role (username). 35 | #password: 36 | 37 | # Connect on a TCP socket. Omitted by default since the client uses a 38 | # domain socket that doesn't need configuration. Windows does not have 39 | # domain sockets, so uncomment these lines. 40 | #host: localhost 41 | 42 | # The TCP port the server listens on. Defaults to 5432. 43 | # If your server runs on a different port number, change accordingly. 44 | #port: 5432 45 | 46 | # Schema search path. The server defaults to $user,public 47 | #schema_search_path: myapp,sharedapp,public 48 | 49 | # Minimum log levels, in increasing order: 50 | # debug5, debug4, debug3, debug2, debug1, 51 | # log, notice, warning, error, fatal, and panic 52 | # Defaults to warning. 53 | #min_messages: notice 54 | 55 | # Warning: The database defined as "test" will be erased and 56 | # re-generated from your development database when you run "rake". 57 | # Do not set this db to the same as development or production. 58 | test: 59 | <<: *default 60 | database: dbname_test 61 | 62 | # As with config/secrets.yml, you never want to store sensitive information, 63 | # like your database password, in your source code. If your source code is 64 | # ever seen by anyone, they now have access to your database. 65 | # 66 | # Instead, provide the password as a unix environment variable when you boot 67 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database 68 | # for a full rundown on how to provide these environment variables in a 69 | # production deployment. 70 | # 71 | # On Heroku and other platform providers, you may have a full connection URL 72 | # available as an environment variable. For example: 73 | # 74 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" 75 | # 76 | # You can use this database configuration with: 77 | # 78 | # production: 79 | # url: <%= ENV['DATABASE_URL'] %> 80 | # 81 | production: 82 | <<: *default 83 | database: dbname_production 84 | username: username 85 | password: <%= ENV['DBNAME_DATABASE_PASSWORD'] %> 86 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.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 | # Do not eager load code on boot. 10 | config.eager_load = false 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 | # Raise an error on page load if there are pending migrations. 23 | config.active_record.migration_error = :page_load 24 | 25 | # Debug mode disables concatenation and preprocessing of assets. 26 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | 30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 31 | # yet still be able to expire them through the digest params. 32 | config.assets.digest = true 33 | config.action_mailer.delivery_method = :letter_opener 34 | 35 | # Adds additional error checking when serving assets at runtime. 36 | # Checks for improperly declared sprockets dependencies. 37 | # Raises helpful error messages. 38 | config.assets.raise_runtime_errors = true 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | 43 | config.action_mailer.default_url_options = { host: 'localhost:3000' } 44 | end 45 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.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 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 35 | # yet still be able to expire them through the digest params. 36 | config.assets.digest = true 37 | 38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | # config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :debug 50 | 51 | # Prepend all log lines with the following tags. 52 | # config.log_tags = [ :subdomain, :uuid ] 53 | 54 | # Use a different logger for distributed setups. 55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 61 | # config.action_controller.asset_host = 'http://assets.example.com' 62 | 63 | # Ignore bad email addresses and do not raise email delivery errors. 64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 65 | # config.action_mailer.raise_delivery_errors = false 66 | 67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 68 | # the I18n.default_locale when a translation cannot be found). 69 | config.i18n.fallbacks = true 70 | 71 | # Send deprecation notices to registered listeners. 72 | config.active_support.deprecation = :notify 73 | 74 | # Use default logging formatter so that PID and timestamp are not suppressed. 75 | config.log_formatter = ::Logger::Formatter.new 76 | 77 | # Do not dump schema after migrations. 78 | config.active_record.dump_schema_after_migration = false 79 | end 80 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.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 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static file server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Randomize the order test cases are executed. 35 | config.active_support.test_order = :random 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | config.action_mailer.default_url_options = { host: 'localhost:3000' } 43 | end 44 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | Rails.application.config.assets.precompile += %w( backend.css ) 12 | -------------------------------------------------------------------------------- /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/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /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. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/initializers/serializer_config.rb: -------------------------------------------------------------------------------- 1 | # Disable for all serializers (except ArraySerializer) 2 | ActiveModel::Serializer.root = false 3 | 4 | ActiveModel::Serializer.setup do |config| 5 | config.key_format = :lower_camel 6 | end 7 | 8 | # Disable for ArraySerializer 9 | ActiveModel::ArraySerializer.root = false 10 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_zvample_session' 4 | -------------------------------------------------------------------------------- /config/initializers/simple_form.rb: -------------------------------------------------------------------------------- 1 | # Use this setup block to configure all options available in SimpleForm. 2 | SimpleForm.setup do |config| 3 | # Wrappers are used by the form builder to generate a 4 | # complete input. You can remove any component from the 5 | # wrapper, change the order or even add your own to the 6 | # stack. The options given below are used to wrap the 7 | # whole input. 8 | config.wrappers :default, class: :input, 9 | hint_class: :field_with_hint, error_class: :field_with_errors do |b| 10 | ## Extensions enabled by default 11 | # Any of these extensions can be disabled for a 12 | # given input by passing: `f.input EXTENSION_NAME => false`. 13 | # You can make any of these extensions optional by 14 | # renaming `b.use` to `b.optional`. 15 | 16 | # Determines whether to use HTML5 (:email, :url, ...) 17 | # and required attributes 18 | b.use :html5 19 | 20 | # Calculates placeholders automatically from I18n 21 | # You can also pass a string as f.input placeholder: "Placeholder" 22 | b.use :placeholder 23 | 24 | ## Optional extensions 25 | # They are disabled unless you pass `f.input EXTENSION_NAME => true` 26 | # to the input. If so, they will retrieve the values from the model 27 | # if any exists. If you want to enable any of those 28 | # extensions by default, you can change `b.optional` to `b.use`. 29 | 30 | # Calculates maxlength from length validations for string inputs 31 | b.optional :maxlength 32 | 33 | # Calculates pattern from format validations for string inputs 34 | b.optional :pattern 35 | 36 | # Calculates min and max from length validations for numeric inputs 37 | b.optional :min_max 38 | 39 | # Calculates readonly automatically from readonly attributes 40 | b.optional :readonly 41 | 42 | ## Inputs 43 | b.use :label_input 44 | b.use :hint, wrap_with: { tag: :span, class: :hint } 45 | b.use :error, wrap_with: { tag: :span, class: :error } 46 | 47 | ## full_messages_for 48 | # If you want to display the full error message for the attribute, you can 49 | # use the component :full_error, like: 50 | # 51 | # b.use :full_error, wrap_with: { tag: :span, class: :error } 52 | end 53 | 54 | # The default wrapper to be used by the FormBuilder. 55 | config.default_wrapper = :default 56 | 57 | # Define the way to render check boxes / radio buttons with labels. 58 | # Defaults to :nested for bootstrap config. 59 | # inline: input + label 60 | # nested: label > input 61 | config.boolean_style = :nested 62 | 63 | # Default class for buttons 64 | config.button_class = 'btn' 65 | 66 | # Method used to tidy up errors. Specify any Rails Array method. 67 | # :first lists the first message for each field. 68 | # Use :to_sentence to list all errors for each field. 69 | # config.error_method = :first 70 | 71 | # Default tag used for error notification helper. 72 | config.error_notification_tag = :div 73 | 74 | # CSS class to add for error notification helper. 75 | config.error_notification_class = 'error_notification' 76 | 77 | # ID to add for error notification helper. 78 | # config.error_notification_id = nil 79 | 80 | # Series of attempts to detect a default label method for collection. 81 | # config.collection_label_methods = [ :to_label, :name, :title, :to_s ] 82 | 83 | # Series of attempts to detect a default value method for collection. 84 | # config.collection_value_methods = [ :id, :to_s ] 85 | 86 | # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none. 87 | # config.collection_wrapper_tag = nil 88 | 89 | # You can define the class to use on all collection wrappers. Defaulting to none. 90 | # config.collection_wrapper_class = nil 91 | 92 | # You can wrap each item in a collection of radio/check boxes with a tag, 93 | # defaulting to :span. 94 | # config.item_wrapper_tag = :span 95 | 96 | # You can define a class to use in all item wrappers. Defaulting to none. 97 | # config.item_wrapper_class = nil 98 | 99 | # How the label text should be generated altogether with the required text. 100 | # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" } 101 | 102 | # You can define the class to use on all labels. Default is nil. 103 | # config.label_class = nil 104 | 105 | # You can define the default class to be used on forms. Can be overriden 106 | # with `html: { :class }`. Defaulting to none. 107 | # config.default_form_class = nil 108 | 109 | # You can define which elements should obtain additional classes 110 | # config.generate_additional_classes_for = [:wrapper, :label, :input] 111 | 112 | # Whether attributes are required by default (or not). Default is true. 113 | # config.required_by_default = true 114 | 115 | # Tell browsers whether to use the native HTML5 validations (novalidate form option). 116 | # These validations are enabled in SimpleForm's internal config but disabled by default 117 | # in this configuration, which is recommended due to some quirks from different browsers. 118 | # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations, 119 | # change this configuration to true. 120 | config.browser_validations = false 121 | 122 | # Collection of methods to detect if a file type was given. 123 | # config.file_methods = [ :mounted_as, :file?, :public_filename ] 124 | 125 | # Custom mappings for input types. This should be a hash containing a regexp 126 | # to match as key, and the input type that will be used when the field name 127 | # matches the regexp as value. 128 | # config.input_mappings = { /count/ => :integer } 129 | 130 | # Custom wrappers for input types. This should be a hash containing an input 131 | # type as key and the wrapper that will be used for all inputs with specified type. 132 | # config.wrapper_mappings = { string: :prepend } 133 | 134 | # Namespaces where SimpleForm should look for custom input classes that 135 | # override default inputs. 136 | # config.custom_inputs_namespaces << "CustomInputs" 137 | 138 | # Default priority for time_zone inputs. 139 | # config.time_zone_priority = nil 140 | 141 | # Default priority for country inputs. 142 | # config.country_priority = nil 143 | 144 | # When false, do not use translations for labels. 145 | # config.translate_labels = true 146 | 147 | # Automatically discover new inputs in Rails' autoload path. 148 | # config.inputs_discovery = true 149 | 150 | # Cache SimpleForm inputs discovery 151 | # config.cache_discovery = !Rails.env.development? 152 | 153 | # Default class for inputs 154 | # config.input_class = nil 155 | 156 | # Define the default class of the input wrapper of the boolean input. 157 | config.boolean_label_class = 'checkbox' 158 | 159 | # Defines if the default input wrapper class should be included in radio 160 | # collection wrappers. 161 | # config.include_default_input_wrapper_class = true 162 | 163 | # Defines which i18n scope will be used in Simple Form. 164 | # config.i18n_scope = 'simple_form' 165 | end 166 | -------------------------------------------------------------------------------- /config/initializers/sorcery.rb: -------------------------------------------------------------------------------- 1 | # The first thing you need to configure is which modules you need in your app. 2 | # The default is nothing which will include only core features (password encryption, login/logout). 3 | # Available submodules are: :user_activation, :http_basic_auth, :remember_me, 4 | # :reset_password, :session_timeout, :brute_force_protection, :activity_logging, :external 5 | Rails.application.config.sorcery.submodules = [:remember_me, :user_activation, :reset_password] 6 | 7 | # Here you can configure each submodule's features. 8 | Rails.application.config.sorcery.configure do |config| 9 | # -- core -- 10 | # What controller action to call for non-authenticated users. You can also 11 | # override the 'not_authenticated' method of course. 12 | # Default: `:not_authenticated` 13 | # 14 | # config.not_authenticated_action = 15 | 16 | 17 | # When a non logged in user tries to enter a page that requires login, save 18 | # the URL he wanted to reach, and send him there after login, using 'redirect_back_or_to'. 19 | # Default: `true` 20 | # 21 | # config.save_return_to_url = 22 | 23 | 24 | # Set domain option for cookies; Useful for remember_me submodule. 25 | # Default: `nil` 26 | # 27 | # config.cookie_domain = 28 | 29 | 30 | # Allow the remember_me cookie to be set through AJAX 31 | # Default: `true` 32 | # 33 | # config.remember_me_httponly = 34 | 35 | 36 | # -- session timeout -- 37 | # How long in seconds to keep the session alive. 38 | # Default: `3600` 39 | # 40 | # config.session_timeout = 41 | 42 | 43 | # Use the last action as the beginning of session timeout. 44 | # Default: `false` 45 | # 46 | # config.session_timeout_from_last_action = 47 | 48 | 49 | # -- http_basic_auth -- 50 | # What realm to display for which controller name. For example {"My App" => "Application"} 51 | # Default: `{"application" => "Application"}` 52 | # 53 | # config.controller_to_realm_map = 54 | 55 | 56 | # -- activity logging -- 57 | # will register the time of last user login, every login. 58 | # Default: `true` 59 | # 60 | # config.register_login_time = 61 | 62 | 63 | # will register the time of last user logout, every logout. 64 | # Default: `true` 65 | # 66 | # config.register_logout_time = 67 | 68 | 69 | # will register the time of last user action, every action. 70 | # Default: `true` 71 | # 72 | # config.register_last_activity_time = 73 | 74 | 75 | # -- external -- 76 | # What providers are supported by this app, i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid, :salesforce] . 77 | # Default: `[]` 78 | # 79 | # config.external_providers = 80 | 81 | 82 | # You can change it by your local ca_file. i.e. '/etc/pki/tls/certs/ca-bundle.crt' 83 | # Path to ca_file. By default use a internal ca-bundle.crt. 84 | # Default: `'path/to/ca_file'` 85 | # 86 | # config.ca_file = 87 | 88 | 89 | # For information about LinkedIn API: 90 | # - user info fields go to https://developer.linkedin.com/documents/profile-fields 91 | # - access permissions go to https://developer.linkedin.com/documents/authentication#granting 92 | # 93 | # config.linkedin.key = "" 94 | # config.linkedin.secret = "" 95 | # config.linkedin.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=linkedin" 96 | # config.linkedin.user_info_fields = ['first-name', 'last-name'] 97 | # config.linkedin.user_info_mapping = {first_name: "firstName", last_name: "lastName"} 98 | # config.linkedin.access_permissions = ['r_basicprofile'] 99 | # 100 | # 101 | # For information about XING API: 102 | # - user info fields go to https://dev.xing.com/docs/get/users/me 103 | # 104 | # config.xing.key = "" 105 | # config.xing.secret = "" 106 | # config.xing.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=xing" 107 | # config.xing.user_info_mapping = {first_name: "first_name", last_name: "last_name"} 108 | # 109 | # 110 | # Twitter will not accept any requests nor redirect uri containing localhost, 111 | # make sure you use 0.0.0.0:3000 to access your app in development 112 | # 113 | # config.twitter.key = "" 114 | # config.twitter.secret = "" 115 | # config.twitter.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=twitter" 116 | # config.twitter.user_info_mapping = {:email => "screen_name"} 117 | # 118 | # config.facebook.key = "" 119 | # config.facebook.secret = "" 120 | # config.facebook.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=facebook" 121 | # config.facebook.user_info_mapping = {:email => "name"} 122 | # config.facebook.access_permissions = ["email", "publish_actions"] 123 | # config.facebook.display = "page" 124 | # config.facebook.api_version = "v2.2" 125 | # 126 | # config.github.key = "" 127 | # config.github.secret = "" 128 | # config.github.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=github" 129 | # config.github.user_info_mapping = {:email => "name"} 130 | # 131 | # config.google.key = "" 132 | # config.google.secret = "" 133 | # config.google.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=google" 134 | # config.google.user_info_mapping = {:email => "email", :username => "name"} 135 | # 136 | # config.vk.key = "" 137 | # config.vk.secret = "" 138 | # config.vk.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=vk" 139 | # config.vk.user_info_mapping = {:login => "domain", :name => "full_name"} 140 | # 141 | # To use liveid in development mode you have to replace mydomain.com with 142 | # a valid domain even in development. To use a valid domain in development 143 | # simply add your domain in your /etc/hosts file in front of 127.0.0.1 144 | # 145 | # config.liveid.key = "" 146 | # config.liveid.secret = "" 147 | # config.liveid.callback_url = "http://mydomain.com:3000/oauth/callback?provider=liveid" 148 | # config.liveid.user_info_mapping = {:username => "name"} 149 | 150 | # For information about JIRA API: 151 | # https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+OAuth+authentication 152 | # to obtain the consumer key and the public key you can use the jira-ruby gem https://github.com/sumoheavy/jira-ruby 153 | # or run openssl req -x509 -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem to obtain the public key 154 | # Make sure you have configured the application link properly 155 | 156 | # config.jira.key = "1234567" 157 | # config.jira.secret = "jiraTest" 158 | # config.jira.site = "http://localhost:2990/jira/plugins/servlet/oauth" 159 | # config.jira.signature_method = "RSA-SHA1" 160 | # config.jira.private_key_file = "rsakey.pem" 161 | 162 | # For information about Salesforce API: 163 | # https://developer.salesforce.com/signup & 164 | # https://www.salesforce.com/us/developer/docs/api_rest/ 165 | # Salesforce callback_url must be https. You can run the following to generate self-signed ssl cert 166 | # openssl req -new -newkey rsa:2048 -sha1 -days 365 -nodes -x509 -keyout server.key -out server.crt 167 | # Make sure you have configured the application link properly 168 | # config.salesforce.key = '123123' 169 | # config.salesforce.secret = 'acb123' 170 | # config.salesforce.callback_url = "https://127.0.0.1:9292/oauth/callback?provider=salesforce" 171 | # config.salesforce.scope = "full" 172 | # config.salesforce.user_info_mapping = {:email => "email"} 173 | 174 | # --- user config --- 175 | config.user_config do |user| 176 | # -- core -- 177 | # specify username attributes, for example: [:username, :email]. 178 | # Default: `[:email]` 179 | # 180 | # user.username_attribute_names = 181 | 182 | 183 | # change *virtual* password attribute, the one which is used until an encrypted one is generated. 184 | # Default: `:password` 185 | # 186 | # user.password_attribute_name = 187 | 188 | 189 | # downcase the username before trying to authenticate, default is false 190 | # Default: `false` 191 | # 192 | # user.downcase_username_before_authenticating = 193 | 194 | 195 | # change default email attribute. 196 | # Default: `:email` 197 | # 198 | # user.email_attribute_name = 199 | 200 | 201 | # change default crypted_password attribute. 202 | # Default: `:crypted_password` 203 | # 204 | # user.crypted_password_attribute_name = 205 | 206 | 207 | # what pattern to use to join the password with the salt 208 | # Default: `""` 209 | # 210 | # user.salt_join_token = 211 | 212 | 213 | # change default salt attribute. 214 | # Default: `:salt` 215 | # 216 | # user.salt_attribute_name = 217 | 218 | 219 | # how many times to apply encryption to the password. 220 | # Default: `nil` 221 | # 222 | # user.stretches = 223 | 224 | 225 | # encryption key used to encrypt reversible encryptions such as AES256. 226 | # WARNING: If used for users' passwords, changing this key will leave passwords undecryptable! 227 | # Default: `nil` 228 | # 229 | # user.encryption_key = 230 | 231 | 232 | # use an external encryption class. 233 | # Default: `nil` 234 | # 235 | # user.custom_encryption_provider = 236 | 237 | 238 | # encryption algorithm name. See 'encryption_algorithm=' for available options. 239 | # Default: `:bcrypt` 240 | # 241 | # user.encryption_algorithm = 242 | 243 | 244 | # make this configuration inheritable for subclasses. Useful for ActiveRecord's STI. 245 | # Default: `false` 246 | # 247 | # user.subclasses_inherit_config = 248 | 249 | 250 | # -- remember_me -- 251 | # How long in seconds the session length will be 252 | # Default: `604800` 253 | # 254 | # user.remember_me_for = 255 | 256 | 257 | # -- user_activation -- 258 | # the attribute name to hold activation state (active/pending). 259 | # Default: `:activation_state` 260 | # 261 | # user.activation_state_attribute_name = 262 | 263 | 264 | # the attribute name to hold activation code (sent by email). 265 | # Default: `:activation_token` 266 | # 267 | # user.activation_token_attribute_name = 268 | 269 | 270 | # the attribute name to hold activation code expiration date. 271 | # Default: `:activation_token_expires_at` 272 | # 273 | # user.activation_token_expires_at_attribute_name = 274 | 275 | 276 | # how many seconds before the activation code expires. nil for never expires. 277 | # Default: `nil` 278 | # 279 | # user.activation_token_expiration_period = 280 | 281 | 282 | # your mailer class. Required. 283 | # Default: `nil` 284 | # 285 | user.user_activation_mailer = UserMailer 286 | 287 | 288 | # when true sorcery will not automatically 289 | # email activation details and allow you to 290 | # manually handle how and when email is sent. 291 | # Default: `false` 292 | # 293 | # user.activation_mailer_disabled = 294 | 295 | 296 | # activation needed email method on your mailer class. 297 | # Default: `:activation_needed_email` 298 | # 299 | # user.activation_needed_email_method_name = 300 | 301 | 302 | # activation success email method on your mailer class. 303 | # Default: `:activation_success_email` 304 | # 305 | # user.activation_success_email_method_name = 306 | 307 | 308 | # do you want to prevent or allow users that did not activate by email to login? 309 | # Default: `true` 310 | # 311 | # user.prevent_non_active_users_to_login = 312 | 313 | 314 | # -- reset_password -- 315 | # reset password code attribute name. 316 | # Default: `:reset_password_token` 317 | # 318 | # user.reset_password_token_attribute_name = 319 | 320 | 321 | # expires at attribute name. 322 | # Default: `:reset_password_token_expires_at` 323 | # 324 | # user.reset_password_token_expires_at_attribute_name = 325 | 326 | 327 | # when was email sent, used for hammering protection. 328 | # Default: `:reset_password_email_sent_at` 329 | # 330 | # user.reset_password_email_sent_at_attribute_name = 331 | 332 | 333 | # mailer class. Needed. 334 | # Default: `nil` 335 | # 336 | user.reset_password_mailer = UserMailer 337 | 338 | 339 | # reset password email method on your mailer class. 340 | # Default: `:reset_password_email` 341 | # 342 | # user.reset_password_email_method_name = 343 | 344 | 345 | # when true sorcery will not automatically 346 | # email password reset details and allow you to 347 | # manually handle how and when email is sent 348 | # Default: `false` 349 | # 350 | # user.reset_password_mailer_disabled = 351 | 352 | 353 | # how many seconds before the reset request expires. nil for never expires. 354 | # Default: `nil` 355 | # 356 | # user.reset_password_expiration_period = 357 | 358 | 359 | # hammering protection, how long in seconds to wait before allowing another email to be sent. 360 | # Default: `5 * 60` 361 | # 362 | # user.reset_password_time_between_emails = 363 | 364 | 365 | # -- brute_force_protection -- 366 | # Failed logins attribute name. 367 | # Default: `:failed_logins_count` 368 | # 369 | # user.failed_logins_count_attribute_name = 370 | 371 | 372 | # This field indicates whether user is banned and when it will be active again. 373 | # Default: `:lock_expires_at` 374 | # 375 | # user.lock_expires_at_attribute_name = 376 | 377 | 378 | # How many failed logins allowed. 379 | # Default: `50` 380 | # 381 | # user.consecutive_login_retries_amount_limit = 382 | 383 | 384 | # How long the user should be banned. in seconds. 0 for permanent. 385 | # Default: `60 * 60` 386 | # 387 | # user.login_lock_time_period = 388 | 389 | # Unlock token attribute name 390 | # Default: `:unlock_token` 391 | # 392 | # user.unlock_token_attribute_name = 393 | 394 | # Unlock token mailer method 395 | # Default: `:send_unlock_token_email` 396 | # 397 | # user.unlock_token_email_method_name = 398 | 399 | # when true sorcery will not automatically 400 | # send email with unlock token 401 | # Default: `false` 402 | # 403 | # user.unlock_token_mailer_disabled = true 404 | 405 | # Unlock token mailer class 406 | # Default: `nil` 407 | # 408 | # user.unlock_token_mailer = UserMailer 409 | 410 | # -- activity logging -- 411 | # Last login attribute name. 412 | # Default: `:last_login_at` 413 | # 414 | # user.last_login_at_attribute_name = 415 | 416 | 417 | # Last logout attribute name. 418 | # Default: `:last_logout_at` 419 | # 420 | # user.last_logout_at_attribute_name = 421 | 422 | 423 | # Last activity attribute name. 424 | # Default: `:last_activity_at` 425 | # 426 | # user.last_activity_at_attribute_name = 427 | 428 | 429 | # How long since last activity is the user defined logged out? 430 | # Default: `10 * 60` 431 | # 432 | # user.activity_timeout = 433 | 434 | 435 | # -- external -- 436 | # Class which holds the various external provider data for this user. 437 | # Default: `nil` 438 | # 439 | # user.authentications_class = 440 | 441 | 442 | # User's identifier in authentications class. 443 | # Default: `:user_id` 444 | # 445 | # user.authentications_user_id_attribute_name = 446 | 447 | 448 | # Provider's identifier in authentications class. 449 | # Default: `:provider` 450 | # 451 | # user.provider_attribute_name = 452 | 453 | 454 | # User's external unique identifier in authentications class. 455 | # Default: `:uid` 456 | # 457 | # user.provider_uid_attribute_name = 458 | end 459 | 460 | # This line must come after the 'user config' block. 461 | # Define which model authenticates with sorcery. 462 | config.user_class = "User" 463 | end 464 | -------------------------------------------------------------------------------- /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] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /config/locales/simple_form.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | simple_form: 3 | "yes": 'Yes' 4 | "no": 'No' 5 | required: 6 | text: 'required' 7 | mark: '*' 8 | # You can uncomment the line below if you need to overwrite the whole required html. 9 | # When using html, text and mark won't be used. 10 | # html: '*' 11 | error_notification: 12 | default_message: "Please review the problems below:" 13 | # Examples 14 | # labels: 15 | # defaults: 16 | # password: 'Password' 17 | # user: 18 | # new: 19 | # email: 'E-mail to sign in.' 20 | # edit: 21 | # email: 'E-mail.' 22 | # hints: 23 | # defaults: 24 | # username: 'User name to sign in.' 25 | # password: 'No special characters, please.' 26 | # include_blanks: 27 | # defaults: 28 | # age: 'Rather not say' 29 | # prompts: 30 | # defaults: 31 | # age: 'Select your age' 32 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | namespace :admin do 3 | root 'post_versions#index' 4 | 5 | resources :post_versions, except: :destroy do 6 | member do 7 | get :edit_decline 8 | put :approve 9 | put :decline 10 | end 11 | end 12 | end 13 | 14 | # The priority is based upon order of creation: first created -> highest priority. 15 | # See how all your routes lay out with "rake routes". 16 | 17 | # You can have the root of your site routed with "root" 18 | # root 'welcome#index' 19 | root 'posts#index' 20 | # Example of regular route: 21 | # get 'products/:id' => 'catalog#view' 22 | 23 | # Example of named route that can be invoked with purchase_url(id: product.id) 24 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase 25 | 26 | # Example resource route (maps HTTP verbs to controller actions automatically): 27 | # resources :products 28 | 29 | resources :posts do 30 | collection do 31 | post :preview 32 | put :add_to_favorites 33 | put :vote 34 | 35 | get '/:state', action: :posts_list, state: /draft|on_moderation|declined/ 36 | end 37 | 38 | resources :comments 39 | end 40 | 41 | resources :post_versions do 42 | member do 43 | put :send_to_moderation 44 | end 45 | end 46 | 47 | resources :sessions, only: [:new, :create, :destroy] 48 | resources :password_resets 49 | 50 | resources :users, except: :destroy do 51 | member do 52 | get :activate 53 | end 54 | end 55 | 56 | get 'login' => 'sessions#new', as: :login 57 | post 'logout' => 'sessions#destroy', as: :logout 58 | 59 | # Example resource route with options: 60 | # resources :products do 61 | # member do 62 | # get 'short' 63 | # post 'toggle' 64 | # end 65 | # 66 | # collection do 67 | # get 'sold' 68 | # end 69 | # end 70 | 71 | # Example resource route with sub-resources: 72 | # resources :products do 73 | # resources :comments, :sales 74 | # resource :seller 75 | # end 76 | 77 | # Example resource route with more complex sub-resources: 78 | # resources :products do 79 | # resources :comments 80 | # resources :sales do 81 | # get 'recent', on: :collection 82 | # end 83 | # end 84 | 85 | # Example resource route with concerns: 86 | # concern :toggleable do 87 | # post 'toggle' 88 | # end 89 | # resources :posts, concerns: :toggleable 90 | # resources :photos, concerns: :toggleable 91 | 92 | # Example resource route within a namespace: 93 | # namespace :admin do 94 | # # Directs /admin/products/* to Admin::ProductsController 95 | # # (app/controllers/admin/products_controller.rb) 96 | # resources :products 97 | # end 98 | end 99 | -------------------------------------------------------------------------------- /config/secrets.yml.example: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 367044cde5c27da34905be8cc3c410949928858f0f6f14abad23e85fbfce4013ae20e5f1566b6ed49266412f941b5fc4ab6b950a74f5ae76127bac034e5a5c90 15 | 16 | test: 17 | secret_key_base: 409632f45bc4f5180754f8a09ff803d94fd834cfecacbc7cef8c6209ddfd9ab7e1120cb5bb6ecc8638722f036a1b35d65a74972de534c5a49b5da49a821ddfbe 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /db/migrate/20151031194302_create_posts.rb: -------------------------------------------------------------------------------- 1 | class CreatePosts < ActiveRecord::Migration 2 | def change 3 | create_table :posts do |t| 4 | t.string :title_ru, null: false 5 | t.string :title_en, null: false 6 | t.text :body_ru, null: false 7 | t.text :body_en, null: false 8 | t.string :source, null: false 9 | t.boolean :is_published, null: false, default: false 10 | t.integer :version, null: false, default: 0 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20151103095347_create_comments.rb: -------------------------------------------------------------------------------- 1 | class CreateComments < ActiveRecord::Migration 2 | def change 3 | create_table :comments do |t| 4 | t.integer :parent_id 5 | t.integer :post_id, null: false, foreign_key: true 6 | t.integer :user_id, null: false, foreign_key: true 7 | t.text :body 8 | t.string :ip 9 | t.boolean :is_hidden 10 | 11 | t.timestamps 12 | end 13 | 14 | add_index :comments, :post_id 15 | add_index :comments, :user_id 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20151111154655_sorcery_core.rb: -------------------------------------------------------------------------------- 1 | class SorceryCore < ActiveRecord::Migration 2 | def change 3 | create_table :users do |t| 4 | t.string :first_name 5 | t.string :last_name 6 | t.date :birth_date 7 | t.string :email, null: false 8 | t.string :crypted_password 9 | t.string :salt 10 | t.integer :login_count, default: 0, null: false 11 | t.boolean :is_active, default: false, null: false 12 | 13 | t.timestamps 14 | end 15 | 16 | add_index :users, :email, unique: true 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20151111154656_sorcery_remember_me.rb: -------------------------------------------------------------------------------- 1 | class SorceryRememberMe < ActiveRecord::Migration 2 | def change 3 | add_column :users, :remember_me_token, :string, :default => nil 4 | add_column :users, :remember_me_token_expires_at, :datetime, :default => nil 5 | 6 | add_index :users, :remember_me_token 7 | end 8 | end -------------------------------------------------------------------------------- /db/migrate/20151112094518_sorcery_user_activation.rb: -------------------------------------------------------------------------------- 1 | class SorceryUserActivation < ActiveRecord::Migration 2 | def change 3 | add_column :users, :activation_state, :string, :default => nil 4 | add_column :users, :activation_token, :string, :default => nil 5 | add_column :users, :activation_token_expires_at, :datetime, :default => nil 6 | 7 | add_index :users, :activation_token 8 | end 9 | end -------------------------------------------------------------------------------- /db/migrate/20151112123948_add_is_admin_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddIsAdminToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :is_admin, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151112210217_sorcery_reset_password.rb: -------------------------------------------------------------------------------- 1 | class SorceryResetPassword < ActiveRecord::Migration 2 | def change 3 | add_column :users, :reset_password_token, :string, :default => nil 4 | add_column :users, :reset_password_token_expires_at, :datetime, :default => nil 5 | add_column :users, :reset_password_email_sent_at, :datetime, :default => nil 6 | 7 | add_index :users, :reset_password_token 8 | end 9 | end -------------------------------------------------------------------------------- /db/migrate/20151114124649_change_users_fields.rb: -------------------------------------------------------------------------------- 1 | class ChangeUsersFields < ActiveRecord::Migration 2 | def up 3 | remove_column :users, :is_active 4 | add_column :users, :deleted_at, :datetime 5 | end 6 | 7 | def down 8 | add_column :users, :is_active, :boolean 9 | remove_column :users, :deleted_at 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20151203181509_add_tree_to_comments.rb: -------------------------------------------------------------------------------- 1 | class AddTreeToComments < ActiveRecord::Migration 2 | def change 3 | add_column :comments, :tree, :ltree 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160120135250_change_posts_fields.rb: -------------------------------------------------------------------------------- 1 | class ChangePostsFields < ActiveRecord::Migration 2 | def up 3 | remove_columns :posts, :title_ru, :body_ru 4 | 5 | rename_column :posts, :title_en, :title 6 | rename_column :posts, :body_en, :body 7 | end 8 | 9 | def down 10 | add_column :posts, :title_ru, :string 11 | add_column :posts, :body_ru, :text 12 | 13 | rename_column :posts, :title, :title_en 14 | rename_column :posts, :body, :body_en 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20160121170807_remove_source_from_posts.rb: -------------------------------------------------------------------------------- 1 | class RemoveSourceFromPosts < ActiveRecord::Migration 2 | def change 3 | remove_column :posts, :source, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160125104823_add_favorite_posts_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddFavoritePostsToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :favorite_posts, :integer, default: [], array: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160126092912_add_user_ref_to_posts.rb: -------------------------------------------------------------------------------- 1 | class AddUserRefToPosts < ActiveRecord::Migration 2 | def change 3 | add_reference :posts, :user, index: true, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160128113543_create_post_votes.rb: -------------------------------------------------------------------------------- 1 | class CreatePostVotes < ActiveRecord::Migration 2 | def change 3 | create_table :post_votes do |t| 4 | t.references :user, index: true, foreign_key: true, null: false 5 | t.references :post, index: true, foreign_key: true, null: false 6 | t.integer :rate, default: 0, null: false 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20160204091437_create_post_versions.rb: -------------------------------------------------------------------------------- 1 | class CreatePostVersions < ActiveRecord::Migration 2 | def change 3 | create_table :post_versions do |t| 4 | t.references :user, index: true, foreign_key: true, null: false 5 | t.string :title, null: false 6 | t.text :body, null: false 7 | t.string :aasm_state 8 | t.string :decline_reason 9 | t.timestamps null: false 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20160204092842_add_post_version_ref_to_posts.rb: -------------------------------------------------------------------------------- 1 | class AddPostVersionRefToPosts < ActiveRecord::Migration 2 | def change 3 | add_reference :posts, :post_version, index: true, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160204125131_add_inherit_version_id_to_post_versions.rb: -------------------------------------------------------------------------------- 1 | class AddInheritVersionIdToPostVersions < ActiveRecord::Migration 2 | def change 3 | add_column :post_versions, :inherit_version_id, :integer, default: 0, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160204171926_remove_fields_from_posts.rb: -------------------------------------------------------------------------------- 1 | class RemoveFieldsFromPosts < ActiveRecord::Migration 2 | def up 3 | remove_columns :posts, :title, :body, :version 4 | end 5 | 6 | def down 7 | add_column :posts, :title, :string 8 | add_column :posts, :body, :text 9 | add_column :posts, :version, :integer 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20160204171926) do 15 | 16 | # These are extensions that must be enabled in order to support this database 17 | enable_extension "plpgsql" 18 | enable_extension "ltree" 19 | 20 | create_table "comments", force: :cascade do |t| 21 | t.integer "parent_id" 22 | t.integer "post_id", null: false 23 | t.integer "user_id", null: false 24 | t.text "body" 25 | t.string "ip" 26 | t.boolean "is_hidden" 27 | t.datetime "created_at" 28 | t.datetime "updated_at" 29 | t.ltree "tree" 30 | end 31 | 32 | add_index "comments", ["post_id"], name: "index_comments_on_post_id", using: :btree 33 | add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree 34 | 35 | create_table "post_versions", force: :cascade do |t| 36 | t.integer "user_id", null: false 37 | t.string "title", null: false 38 | t.text "body", null: false 39 | t.string "aasm_state" 40 | t.string "decline_reason" 41 | t.datetime "created_at", null: false 42 | t.datetime "updated_at", null: false 43 | t.integer "inherit_version_id", default: 0, null: false 44 | end 45 | 46 | add_index "post_versions", ["user_id"], name: "index_post_versions_on_user_id", using: :btree 47 | 48 | create_table "post_votes", force: :cascade do |t| 49 | t.integer "user_id", null: false 50 | t.integer "post_id", null: false 51 | t.integer "rate", default: 0, null: false 52 | t.datetime "created_at" 53 | t.datetime "updated_at" 54 | end 55 | 56 | add_index "post_votes", ["post_id"], name: "index_post_votes_on_post_id", using: :btree 57 | add_index "post_votes", ["user_id"], name: "index_post_votes_on_user_id", using: :btree 58 | 59 | create_table "posts", force: :cascade do |t| 60 | t.boolean "is_published", default: false, null: false 61 | t.datetime "created_at" 62 | t.datetime "updated_at" 63 | t.integer "user_id" 64 | t.integer "post_version_id" 65 | end 66 | 67 | add_index "posts", ["post_version_id"], name: "index_posts_on_post_version_id", using: :btree 68 | add_index "posts", ["user_id"], name: "index_posts_on_user_id", using: :btree 69 | 70 | create_table "users", force: :cascade do |t| 71 | t.string "first_name" 72 | t.string "last_name" 73 | t.date "birth_date" 74 | t.string "email", null: false 75 | t.string "crypted_password" 76 | t.string "salt" 77 | t.integer "login_count", default: 0, null: false 78 | t.datetime "created_at" 79 | t.datetime "updated_at" 80 | t.string "remember_me_token" 81 | t.datetime "remember_me_token_expires_at" 82 | t.string "activation_state" 83 | t.string "activation_token" 84 | t.datetime "activation_token_expires_at" 85 | t.boolean "is_admin", default: false 86 | t.string "reset_password_token" 87 | t.datetime "reset_password_token_expires_at" 88 | t.datetime "reset_password_email_sent_at" 89 | t.datetime "deleted_at" 90 | t.integer "favorite_posts", default: [], array: true 91 | end 92 | 93 | add_index "users", ["activation_token"], name: "index_users_on_activation_token", using: :btree 94 | add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree 95 | add_index "users", ["remember_me_token"], name: "index_users_on_remember_me_token", using: :btree 96 | add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", using: :btree 97 | 98 | add_foreign_key "post_versions", "users" 99 | add_foreign_key "post_votes", "posts" 100 | add_foreign_key "post_votes", "users" 101 | add_foreign_key "posts", "post_versions" 102 | add_foreign_key "posts", "users" 103 | end 104 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/templates/haml/scaffold/_form.html.haml: -------------------------------------------------------------------------------- 1 | = simple_form_for(@<%= singular_table_name %>) do |f| 2 | = f.error_notification 3 | 4 | .form-inputs 5 | <%- attributes.each do |attribute| -%> 6 | = f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> 7 | <%- end -%> 8 | 9 | .form-actions 10 | = f.button :submit 11 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/log/.keep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

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

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

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

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.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 | -------------------------------------------------------------------------------- /spec/factories/common.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | sequence(:email) { |n| "test_email_#{n}@zvlex.me" } 3 | 4 | factory :user do 5 | first_name 'Alexander' 6 | last_name 'Zutikov' 7 | email { generate(:email) } 8 | password 'secret' 9 | password_confirmation 'secret' 10 | end 11 | 12 | factory :post_version do 13 | user 14 | title 'Lorem Ipsum' 15 | body 'The standard Lorem Ipsum' 16 | 17 | factory :on_moderation_post_version do 18 | aasm_state 'on_moderation' 19 | end 20 | 21 | factory :approved_post_version do 22 | aasm_state 'approved' 23 | end 24 | 25 | factory :declined_post_version do 26 | aasm_state 'declined' 27 | end 28 | end 29 | 30 | factory :post do 31 | user 32 | post_version 33 | end 34 | 35 | factory :post_vote do 36 | user 37 | post 38 | rate 1 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/models/post_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Post do 4 | USERS_QUANTITY = 3 5 | 6 | let(:post) { create(:post) } 7 | let(:rand_array) { [-1, 0, 1, 1, 0, 1, -1] } 8 | 9 | it "selects post's favorites quantity" do 10 | USERS_QUANTITY.times do 11 | user = create(:user) 12 | user.add_or_drop_favorites!(post) 13 | end 14 | 15 | expect(post.favorites_quantity).to eq(3) 16 | end 17 | 18 | it 'counts post rating quantity' do 19 | rand_array.size.times do |num| 20 | user = create(:user) 21 | user.rate_post(post, rand_array[num]) 22 | end 23 | 24 | array_quantity = rand_array.sum 25 | 26 | expect(post.rating_quantity).to eq(array_quantity) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/models/post_version_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe PostVersion, type: :model do 4 | let(:user) { create(:user) } 5 | let(:post_version) { create(:post_version, user: user) } 6 | let(:approved_post_version) { create(:approved_post_version) } 7 | let(:on_moderation_post_version) { create(:on_moderation_post_version) } 8 | let!(:post) { create(:post, post_version: approved_post_version, user: user) } 9 | 10 | it 'creates new post version draft' do 11 | expect(post_version.aasm_state).to eq('draft') 12 | end 13 | 14 | describe '#update' do 15 | 16 | context 'when approved' do 17 | it 'creates new draft with inherit_version_id' do 18 | params = { title: 'Lorem Ipsum', body: 'Sed eget vestibulum risus' } 19 | new_post_version = approved_post_version.update_or_create_post_version!(params) 20 | 21 | expect(new_post_version.inherit_version_id).to eq(approved_post_version.id) 22 | end 23 | end 24 | 25 | context 'when is draft' do 26 | it 'updates current draft' do 27 | params = { title: 'Lorem Ipsum', body: 'Sed eget vestibulum risus' } 28 | post_version.update_or_create_post_version!(params) 29 | 30 | expect(post_version.title).to eq('Lorem Ipsum') 31 | end 32 | end 33 | end 34 | 35 | describe '#approve' do 36 | context 'when admin approves post version' do 37 | it 'creates post' do 38 | on_moderation_post_version.approve! 39 | 40 | expect(on_moderation_post_version.post).not_to be_nil 41 | end 42 | 43 | it 'updates post' do 44 | new_post_version = create(:on_moderation_post_version, inherit_version_id: approved_post_version.id, user: user) 45 | new_post_version.approve! 46 | post.reload 47 | 48 | expect(post.post_version).to eq(new_post_version) 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe User do 4 | let(:user) { create(:user) } 5 | let(:post) { create(:post) } 6 | let(:rate) { rand(-1..1) } 7 | 8 | context 'when pressed favorite button' do 9 | it 'drops favorite posts' do 10 | user.favorite_posts = [post.id] 11 | user.drop_favorites(post) 12 | 13 | expect(user.favorite_posts).to eq([]) 14 | end 15 | 16 | it 'adds favorite posts' do 17 | user.favorite_posts = [] 18 | user.add_favorites(post) 19 | 20 | expect(user.favorite_posts).not_to be_empty 21 | end 22 | 23 | it 'checks if post are included' do 24 | user.favorite_posts = [post.id] 25 | included = user.included_in_favorites?(post) 26 | 27 | expect(included).to be(true) 28 | end 29 | end 30 | 31 | context 'when user votes post' do 32 | specify 'skip or vote up/down' do 33 | user.rate_post(post, rate) 34 | 35 | expect(user.post_votes.map(&:rate)).to eq([rate]) 36 | end 37 | 38 | it "does not vote if user already voted" do 39 | create(:post_vote, user: user, post: post, rate: rate) 40 | user.rate_post(post, rate) 41 | 42 | expect(user.post_votes.size).to be(1) 43 | end 44 | 45 | specify 'several users voted' do 46 | 3.times do 47 | create(:post_vote, post: post, rate: rate) 48 | end 49 | 50 | expect(post.user_votes.size).to eq(3) 51 | end 52 | 53 | specify 'user can not vote his post' do 54 | post = create(:post, user: user) 55 | user.rate_post(post, rate) 56 | 57 | expect(user.post_votes).to be_empty 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV['RAILS_ENV'] ||= 'test' 3 | require File.expand_path('../../config/environment', __FILE__) 4 | # Prevent database truncation if the environment is production 5 | abort("The Rails environment is running in production mode!") if Rails.env.production? 6 | require 'spec_helper' 7 | require 'factory_girl' 8 | require 'rspec/rails' 9 | # Add additional requires below this line. Rails is not loaded until this point! 10 | 11 | # Requires supporting ruby files with custom matchers and macros, etc, in 12 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 13 | # run as spec files by default. This means that files in spec/support that end 14 | # in _spec.rb will both be required and run as specs, causing the specs to be 15 | # run twice. It is recommended that you do not name files matching this glob to 16 | # end with _spec.rb. You can configure this pattern with the --pattern 17 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 18 | # 19 | # The following line is provided for convenience purposes. It has the downside 20 | # of increasing the boot-up time by auto-requiring all files in the support 21 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 22 | # require only the support files necessary. 23 | # 24 | # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 25 | 26 | # Checks for pending migration and applies them before tests are run. 27 | # If you are not using ActiveRecord, you can remove this line. 28 | ActiveRecord::Migration.maintain_test_schema! 29 | 30 | RSpec.configure do |config| 31 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 32 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 33 | 34 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 35 | # examples within a transaction, remove the following line or assign false 36 | # instead of true. 37 | config.use_transactional_fixtures = true 38 | config.include FactoryGirl::Syntax::Methods 39 | 40 | # RSpec Rails can automatically mix in different behaviours to your tests 41 | # based on their file location, for example enabling you to call `get` and 42 | # `post` in specs under `spec/controllers`. 43 | # 44 | # You can disable this behaviour by removing the line below, and instead 45 | # explicitly tag your specs with their type, e.g.: 46 | # 47 | # RSpec.describe UsersController, :type => :controller do 48 | # # ... 49 | # end 50 | # 51 | # The different available types are documented in the features, such as in 52 | # https://relishapp.com/rspec/rspec-rails/docs 53 | config.infer_spec_type_from_file_location! 54 | 55 | # Filter lines from Rails gems in backtraces. 56 | config.filter_rails_from_backtrace! 57 | # arbitrary gems may also be filtered via: 58 | # config.filter_gems_from_backtrace("gem name") 59 | end 60 | -------------------------------------------------------------------------------- /spec/services/markdown_parser_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe MarkdownParser do 4 | it 'convers markdown tags to html' do 5 | md_title = '# title' 6 | expect(MarkdownParser.new(md_title).to_html.strip).to eq('

title

') 7 | end 8 | 9 | it 'wraps simple text to paragraph' do 10 | simple_text = 'hello' 11 | expect(MarkdownParser.new(simple_text).to_html.strip).to eq('

hello

') 12 | end 13 | 14 | it 'filters ' 16 | expect(MarkdownParser.new(script_tag).to_html.strip).to eq('

alert(1)

') 17 | end 18 | end 19 | 20 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # The `.rspec` file also contains a few flags that are not defaults but that 16 | # users commonly want. 17 | # 18 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 19 | RSpec.configure do |config| 20 | # rspec-expectations config goes here. You can use an alternate 21 | # assertion/expectation library such as wrong or the stdlib/minitest 22 | # assertions if you prefer. 23 | config.expect_with :rspec do |expectations| 24 | # This option will default to `true` in RSpec 4. It makes the `description` 25 | # and `failure_message` of custom matchers include text for helper methods 26 | # defined using `chain`, e.g.: 27 | # be_bigger_than(2).and_smaller_than(4).description 28 | # # => "be bigger than 2 and smaller than 4" 29 | # ...rather than: 30 | # # => "be bigger than 2" 31 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 32 | end 33 | 34 | # rspec-mocks config goes here. You can use an alternate test double 35 | # library (such as bogus or mocha) by changing the `mock_with` option here. 36 | config.mock_with :rspec do |mocks| 37 | # Prevents you from mocking or stubbing a method that does not exist on 38 | # a real object. This is generally recommended, and will default to 39 | # `true` in RSpec 4. 40 | mocks.verify_partial_doubles = true 41 | end 42 | 43 | # The settings below are suggested to provide a good initial experience 44 | # with RSpec, but feel free to customize to your heart's content. 45 | =begin 46 | # These two settings work together to allow you to limit a spec run 47 | # to individual examples or groups you care about by tagging them with 48 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 49 | # get run. 50 | config.filter_run :focus 51 | config.run_all_when_everything_filtered = true 52 | 53 | # Allows RSpec to persist some state between runs in order to support 54 | # the `--only-failures` and `--next-failure` CLI options. We recommend 55 | # you configure your source control system to ignore this file. 56 | config.example_status_persistence_file_path = "spec/examples.txt" 57 | 58 | # Limits the available syntax to the non-monkey patched syntax that is 59 | # recommended. For more details, see: 60 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 61 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 62 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 63 | config.disable_monkey_patching! 64 | 65 | # Many RSpec users commonly either run the entire suite or an individual 66 | # file, and it's useful to allow more verbose output when running an 67 | # individual spec file. 68 | if config.files_to_run.one? 69 | # Use the documentation formatter for detailed output, 70 | # unless a formatter has already been configured 71 | # (e.g. via a command-line flag). 72 | config.default_formatter = 'doc' 73 | end 74 | 75 | # Print the 10 slowest examples and example groups at the 76 | # end of the spec run, to help surface which specs are running 77 | # particularly slow. 78 | config.profile_examples = 10 79 | 80 | # Run specs in random order to surface order dependencies. If you find an 81 | # order dependency and want to debug it, you can fix the order by providing 82 | # the seed, which is printed after each run. 83 | # --seed 1234 84 | config.order = :random 85 | 86 | # Seed global randomization in this process using the `--seed` CLI option. 87 | # Setting this allows you to use `--seed` to deterministically reproduce 88 | # test failures related to randomization by passing the same `--seed` value 89 | # as the one that triggered the failure. 90 | Kernel.srand config.seed 91 | =end 92 | end 93 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/javascripts/notify.min.js: -------------------------------------------------------------------------------- 1 | (function(e){typeof define=="function"&&define.amd?define(["jquery"],e):typeof module=="object"&&module.exports?module.exports=function(t,n){return n===undefined&&(typeof window!="undefined"?n=require("jquery"):n=require("jquery")(t)),e(n),n}:e(jQuery)})(function(e){function L(t,n,i){typeof i=="string"&&(i={className:i}),this.options=w(b,e.isPlainObject(i)?i:{}),this.loadHTML(),this.wrapper=e(h.html),this.options.clickToHide&&this.wrapper.addClass(r+"-hidable"),this.wrapper.data(r,this),this.arrow=this.wrapper.find("."+r+"-arrow"),this.container=this.wrapper.find("."+r+"-container"),this.container.append(this.userContainer),t&&t.length&&(this.elementType=t.attr("type"),this.originalElement=t,this.elem=T(t),this.elem.data(r,this),this.elem.before(this.wrapper)),this.container.hide(),this.run(n)}var t=[].indexOf||function(e){for(var t=0,n=this.length;t\n
\n
\n',css:"."+r+"-corner {\n position: fixed;\n margin: 5px;\n z-index: 1050;\n}\n\n."+r+"-corner ."+r+"-wrapper,\n."+r+"-corner ."+r+"-container {\n position: relative;\n display: block;\n height: inherit;\n width: inherit;\n margin: 3px;\n}\n\n."+r+"-wrapper {\n z-index: 1;\n position: absolute;\n display: inline-block;\n height: 0;\n width: 0;\n}\n\n."+r+"-container {\n display: none;\n z-index: 1;\n position: absolute;\n}\n\n."+r+"-hidable {\n cursor: pointer;\n}\n\n[data-notify-text],[data-notify-html] {\n position: relative;\n}\n\n."+r+"-arrow {\n position: absolute;\n z-index: 2;\n width: 0;\n height: 0;\n}"},p={"border-radius":["-webkit-","-moz-"]},d=function(e){return c[e]},v=function(t,i){if(!t)throw"Missing Style name";if(!i)throw"Missing Style definition";if(!i.html)throw"Missing Style HTML";var s=c[t];s&&s.cssElem&&(window.console&&console.warn(n+": overwriting style '"+t+"'"),c[t].cssElem.remove()),i.name=t,c[t]=i;var o="";i.classes&&e.each(i.classes,function(t,n){return o+="."+r+"-"+i.name+"-"+t+" {\n",e.each(n,function(t,n){return p[t]&&e.each(p[t],function(e,r){return o+=" "+r+t+": "+n+";\n"}),o+=" "+t+": "+n+";\n"}),o+="}\n"}),i.css&&(o+="/* styles for "+i.name+" */\n"+i.css),o&&(i.cssElem=m(o),i.cssElem.attr("id","notify-"+i.name));var u={},a=e(i.html);g("html",a,u),g("text",a,u),i.fields=u},m=function(t){var n,r,i;r=S("style"),r.attr("type","text/css"),e("head").append(r);try{r.html(t)}catch(s){r[0].styleSheet.cssText=t}return r},g=function(t,n,r){var s;return t!=="html"&&(t="text"),s="data-notify-"+t,y(n,"["+s+"]").each(function(){var n;n=e(this).attr(s),n||(n=i),r[n]=t})},y=function(e,t){return e.is(t)?e:e.find(t)},b={clickToHide:!0,autoHide:!0,autoHideDelay:5e3,arrowShow:!0,arrowSize:5,breakNewLines:!0,elementPosition:"bottom",globalPosition:"top right",style:"bootstrap",className:"error",showAnimation:"slideDown",showDuration:400,hideAnimation:"slideUp",hideDuration:200,gap:5},w=function(t,n){var r;return r=function(){},r.prototype=t,e.extend(!0,new r,n)},E=function(t){return e.extend(b,t)},S=function(t){return e("<"+t+">")},x={},T=function(t){var n;return t.is("[type=radio]")&&(n=t.parents("form:first").find("[type=radio]").filter(function(n,r){return e(r).attr("name")===t.attr("name")}),t=n.first()),t},N=function(e,t,n){var r,i;if(typeof n=="string")n=parseInt(n,10);else if(typeof n!="number")return;if(isNaN(n))return;return r=s[f[t.charAt(0)]],i=t,e[r]!==undefined&&(t=s[r.charAt(0)],n=-n),e[t]===undefined?e[t]=n:e[t]+=n,null},C=function(e,t,n){if(e==="l"||e==="t")return 0;if(e==="c"||e==="m")return n/2-t/2;if(e==="r"||e==="b")return n-t;throw"Invalid alignment"},k=function(e){return k.e=k.e||S("div"),k.e.text(e).html()};L.prototype.loadHTML=function(){var t;t=this.getStyle(),this.userContainer=e(t.html),this.userFields=t.fields},L.prototype.show=function(e,t){var n,r,i,s,o;r=function(n){return function(){!e&&!n.elem&&n.destroy();if(t)return t()}}(this),o=this.container.parent().parents(":hidden").length>0,i=this.container.add(this.arrow),n=[];if(o&&e)s="show";else if(o&&!e)s="hide";else if(!o&&e)s=this.options.showAnimation,n.push(this.options.showDuration);else{if(!!o||!!e)return r();s=this.options.hideAnimation,n.push(this.options.hideDuration)}return n.push(r),i[s].apply(i,n)},L.prototype.setGlobalPosition=function(){var t=this.getPosition(),n=t[0],i=t[1],o=s[n],u=s[i],a=n+"|"+i,f=x[a];if(!f){f=x[a]=S("div");var l={};l[o]=0,u==="middle"?l.top="45%":u==="center"?l.left="45%":l[u]=0,f.css(l).addClass(r+"-corner"),e("body").append(f)}return f.prepend(this.wrapper)},L.prototype.setElementPosition=function(){var n,r,i,l,c,h,p,d,v,m,g,y,b,w,E,S,x,T,k,L,A,O,M,_,D,P,H,B,j;H=this.getPosition(),_=H[0],O=H[1],M=H[2],g=this.elem.position(),d=this.elem.outerHeight(),y=this.elem.outerWidth(),v=this.elem.innerHeight(),m=this.elem.innerWidth(),j=this.wrapper.position(),c=this.container.height(),h=this.container.width(),T=s[_],L=f[_],A=s[L],p={},p[A]=_==="b"?d:_==="r"?y:0,N(p,"top",g.top-j.top),N(p,"left",g.left-j.left),B=["top","left"];for(w=0,S=B.length;w=0&&N(r,s[O],i*2)}t.call(u,_)>=0?(N(p,"left",C(O,h,y)),r&&N(r,"left",C(O,i,m))):t.call(o,_)>=0&&(N(p,"top",C(O,c,d)),r&&N(r,"top",C(O,i,v))),this.container.is(":visible")&&(p.display="block"),this.container.removeAttr("style").css(p);if(r)return this.arrow.removeAttr("style").css(r)},L.prototype.getPosition=function(){var e,n,r,i,s,f,c,h;h=this.options.position||(this.elem?this.options.elementPosition:this.options.globalPosition),e=l(h),e.length===0&&(e[0]="b");if(n=e[0],t.call(a,n)<0)throw"Must be one of ["+a+"]";if(e.length===1||(r=e[0],t.call(u,r)>=0)&&(i=e[1],t.call(o,i)<0)||(s=e[0],t.call(o,s)>=0)&&(f=e[1],t.call(u,f)<0))e[1]=(c=e[0],t.call(o,c)>=0)?"m":"l";return e.length===2&&(e[2]=e[1]),e},L.prototype.getStyle=function(e){var t;e||(e=this.options.style),e||(e="default"),t=c[e];if(!t)throw"Missing style: "+e;return t},L.prototype.updateClasses=function(){var t,n;return t=["base"],e.isArray(this.options.className)?t=t.concat(this.options.className):this.options.className&&t.push(this.options.className),n=this.getStyle(),t=e.map(t,function(e){return r+"-"+n.name+"-"+e}).join(" "),this.userContainer.attr("class",t)},L.prototype.run=function(t,n){var r,s,o,u,a;e.isPlainObject(n)?e.extend(this.options,n):e.type(n)==="string"&&(this.options.className=n);if(this.container&&!t){this.show(!1);return}if(!this.container&&!t)return;s={},e.isPlainObject(t)?s=t:s[i]=t;for(o in s){r=s[o],u=this.userFields[o];if(!u)continue;u==="text"&&(r=k(r),this.options.breakNewLines&&(r=r.replace(/\n/g,"
"))),a=o===i?"":"="+o,y(this.userContainer,"[data-notify-"+u+a+"]").html(r)}this.updateClasses(),this.elem?this.setElementPosition():this.setGlobalPosition(),this.show(!0),this.options.autoHide&&(clearTimeout(this.autohideTimer),this.autohideTimer=setTimeout(this.show.bind(this,!1),this.options.autoHideDelay))},L.prototype.destroy=function(){this.wrapper.data(r,null),this.wrapper.remove()},e[n]=function(t,r,i){return t&&t.nodeName||t.jquery?e(t)[n](r,i):(i=r,r=t,new L(null,r,i)),t},e.fn[n]=function(t,n){return e(this).each(function(){var i=T(e(this)).data(r);i&&i.destroy();var s=new L(e(this),t,n)}),this},e.extend(e[n],{defaults:E,addStyle:v,pluginOptions:b,getStyle:d,insertCSS:m}),v("bootstrap",{html:"
\n\n
",classes:{base:{"font-weight":"bold",padding:"8px 15px 8px 14px","text-shadow":"0 1px 0 rgba(255, 255, 255, 0.5)","background-color":"#fcf8e3",border:"1px solid #fbeed5","border-radius":"4px","white-space":"nowrap","padding-left":"25px","background-repeat":"no-repeat","background-position":"3px 7px"},error:{color:"#B94A48","background-color":"#F2DEDE","border-color":"#EED3D7","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAtRJREFUeNqkVc1u00AQHq+dOD+0poIQfkIjalW0SEGqRMuRnHos3DjwAH0ArlyQeANOOSMeAA5VjyBxKBQhgSpVUKKQNGloFdw4cWw2jtfMOna6JOUArDTazXi/b3dm55socPqQhFka++aHBsI8GsopRJERNFlY88FCEk9Yiwf8RhgRyaHFQpPHCDmZG5oX2ui2yilkcTT1AcDsbYC1NMAyOi7zTX2Agx7A9luAl88BauiiQ/cJaZQfIpAlngDcvZZMrl8vFPK5+XktrWlx3/ehZ5r9+t6e+WVnp1pxnNIjgBe4/6dAysQc8dsmHwPcW9C0h3fW1hans1ltwJhy0GxK7XZbUlMp5Ww2eyan6+ft/f2FAqXGK4CvQk5HueFz7D6GOZtIrK+srupdx1GRBBqNBtzc2AiMr7nPplRdKhb1q6q6zjFhrklEFOUutoQ50xcX86ZlqaZpQrfbBdu2R6/G19zX6XSgh6RX5ubyHCM8nqSID6ICrGiZjGYYxojEsiw4PDwMSL5VKsC8Yf4VRYFzMzMaxwjlJSlCyAQ9l0CW44PBADzXhe7xMdi9HtTrdYjFYkDQL0cn4Xdq2/EAE+InCnvADTf2eah4Sx9vExQjkqXT6aAERICMewd/UAp/IeYANM2joxt+q5VI+ieq2i0Wg3l6DNzHwTERPgo1ko7XBXj3vdlsT2F+UuhIhYkp7u7CarkcrFOCtR3H5JiwbAIeImjT/YQKKBtGjRFCU5IUgFRe7fF4cCNVIPMYo3VKqxwjyNAXNepuopyqnld602qVsfRpEkkz+GFL1wPj6ySXBpJtWVa5xlhpcyhBNwpZHmtX8AGgfIExo0ZpzkWVTBGiXCSEaHh62/PoR0p/vHaczxXGnj4bSo+G78lELU80h1uogBwWLf5YlsPmgDEd4M236xjm+8nm4IuE/9u+/PH2JXZfbwz4zw1WbO+SQPpXfwG/BBgAhCNZiSb/pOQAAAAASUVORK5CYII=)"},success:{color:"#468847","background-color":"#DFF0D8","border-color":"#D6E9C6","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAutJREFUeNq0lctPE0Ecx38zu/RFS1EryqtgJFA08YCiMZIAQQ4eRG8eDGdPJiYeTIwHTfwPiAcvXIwXLwoXPaDxkWgQ6islKlJLSQWLUraPLTv7Gme32zoF9KSTfLO7v53vZ3d/M7/fIth+IO6INt2jjoA7bjHCJoAlzCRw59YwHYjBnfMPqAKWQYKjGkfCJqAF0xwZjipQtA3MxeSG87VhOOYegVrUCy7UZM9S6TLIdAamySTclZdYhFhRHloGYg7mgZv1Zzztvgud7V1tbQ2twYA34LJmF4p5dXF1KTufnE+SxeJtuCZNsLDCQU0+RyKTF27Unw101l8e6hns3u0PBalORVVVkcaEKBJDgV3+cGM4tKKmI+ohlIGnygKX00rSBfszz/n2uXv81wd6+rt1orsZCHRdr1Imk2F2Kob3hutSxW8thsd8AXNaln9D7CTfA6O+0UgkMuwVvEFFUbbAcrkcTA8+AtOk8E6KiQiDmMFSDqZItAzEVQviRkdDdaFgPp8HSZKAEAL5Qh7Sq2lIJBJwv2scUqkUnKoZgNhcDKhKg5aH+1IkcouCAdFGAQsuWZYhOjwFHQ96oagWgRoUov1T9kRBEODAwxM2QtEUl+Wp+Ln9VRo6BcMw4ErHRYjH4/B26AlQoQQTRdHWwcd9AH57+UAXddvDD37DmrBBV34WfqiXPl61g+vr6xA9zsGeM9gOdsNXkgpEtTwVvwOklXLKm6+/p5ezwk4B+j6droBs2CsGa/gNs6RIxazl4Tc25mpTgw/apPR1LYlNRFAzgsOxkyXYLIM1V8NMwyAkJSctD1eGVKiq5wWjSPdjmeTkiKvVW4f2YPHWl3GAVq6ymcyCTgovM3FzyRiDe2TaKcEKsLpJvNHjZgPNqEtyi6mZIm4SRFyLMUsONSSdkPeFtY1n0mczoY3BHTLhwPRy9/lzcziCw9ACI+yql0VLzcGAZbYSM5CCSZg1/9oc/nn7+i8N9p/8An4JMADxhH+xHfuiKwAAAABJRU5ErkJggg==)"},info:{color:"#3A87AD","background-color":"#D9EDF7","border-color":"#BCE8F1","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QYFAhkSsdes/QAAA8dJREFUOMvVlGtMW2UYx//POaWHXg6lLaW0ypAtw1UCgbniNOLcVOLmAjHZolOYlxmTGXVZdAnRfXQm+7SoU4mXaOaiZsEpC9FkiQs6Z6bdCnNYruM6KNBw6YWewzl9z+sHImEWv+vz7XmT95f/+3/+7wP814v+efDOV3/SoX3lHAA+6ODeUFfMfjOWMADgdk+eEKz0pF7aQdMAcOKLLjrcVMVX3xdWN29/GhYP7SvnP0cWfS8caSkfHZsPE9Fgnt02JNutQ0QYHB2dDz9/pKX8QjjuO9xUxd/66HdxTeCHZ3rojQObGQBcuNjfplkD3b19Y/6MrimSaKgSMmpGU5WevmE/swa6Oy73tQHA0Rdr2Mmv/6A1n9w9suQ7097Z9lM4FlTgTDrzZTu4StXVfpiI48rVcUDM5cmEksrFnHxfpTtU/3BFQzCQF/2bYVoNbH7zmItbSoMj40JSzmMyX5qDvriA7QdrIIpA+3cdsMpu0nXI8cV0MtKXCPZev+gCEM1S2NHPvWfP/hL+7FSr3+0p5RBEyhEN5JCKYr8XnASMT0xBNyzQGQeI8fjsGD39RMPk7se2bd5ZtTyoFYXftF6y37gx7NeUtJJOTFlAHDZLDuILU3j3+H5oOrD3yWbIztugaAzgnBKJuBLpGfQrS8wO4FZgV+c1IxaLgWVU0tMLEETCos4xMzEIv9cJXQcyagIwigDGwJgOAtHAwAhisQUjy0ORGERiELgG4iakkzo4MYAxcM5hAMi1WWG1yYCJIcMUaBkVRLdGeSU2995TLWzcUAzONJ7J6FBVBYIggMzmFbvdBV44Corg8vjhzC+EJEl8U1kJtgYrhCzgc/vvTwXKSib1paRFVRVORDAJAsw5FuTaJEhWM2SHB3mOAlhkNxwuLzeJsGwqWzf5TFNdKgtY5qHp6ZFf67Y/sAVadCaVY5YACDDb3Oi4NIjLnWMw2QthCBIsVhsUTU9tvXsjeq9+X1d75/KEs4LNOfcdf/+HthMnvwxOD0wmHaXr7ZItn2wuH2SnBzbZAbPJwpPx+VQuzcm7dgRCB57a1uBzUDRL4bfnI0RE0eaXd9W89mpjqHZnUI5Hh2l2dkZZUhOqpi2qSmpOmZ64Tuu9qlz/SEXo6MEHa3wOip46F1n7633eekV8ds8Wxjn37Wl63VVa+ej5oeEZ/82ZBETJjpJ1Rbij2D3Z/1trXUvLsblCK0XfOx0SX2kMsn9dX+d+7Kf6h8o4AIykuffjT8L20LU+w4AZd5VvEPY+XpWqLV327HR7DzXuDnD8r+ovkBehJ8i+y8YAAAAASUVORK5CYII=)"},warn:{color:"#C09853","background-color":"#FCF8E3","border-color":"#FBEED5","background-image":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAABJlBMVEXr6eb/2oD/wi7/xjr/0mP/ykf/tQD/vBj/3o7/uQ//vyL/twebhgD/4pzX1K3z8e349vK6tHCilCWbiQymn0jGworr6dXQza3HxcKkn1vWvV/5uRfk4dXZ1bD18+/52YebiAmyr5S9mhCzrWq5t6ufjRH54aLs0oS+qD751XqPhAybhwXsujG3sm+Zk0PTwG6Shg+PhhObhwOPgQL4zV2nlyrf27uLfgCPhRHu7OmLgAafkyiWkD3l49ibiAfTs0C+lgCniwD4sgDJxqOilzDWowWFfAH08uebig6qpFHBvH/aw26FfQTQzsvy8OyEfz20r3jAvaKbhgG9q0nc2LbZxXanoUu/u5WSggCtp1anpJKdmFz/zlX/1nGJiYmuq5Dx7+sAAADoPUZSAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfdBgUBGhh4aah5AAAAlklEQVQY02NgoBIIE8EUcwn1FkIXM1Tj5dDUQhPU502Mi7XXQxGz5uVIjGOJUUUW81HnYEyMi2HVcUOICQZzMMYmxrEyMylJwgUt5BljWRLjmJm4pI1hYp5SQLGYxDgmLnZOVxuooClIDKgXKMbN5ggV1ACLJcaBxNgcoiGCBiZwdWxOETBDrTyEFey0jYJ4eHjMGWgEAIpRFRCUt08qAAAAAElFTkSuQmCC)"}}}),e(function(){m(h.css).attr("id","core-notify"),e(document).on("click","."+r+"-hidable",function(t){e(this).trigger("notify-hide")}),e(document).on("notify-hide","."+r+"-wrapper",function(t){var n=e(this).data(r);n&&n.show(!1)})})}) -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zvlex/simple-vue-rails-app/9a8bd29266d98ccdbba05b3e1701253a6190b3eb/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------