├── .gitignore ├── .ruby-gemset ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── app │ │ │ ├── helpers │ │ │ │ ├── global_search.js │ │ │ │ ├── locales.js │ │ │ │ ├── pagination.js │ │ │ │ └── turbolinks │ │ │ │ │ ├── auto_navbar_collapse.js │ │ │ │ │ └── transitions.js │ │ │ ├── init.js │ │ │ ├── initializers │ │ │ │ └── bootstrap-checkbox-radio.js │ │ │ └── lib │ │ │ │ ├── notifications.js │ │ │ │ └── turbolinks_native_message_handler.js │ │ ├── application.js │ │ ├── cable.js │ │ ├── channels │ │ │ └── .keep │ │ └── ext │ │ │ ├── jquery │ │ │ ├── animateCSS.js │ │ │ └── once.js │ │ │ ├── rails.js │ │ │ └── turbolinks.coffee │ └── stylesheets │ │ ├── application.scss │ │ ├── ext │ │ └── paper-dashboard.scss │ │ ├── mixins.scss │ │ └── views │ │ ├── devise.scss │ │ ├── errors.scss │ │ └── home.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ ├── .keep │ │ ├── devise_permitted_parameters.rb │ │ ├── locale_manager.rb │ │ ├── meta_tags_helpers.rb │ │ ├── pundit_helpers.rb │ │ ├── token_authentication.rb │ │ └── turbolinks_helpers.rb │ ├── errors_controller.rb │ ├── home_controller.rb │ ├── recipes_controller.rb │ └── users │ │ ├── passwords_controller.rb │ │ ├── registrations_controller.rb │ │ ├── sessions_controller.rb │ │ └── settings_controller.rb ├── helpers │ ├── application_helper.rb │ ├── home_helper.rb │ └── recipes_helper.rb ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── application_record.rb │ ├── concerns │ │ ├── .keep │ │ └── user_token.rb │ ├── home │ │ └── params.rb │ ├── recipe.rb │ └── user.rb ├── policies │ ├── application_policy.rb │ ├── recipe_policy.rb │ └── user_policy.rb └── views │ ├── devise │ ├── mailer │ │ ├── confirmation_instructions.html.erb │ │ ├── password_change.html.erb │ │ ├── reset_password_instructions.html.erb │ │ └── unlock_instructions.html.erb │ ├── passwords │ │ ├── create.js.erb │ │ ├── edit.html.slim │ │ ├── edit │ │ │ └── _form.html.slim │ │ ├── new.html+app.slim │ │ ├── new.html.slim │ │ └── update.js.erb │ ├── registrations │ │ ├── create.js+app.erb │ │ ├── create.js.erb │ │ ├── destroy.js.erb │ │ ├── edit.html.slim │ │ ├── edit │ │ │ └── _form.html.slim │ │ ├── new.html.slim │ │ ├── new │ │ │ ├── _form.html+app.slim │ │ │ └── _form.html.slim │ │ └── update.js.erb │ └── sessions │ │ ├── create.js+app.erb │ │ ├── create.js.erb │ │ ├── new.html.slim │ │ ├── new.js.erb │ │ └── new │ │ └── _form.html.slim │ ├── errors │ ├── internal_server_error.html.slim │ └── not_found.html.slim │ ├── home │ ├── _recipe.html.slim │ ├── _recipes.html.slim │ ├── _recipes_not_found.html.slim │ ├── _recipes_relation.html.slim │ ├── _recipes_search_filters.html.slim │ ├── index.html+app.slim │ ├── index.html.slim │ └── index.js.erb │ ├── kaminari │ ├── _first_page.html.slim │ ├── _gap.html.slim │ ├── _last_page.html.slim │ ├── _next_page.html.slim │ ├── _page.html.slim │ ├── _paginator.html.slim │ └── _prev_page.html.slim │ ├── layouts │ ├── _flash_messages.html.slim │ ├── _footer.html.slim │ ├── _sidebar.html.slim │ ├── _topnavbar.html.slim │ ├── application.html+app.slim │ ├── application.html+msite.slim │ ├── application.html.slim │ ├── mailer.html.erb │ ├── mailer.text.erb │ └── navbar │ │ ├── _header.html.slim │ │ ├── _settings.html.slim │ │ └── locales │ │ ├── _for_guests.html.slim │ │ └── _for_users.html.slim │ └── recipes │ ├── _form.html.slim │ ├── edit.html.slim │ ├── new.html.slim │ ├── save.js.erb │ └── show.html.slim ├── bin ├── bundle ├── rails ├── rake ├── setup ├── spring └── update ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── friendly_id.rb │ ├── inflections.rb │ ├── kaminari_config.rb │ ├── locales.rb │ ├── meta_tags.rb │ ├── mime_types.rb │ ├── new_framework_defaults.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ ├── en │ │ ├── global.yml │ │ ├── models │ │ │ ├── recipe.yml │ │ │ └── user.yml │ │ ├── navigation.yml │ │ ├── pagination.yml │ │ ├── titles.yml │ │ └── views │ │ │ ├── devise │ │ │ ├── mailer.yml │ │ │ ├── passwords.yml │ │ │ ├── registrations.yml │ │ │ └── sessions.yml │ │ │ ├── errors │ │ │ ├── internal_server_error.yml │ │ │ └── not_found.yml │ │ │ ├── home.yml │ │ │ ├── layouts │ │ │ ├── footer.yml │ │ │ └── navbar.yml │ │ │ └── recipes.yml │ └── pt-BR │ │ ├── global.yml │ │ ├── models │ │ ├── recipe.yml │ │ └── user.yml │ │ ├── navigation.yml │ │ ├── pagination.yml │ │ ├── titles.yml │ │ └── views │ │ ├── devise │ │ ├── mailer.yml │ │ ├── passwords.yml │ │ ├── registrations.yml │ │ └── sessions.yml │ │ ├── errors │ │ ├── internal_server_error.yml │ │ └── not_found.yml │ │ ├── home.yml │ │ ├── layouts │ │ ├── footer.yml │ │ └── navbar.yml │ │ └── recipes.yml ├── puma.rb ├── routes.rb ├── secrets.yml └── spring.rb ├── db ├── migrate │ ├── 20160806032155_devise_create_users.rb │ ├── 20160806033435_add_role_to_users.rb │ ├── 20160806200056_add_name_to_user.rb │ ├── 20160827062724_add_authentication_token_to_users.rb │ ├── 20161111224106_create_recipes.rb │ ├── 20161112004523_add_story_to_recipes.rb │ ├── 20161115142009_create_friendly_id_slugs.rb │ └── 20161115142053_add_slug_to_recipes.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── public ├── 422.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── sample.env ├── spec ├── factories │ ├── recipes.rb │ └── users.rb ├── models │ ├── recipe_spec.rb │ └── user_spec.rb ├── policies │ └── user_policy_spec.rb ├── rails_helper.rb └── spec_helper.rb ├── tmp └── .keep └── vendor └── assets ├── fonts ├── .DS_Store ├── themify.eot ├── themify.svg ├── themify.ttf └── themify.woff ├── images ├── .DS_Store ├── apple-icon.png ├── background.jpg ├── faces │ ├── .DS_Store │ ├── face-0.jpg │ ├── face-1.jpg │ ├── face-2.jpg │ └── face-3.jpg ├── favicon.png ├── new_logo.png └── tim_80x80.png ├── javascripts ├── .keep ├── bootbox.js ├── bootstrap-checkbox-radio.js ├── bootstrap-notify.js └── paper-dashboard.js └── stylesheets ├── .keep ├── paper-dashboard.scss ├── paper ├── _alerts.scss ├── _buttons.scss ├── _cards.scss ├── _chartist.scss ├── _checkbox-radio.scss ├── _dropdown.scss ├── _footers.scss ├── _inputs.scss ├── _misc.scss ├── _mixins.scss ├── _navbars.scss ├── _responsive.scss ├── _sidebar-and-main-panel.scss ├── _tables.scss ├── _tabs-navs-pagination.scss ├── _typography.scss ├── _variables.scss └── mixins │ ├── _buttons.scss │ ├── _cards.scss │ ├── _chartist.scss │ ├── _icons.scss │ ├── _inputs.scss │ ├── _labels.scss │ ├── _navbars.scss │ ├── _sidebar.scss │ ├── _tabs.scss │ ├── _transparency.scss │ └── _vendor-prefixes.scss └── themify-icons.css /.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 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | # Ignore Byebug command history file. 17 | .byebug_history 18 | 19 | .env 20 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | master-app 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.2 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby "2.3.2" 4 | 5 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 6 | gem 'rails', '~> 5.0.0.1' 7 | # Use postgresql as the database for Active Record 8 | gem 'pg', '~> 0.19' 9 | # Use Puma as the app server 10 | gem 'puma', '~> 3.6' 11 | # Use SCSS for stylesheets 12 | gem 'sass-rails', '~> 5.0' 13 | # Use Uglifier as compressor for JavaScript assets 14 | gem 'uglifier', '>= 1.3.0' 15 | # Use CoffeeScript for .coffee assets and views 16 | gem 'coffee-rails', '~> 4.2' 17 | # See https://github.com/rails/execjs#readme for more supported runtimes 18 | # gem 'therubyracer', platforms: :ruby 19 | 20 | # Use jquery as the JavaScript library 21 | gem 'jquery-rails' 22 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks 23 | gem 'turbolinks', '~> 5.0', '>= 5.0.1' 24 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 25 | gem 'jbuilder', '~> 2.5' 26 | # Use Redis adapter to run Action Cable in production 27 | # gem 'redis', '~> 3.0' 28 | # Use ActiveModel has_secure_password 29 | # gem 'bcrypt', '~> 3.1.7' 30 | 31 | # Use Capistrano for deployment 32 | # gem 'capistrano-rails', group: :development 33 | 34 | source 'https://rails-assets.org' do 35 | gem 'rails-assets-animate.css', '3.5.2' 36 | end 37 | 38 | # ** Views/Templates ** 39 | gem 'slim-rails', '~> 3.1' 40 | gem 'bootstrap-sass', '3.3.7' 41 | gem 'font-awesome-rails', '~> 4.7' 42 | gem 'bootstrap_form', '~> 2.5' 43 | gem 'browser', '~> 2.3' 44 | gem 'kaminari', '~> 0.17.0' 45 | 46 | # ** SEO ** 47 | gem 'meta-tags', '~> 2.3', '>= 2.3.1' 48 | gem 'friendly_id', '~> 5.1' 49 | 50 | # ** i18n ** 51 | gem 'rails-i18n', '~> 5.0' 52 | gem 'devise-i18n', '~> 1.1' 53 | 54 | # ** i18n ** 55 | gem 'devise', '~> 4.2' 56 | gem 'pundit', '~> 1.1' 57 | 58 | # Asynchronous processing 59 | gem 'sucker_punch', '~> 2.0', '>= 2.0.2' 60 | 61 | group :development, :test do 62 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 63 | gem 'pry', '~> 0.10.4' 64 | gem 'pry-byebug', '~> 3.4' 65 | gem 'byebug', platform: :mri 66 | gem 'rspec-rails', '~> 3.5', '>= 3.5.2' 67 | gem 'i18n-tasks', '~> 0.9.6' 68 | gem 'dotenv-rails', '~> 2.1', '>= 2.1.1' 69 | gem 'faker', '~> 1.6', '>= 1.6.6' 70 | gem 'factory_girl_rails', '~> 4.7' 71 | end 72 | 73 | group :development do 74 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. 75 | gem 'web-console' 76 | gem 'listen', '~> 3.1.5' 77 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 78 | gem 'spring' 79 | gem 'letter_opener', '~> 1.4', '>= 1.4.1' 80 | end 81 | 82 | group :production do 83 | gem 'heroku-deflater', git: 'https://github.com/romanbsd/heroku-deflater.git', ref: '60d92ba0f8ae2' 84 | end 85 | 86 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 87 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Master App 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | `2.3.2` 10 | 11 | * System dependencies 12 | 13 | * Configuration 14 | ``` 15 | # Create a .env file and set all environment variables 16 | cp sample.env .env 17 | ``` 18 | 19 | * Database creation 20 | ``` 21 | rails db:create && rails db:migrate 22 | ``` 23 | 24 | * Database initialization 25 | 26 | * How to run the test suite 27 | 28 | * Services (job queues, cache servers, search engines, etc.) 29 | 30 | **Job queues:** 31 | - https://github.com/brandonhilkert/sucker_punch 32 | 33 | **Heroku:** 34 | - [SendGrid](https://elements.heroku.com/addons/sendgrid) 35 | 36 | * Deployment instructions 37 | - Look in sample.env and set all environment variables listed in it. 38 | 39 | * ... 40 | -------------------------------------------------------------------------------- /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_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/app/helpers/global_search.js: -------------------------------------------------------------------------------- 1 | $(document).on('turbolinks:load', function() { 2 | $('form[data-global-search="true"]').on('submit',function (e) { 3 | e.preventDefault(); 4 | 5 | var $this = $(this), $q = $this.find('[name="search[q]"]'); 6 | var query = $.trim($q.val()); 7 | 8 | // normalize all query empty spaces 9 | $q.val( query.replace(/\s+/g, " ") ); 10 | 11 | var url = $this.attr('action') + '?' + $this.serialize(); 12 | 13 | Turbolinks.visit(url); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/helpers/locales.js: -------------------------------------------------------------------------------- 1 | $(document).on('turbolinks:load', function() { 2 | $("a[data-locale=true]").on("click", function (e) { 3 | e.preventDefault(); 4 | 5 | $(document).one("turbolinks:render", function() { 6 | Turbolinks.clearCache(); 7 | }); 8 | 9 | var url = $(this).attr('href'); 10 | 11 | Turbolinks.visit(url, "replace"); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/helpers/pagination.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $(document).on("click",'.pagination a[data-remote=true]', function() { 3 | history.pushState({}, '', $(this).attr('href')); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/helpers/turbolinks/auto_navbar_collapse.js: -------------------------------------------------------------------------------- 1 | $(document).on('turbolinks:before-visit', function() { 2 | var $witch = $('[data-auto-navbar-collapse="true"]'); 3 | 4 | if ($witch.length) { 5 | // Example of how to autoclose a regular bootstrap navbar. 6 | // $navbar.collapse('hide'); 7 | 8 | // Autoclose the paper admin menu. 9 | var navStateClass = 'nav-open'; 10 | var $html = $('html'); 11 | 12 | if ( $html.hasClass(navStateClass) ) { 13 | $html.removeClass(navStateClass); 14 | } 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/helpers/turbolinks/transitions.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | var _cache = {}; 3 | 4 | function getContextKey() { 5 | var data = $('body').data(); 6 | 7 | return data.controller + data.action; 8 | } 9 | 10 | function animate(el) { 11 | var $el = $(el); 12 | var animationType = $el.data('animateCss') || 'fadeIn'; 13 | 14 | $(el).animateCSS(animationType); 15 | } 16 | 17 | function animateOnLoad(el) { 18 | var contextKey = getContextKey() 19 | 20 | var isFirstLoad = _cache[contextKey]; 21 | 22 | if (!isFirstLoad) { 23 | _cache[contextKey] = true; 24 | 25 | animate(this); 26 | } 27 | } 28 | 29 | $(document).on('turbolinks:visit', function() { 30 | $('[data-animate-css]').each(function () { 31 | animate(this); 32 | }); 33 | }); 34 | 35 | $(document).on('turbolinks:load', function() { 36 | $('[data-animate-css]').each(function () { 37 | animateOnLoad(this); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/init.js: -------------------------------------------------------------------------------- 1 | //= require_tree ./initializers 2 | //= require_tree ./helpers 3 | //= require_tree ./lib 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/initializers/bootstrap-checkbox-radio.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | $(document).on('turbolinks:load', function () { 3 | $('input[type="checkbox"]').each(function () { 4 | var $checkbox = $(this); 5 | $checkbox.checkbox(); 6 | }); 7 | }); 8 | 9 | $(document).on('turbolinks:load', function () { 10 | $('input[type="radio"]').each(function () { 11 | var $radio = $(this); 12 | $radio.radio(); 13 | }); 14 | }); 15 | })(); 16 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/lib/notifications.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var DATA_DISABLE = 'data-disable', 3 | NOTIFICATIONS = { 4 | timeout: 2000, 5 | alert: { 6 | state: 'danger', 7 | icon: 'ti-thumb-down' 8 | }, 9 | notice: { 10 | state: 'success', 11 | icon: 'ti-thumb-up' 12 | } 13 | }; 14 | 15 | function notifyWith(flashType, message) { 16 | var notification = NOTIFICATIONS[flashType]; 17 | 18 | $.notify({ 19 | icon: notification['icon'], 20 | message: message 21 | },{ 22 | type: notification['state'], 23 | timer: NOTIFICATIONS['timeout'] 24 | }); 25 | } 26 | 27 | function notifyIfEnable(data) { 28 | if (data && !data['disable']) { 29 | notifyWith(data['type'], data['text']); 30 | } 31 | } 32 | 33 | function notifyWithElement(el) { 34 | var $el = $(el), data = $.extend( 35 | { disable: $el.attr(DATA_DISABLE) }, 36 | $el.data() 37 | ); 38 | 39 | notifyIfEnable(data); 40 | 41 | return $el; 42 | } 43 | 44 | this.RailsFlashMessagesAsNotifications = { 45 | showAll: function() { 46 | $('#flash-messages li').each(function(i, el) { 47 | notifyWithElement(el).attr( 48 | DATA_DISABLE, "true" 49 | ); 50 | }); 51 | }, 52 | 53 | alert: function(message) { 54 | notifyWith('alert', message); 55 | }, 56 | 57 | notice: function(message) { 58 | notifyWith('notice', message); 59 | } 60 | }; 61 | 62 | // Application Layout > ul.hidden#flash-messages wrapper. 63 | $(document).on("turbolinks:load", RailsFlashMessagesAsNotifications.showAll); 64 | })(this); 65 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/lib/turbolinks_native_message_handler.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var android = this.android, 3 | webkit = this.webkit; 4 | 5 | var consoleHandler = { 6 | postMessage: function(data) { 7 | console.log("TurbolinksNativeMessageHandler.postMessage:", data); 8 | } 9 | }; 10 | 11 | this.TurbolinksNativeMessageHandler = { 12 | _getDefaultNamespace: function() { 13 | var $container = $('[data-turbolinks-native-message-handler]'); 14 | 15 | if ($container.length) { 16 | return $container.data()['turbolinksNativeMessageHandler']; 17 | } 18 | }, 19 | 20 | _getNamespace: function(value) { 21 | if (value) { 22 | return value; 23 | } else { 24 | return this._getDefaultNamespace(); 25 | } 26 | }, 27 | 28 | postMessage: function(data, namespace) { 29 | var handler; 30 | 31 | // Android container 32 | if (android) { 33 | handler = android; 34 | 35 | // iOS container 36 | } else if (webkit && webkit.messageHandlers) { 37 | var _namespace = this._getNamespace(namespace); 38 | 39 | handler = webkit.messageHandlers[_namespace]; 40 | 41 | } else { 42 | handler = consoleHandler; 43 | } 44 | 45 | handler.postMessage(data); 46 | } 47 | }; 48 | })(); 49 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or 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. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | // ** Main dependencies ** 14 | //= require jquery 15 | //= require jquery_ujs 16 | //= require bootstrap-sprockets 17 | //= require turbolinks 18 | 19 | // ** Plugins ** 20 | //= require bootstrap-checkbox-radio 21 | //= require bootstrap-notify 22 | //= require bootbox 23 | //= require paper-dashboard 24 | 25 | // ** Extensions ** 26 | //= require_tree ./ext 27 | 28 | // ** Project ** 29 | //= require ./app/init 30 | 31 | //= require_tree . 32 | -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the rails generate channel command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/ext/jquery/animateCSS.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'; 3 | 4 | $.fn.extend({ 5 | animateCSS: function (animationName) { 6 | $(this).addClass('animated ' + animationName).one(animationEnd, function() { 7 | $(this).removeClass('animated ' + animationName); 8 | }); 9 | } 10 | }); 11 | })(jQuery); 12 | -------------------------------------------------------------------------------- /app/assets/javascripts/ext/jquery/once.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $.once = function(fn, context) { 3 | var result; 4 | 5 | return function() { 6 | if(fn) { 7 | result = fn.apply(context || this, arguments); 8 | fn = null; 9 | } 10 | 11 | return result; 12 | }; 13 | }; 14 | })(jQuery); 15 | -------------------------------------------------------------------------------- /app/assets/javascripts/ext/rails.js: -------------------------------------------------------------------------------- 1 | /* 2 | Based on 3 | http://thehungrycoder.com/ruby-on-rails/replace-rails-confirm-dialog-with-bootboxjs.html 4 | */ 5 | 6 | $(function() { 7 | var ConfirmToDestroy = {}; 8 | 9 | var centralize = function(box) { 10 | // https://github.com/makeusabrew/bootbox/issues/166 11 | box.css({ 12 | 'top': '50%', 13 | 'margin-top': function () { 14 | return -(box.height() / 2); 15 | } 16 | }); 17 | }; 18 | 19 | ConfirmToDestroy.customDialog = function(message, confirmMSG, cancelMSG, callback) { 20 | var box = bootbox.confirm({ 21 | message: message, 22 | callback: callback, 23 | buttons: { 24 | confirm: { 25 | label: (confirmMSG || 'Yes, definitely!'), 26 | className: 'btn-danger btn-fill' 27 | }, 28 | cancel: { 29 | label: (cancelMSG || 'Opss! No.'), 30 | className: 'btn-fill' 31 | } 32 | } 33 | }); 34 | 35 | centralize(box); 36 | }; 37 | 38 | ConfirmToDestroy.dialog = function(message, callback) { 39 | ConfirmToDestroy.customDialog(message, false, false, callback); 40 | }; 41 | 42 | var railsCallbackHandler = function(callback) { 43 | return function (result) { 44 | if (typeof callback === 'function') { 45 | if (result) { 46 | return callback(); 47 | } 48 | } 49 | } 50 | }; 51 | 52 | ConfirmToDestroy.railsDialog = function(message, confirmMSG, cancelMSG, callback) { 53 | var handler = railsCallbackHandler(callback); 54 | 55 | if (!confirmMSG || !cancelMSG) { 56 | ConfirmToDestroy.dialog(message, handler); 57 | } else { 58 | ConfirmToDestroy.customDialog(message, confirmMSG, cancelMSG, handler); 59 | } 60 | }; 61 | 62 | $(document).on('turbolinks:load', function() { 63 | $('[data-destroy="action"]').on('click', function(e) { 64 | e.preventDefault(); 65 | 66 | var $this = $(this); 67 | var data, message, confirmButton, cancelButton; 68 | 69 | data = $this.data(); 70 | 71 | message = data['confirmMsg']; 72 | cancelButton = data['cancelBtn']; 73 | confirmButton = data['confirmBtn']; 74 | 75 | ConfirmToDestroy.customDialog(message, confirmButton, cancelButton, 76 | function(result) { 77 | if(result) { 78 | $.ajax({url: $this.attr('href'), type: 'DELETE'}); 79 | } 80 | } 81 | ); 82 | }); 83 | }); 84 | 85 | $.rails.allowAction = function(element) { 86 | var answer, callback, message, cancelMSG, confirmMSG; 87 | 88 | message = element.data('confirm'); 89 | cancelMSG = element.data('cancelBtn'); 90 | confirmMSG = element.data('confirmBtn'); 91 | 92 | if (!message) { 93 | return true; 94 | } 95 | 96 | answer = false; 97 | callback = void 0; 98 | 99 | if ($.rails.fire(element, 'confirm')) { 100 | ConfirmToDestroy.railsDialog(message, confirmMSG, cancelMSG, function() { 101 | var oldAllowAction; 102 | 103 | callback = $.rails.fire(element, 'confirm:complete', [answer]); 104 | 105 | if (callback) { 106 | oldAllowAction = $.rails.allowAction; 107 | 108 | $.rails.allowAction = function() { 109 | return true; 110 | }; 111 | 112 | element.trigger('click'); 113 | return $.rails.allowAction = oldAllowAction; 114 | } 115 | }); 116 | } 117 | 118 | return false; 119 | }; 120 | }); 121 | -------------------------------------------------------------------------------- /app/assets/javascripts/ext/turbolinks.coffee: -------------------------------------------------------------------------------- 1 | # Monkey patch Turbolinks to render 404 & 500 normally 2 | Turbolinks.HttpRequest.prototype.requestLoaded = -> 3 | @endRequest => 4 | if 200 <= @xhr.status < 300 or @xhr.status = 404 or @xhr.status = 500 5 | @delegate.requestCompletedWithResponse(@xhr.responseText, @xhr.getResponseHeader("Turbolinks-Location")) 6 | else 7 | @failed = true 8 | @delegate.requestFailedWithStatusCode(@xhr.status, @xhr.responseText) 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | //vendor/css 2 | 3 | @import "bootstrap-sprockets"; 4 | @import "bootstrap"; 5 | @import "animate.css/animate"; 6 | 7 | @import "paper-dashboard"; 8 | @import "ext/paper-dashboard"; 9 | 10 | @import "mixins"; 11 | 12 | @import "views/home"; 13 | @import "views/devise"; 14 | @import "views/errors"; 15 | 16 | // ADD THIS AT THE BOTTOM (otherwise the icons won't appear for Android devices) 17 | @import "font-awesome"; 18 | @import "themify-icons"; 19 | -------------------------------------------------------------------------------- /app/assets/stylesheets/ext/paper-dashboard.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | .content{ 3 | padding: 20px 20px; 4 | } 5 | } 6 | 7 | 8 | //Fix Android container layout issue. 9 | // The css overflow property makes a blank screen. 10 | body.android-app { 11 | .main-panel{ 12 | overflow: visible; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/assets/stylesheets/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin strong-border { 2 | border-style: solid; 3 | border-width: $border-thick; 4 | border-color: $default-color; 5 | } 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/views/devise.scss: -------------------------------------------------------------------------------- 1 | .card.sign_in, 2 | .card.sign_up { 3 | .content { 4 | form .checkbox label { 5 | padding-left: 0; 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/views/errors.scss: -------------------------------------------------------------------------------- 1 | .errors { 2 | .card .content{ 3 | h4 { 4 | margin-top: 0; 5 | } 6 | 7 | input[type="text"] { 8 | @include strong-border; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/assets/stylesheets/views/home.scss: -------------------------------------------------------------------------------- 1 | .recipe { 2 | p.user { 3 | margin-bottom: 15px; 4 | 5 | .name, .edit { margin-right: 10px; } 6 | } 7 | } 8 | 9 | .home.index { 10 | .recipes-search { 11 | margin-top: 20px; 12 | 13 | form .recipes-search-input { 14 | @include strong-border; 15 | } 16 | } 17 | 18 | .recipe { 19 | hr { 20 | margin-top: 5px; 21 | } 22 | 23 | h4 { 24 | margin-top: 10px; 25 | margin-bottom: 10px; 26 | } 27 | } 28 | } 29 | 30 | @media (max-width: $screen-xs-min) { 31 | .home.index .recipes-search form { 32 | margin-top: 10px; 33 | 34 | .form-group { 35 | margin-bottom: 10px; 36 | } 37 | } 38 | } 39 | 40 | @media (min-width: $screen-sm-min) { 41 | .home.index .recipes-search { 42 | form .recipes-search-input { 43 | margin-right: 4px; 44 | } 45 | } 46 | } 47 | 48 | @media (min-width: $screen-sm-min) and (max-width: $screen-md-min) { 49 | .home.index .recipes-search form .recipes-search-input { 50 | width: 300px; 51 | } 52 | } 53 | 54 | @media (min-width: $screen-lg-min) { 55 | .home.index .recipes-search form .recipes-search-input { 56 | width: 350px; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include LocaleManager 3 | include TurbolinksHelpers 4 | include TokenAuthentication 5 | include PunditHelpers 6 | include MetaTagsHelpers 7 | 8 | protect_from_forgery with: :exception 9 | 10 | before_action do 11 | # TODO: fix this logic. It depends on the order of the commands. 12 | request.variant = :msite if browser.device.mobile? 13 | request.variant = :app if turbolinks_app? 14 | end 15 | 16 | private 17 | 18 | # Mix of these two concepts: 19 | # http://blog.bigbinary.com/2016/02/29/rails-5-improves-redirect_to_back-with-redirect-back.html 20 | # https://github.com/rails/rails/blob/5-0-stable/actionpack/lib/action_controller/metal/redirecting.rb 21 | def redirect_back_with_default_fallback(**args) 22 | location = args.delete(:or) 23 | 24 | if location 25 | redirect_to location, **args 26 | else 27 | location = (request.referer || root_path) 28 | 29 | redirect_back(**args.merge!(fallback_location: location)) 30 | end 31 | end 32 | 33 | # https://github.com/plataformatec/devise/wiki/How-To:-Change-the-redirect-path-after-destroying-a-session-i.e.-signing-out 34 | def after_sign_out_path_for(resource_or_scope) 35 | turbolinks_ios_app? ? new_user_session_path : super 36 | end 37 | 38 | protected 39 | 40 | def home_params 41 | @home_params ||= Home::Params.new(params) 42 | end 43 | helper_method :home_params 44 | end 45 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/concerns/devise_permitted_parameters.rb: -------------------------------------------------------------------------------- 1 | module DevisePermittedParameters 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | before_action :configure_permitted_parameters 6 | end 7 | 8 | protected 9 | 10 | def configure_permitted_parameters 11 | devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) 12 | devise_parameter_sanitizer.permit(:account_update, keys: [:name]) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/concerns/locale_manager.rb: -------------------------------------------------------------------------------- 1 | module LocaleManager 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | before_action :set_locale! 6 | end 7 | 8 | private 9 | 10 | def set_locale! 11 | case locale_strategy 12 | when :keep then return 13 | when :apply_param then set_locale_with(locale_param) 14 | when :apply_cookie then set_locale_with(locale_cookie) 15 | when :apply_default then set_locale_with(I18n.default_locale) 16 | else fail NotImplementedError 17 | end 18 | end 19 | 20 | def set_locale_with(locale) 21 | I18n.locale = locale.to_sym 22 | cookies.permanent[:current_locale] = locale 23 | end 24 | 25 | def locale_cookie 26 | @locale_cookie ||= cookies[:current_locale] 27 | end 28 | 29 | def locale_param 30 | @locale_param ||= String(params[:locale]).tap(&:strip!) 31 | end 32 | 33 | def valid_locale?(locale) 34 | I18n.available_locales.include?(locale.to_sym) 35 | end 36 | 37 | def current_locale?(locale) 38 | I18n.locale == locale.to_sym 39 | end 40 | 41 | def locale_strategy 42 | if locale_param.present? 43 | return :keep if locale_param == locale_cookie && current_locale?(locale_param) 44 | 45 | valid_locale?(locale_param) && !current_locale?(locale_param) ? :apply_param : :apply_default 46 | else 47 | return :apply_default if locale_cookie.blank? 48 | return :keep if current_locale?(locale_cookie) 49 | 50 | valid_locale?(locale_cookie) ? :apply_cookie : :apply_default 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /app/controllers/concerns/meta_tags_helpers.rb: -------------------------------------------------------------------------------- 1 | module MetaTagsHelpers 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | before_action :set_meta_tags_with_defaults 6 | end 7 | 8 | def set_meta_tags_with_defaults(data = {}) 9 | defaults = title_meta_tag_data 10 | .merge!(default_image_meta_tag_data) 11 | .merge!(description_meta_tag_data) 12 | 13 | set_meta_tags defaults.merge!(data) 14 | end 15 | 16 | def description_meta_tag_data 17 | { description: I18n.t('app.meta.description'.freeze) } 18 | end 19 | 20 | def default_image_meta_tag_data 21 | { image_src: ENV.fetch('DEFAULT_RECIPE_IMAGE').freeze } 22 | end 23 | 24 | def title_meta_tag_data 25 | data = {} 26 | locales = I18n.t(:titles) 27 | default = locales[:application] 28 | controller = controller_name.to_sym 29 | 30 | data[:title] = locales.dig(controller, action_name.to_sym) 31 | data[:title] ||= locales[controller].instance_of?(String) ? locales[controller] : default 32 | 33 | if !turbolinks_app? && data[:title] != default 34 | data[:site] = default 35 | end 36 | 37 | data 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/controllers/concerns/pundit_helpers.rb: -------------------------------------------------------------------------------- 1 | module PunditHelpers 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | include Pundit 6 | 7 | rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized 8 | end 9 | 10 | private 11 | 12 | def user_not_authorized 13 | flash[:alert] = I18n.t("auth.access_denied") 14 | 15 | redirect_back_with_default_fallback turbolinks: :advance 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/concerns/token_authentication.rb: -------------------------------------------------------------------------------- 1 | # FIXME: Very weak strategy for a production App. 2 | module TokenAuthentication 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | before_action :authenticate_user_from_token! 7 | end 8 | 9 | private 10 | 11 | def authenticate_user_from_token! 12 | return if user_signed_in? 13 | 14 | auth_token = params[:auth_token].presence 15 | user = auth_token && User.find_by(authentication_token: String(auth_token)) 16 | 17 | if user 18 | # Notice we are passing store false, so the user is not 19 | # actually stored in the session and a token is needed 20 | # for every request. If you want the token to work as a 21 | # sign in token, you can simply remove store: false. 22 | # sign_in user, store: false 23 | sign_in user 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/controllers/concerns/turbolinks_helpers.rb: -------------------------------------------------------------------------------- 1 | module TurbolinksHelpers 2 | extend ActiveSupport::Concern 3 | 4 | TURBOLINKS_IOS_APP_USER_AGENT = ENV.fetch("TURBOLINKS_IOS_APP_USER_AGENT").freeze 5 | TURBOLINKS_ANDROID_APP_USER_AGENT = ENV.fetch("TURBOLINKS_ANDROID_APP_USER_AGENT").freeze 6 | 7 | def turbolinks_ios_app_user_agent? 8 | request.user_agent.include?(TURBOLINKS_IOS_APP_USER_AGENT) 9 | end 10 | 11 | def turbolinks_android_app_user_agent? 12 | request.user_agent.include?(TURBOLINKS_ANDROID_APP_USER_AGENT) 13 | end 14 | 15 | def turbolinks_app_user_agent? 16 | turbolinks_ios_app_user_agent? || turbolinks_android_app_user_agent? 17 | end 18 | 19 | included do 20 | [ 21 | [:turbolinks_app?, :turbolinks_app_user_agent?], 22 | [:turbolinks_ios_app?, :turbolinks_ios_app_user_agent?], 23 | [:turbolinks_android_app?, :turbolinks_android_app_user_agent?] 24 | ].each do |alias_name, original_name| 25 | alias_method alias_name, original_name 26 | 27 | helper_method alias_name 28 | helper_method original_name 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/controllers/errors_controller.rb: -------------------------------------------------------------------------------- 1 | class ErrorsController < ApplicationController 2 | def not_found 3 | render status: 404 4 | end 5 | 6 | def internal_server_error 7 | render status: 500 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | def index 3 | set_meta_tags(title: t(".titles.#{home_params.current_filter}")) 4 | 5 | @recipes = if guest_in_my_recipes_filter? 6 | Recipe.none 7 | else 8 | find_all_recipes(relation_base) 9 | end 10 | end 11 | 12 | private 13 | 14 | def guest_in_my_recipes_filter? 15 | !user_signed_in? && home_params.my_recipes_filter? 16 | end 17 | 18 | def relation_base 19 | base = Recipe.joins(:user).includes(:user) 20 | 21 | return base if user_signed_in? && current_user.admin? 22 | 23 | user_ids = User.admin.pluck(:id) 24 | 25 | user_ids << current_user.id if user_signed_in? 26 | 27 | base.where('recipes.user_id'.freeze => user_ids) 28 | end 29 | 30 | def find_all_recipes(relation) 31 | if home_params.search? 32 | relation = apply_user_filter(relation) 33 | relation = apply_query_filter(relation) 34 | end 35 | 36 | apply_pagination relation.order(created_at: :desc) 37 | end 38 | 39 | def apply_user_filter(relation) 40 | return relation if !user_signed_in? 41 | 42 | if home_params.my_recipes_filter? 43 | relation.where(user_id: current_user.id) 44 | else 45 | relation 46 | end 47 | end 48 | 49 | def apply_query_filter(relation) 50 | q = params.dig(:search, :q) 51 | 52 | return relation if q.blank? 53 | 54 | query = "%#{q}%" 55 | 56 | relation.where('recipes.title ILIKE ?'.freeze, query) 57 | .or(relation.where('recipes.story ILIKE ?'.freeze, query)) 58 | .or(relation.where('recipes.ingredients ILIKE ?'.freeze, query)) 59 | .or(relation.where('users.name ILIKE ?'.freeze, query)) 60 | end 61 | 62 | def apply_pagination(relation) 63 | relation.page home_params.current_filter_page_param 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /app/controllers/recipes_controller.rb: -------------------------------------------------------------------------------- 1 | class RecipesController < ApplicationController 2 | before_action :authenticate_user!, except: :show 3 | 4 | before_action :set_recipe, except: [:new, :create] 5 | 6 | after_action :verify_authorized 7 | 8 | def show 9 | set_meta_tags(title: @recipe.title, description: @recipe.story) 10 | end 11 | 12 | def new 13 | @recipe = build_recipe 14 | 15 | authorize @recipe 16 | end 17 | 18 | def create 19 | @recipe = build_recipe(recipe_params) 20 | 21 | authorize @recipe 22 | 23 | if @recipe.save 24 | redirect_to @recipe, notice: t(".notice"), turbolinks: :advance 25 | else 26 | respond_to do |format| 27 | format.html { render :new } 28 | format.js { render :save } 29 | end 30 | end 31 | end 32 | 33 | def edit 34 | end 35 | 36 | def update 37 | if @recipe.update(recipe_params) 38 | redirect_to @recipe, notice: t(".notice"), turbolinks: :advance 39 | else 40 | respond_to do |format| 41 | format.html { render :edit } 42 | format.js { render :save } 43 | end 44 | end 45 | end 46 | 47 | def destroy 48 | @recipe.destroy 49 | 50 | back_to = params[:back_to] || root_path 51 | 52 | redirect_to back_to, notice: t(".notice"), turbolinks: :replace 53 | end 54 | 55 | private 56 | 57 | def build_recipe(params={}) 58 | current_user.recipes.build(params) 59 | end 60 | 61 | def set_recipe 62 | @recipe = Recipe.friendly.find(params[:id]) 63 | 64 | authorize @recipe 65 | end 66 | 67 | def recipe_params 68 | params.require(:recipe).permit(:title, :story, :ingredients, :instructions) 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /app/controllers/users/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::PasswordsController < Devise::PasswordsController 2 | include MetaTagsHelpers 3 | 4 | respond_to :html, :js 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/users/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::RegistrationsController < Devise::RegistrationsController 2 | include MetaTagsHelpers 3 | include DevisePermittedParameters 4 | 5 | respond_to :html, :js 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/users/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::SessionsController < Devise::SessionsController 2 | include MetaTagsHelpers 3 | include TurbolinksHelpers 4 | 5 | respond_to :html, :js 6 | 7 | before_action :verify_destroy_request, only: :destroy 8 | skip_before_action :verify_authenticity_token, if: :js_request? 9 | 10 | # https://github.com/plataformatec/devise/blob/v4.2.0/app/controllers/devise/sessions_controller.rb#L73 11 | def respond_to_on_destroy 12 | respond_to do |format| 13 | format.js { redirect_after_sign_out turbolinks: :advance } 14 | format.all { head :no_content } 15 | format.any(*navigational_formats) { redirect_after_sign_out } 16 | end 17 | end 18 | 19 | private 20 | 21 | def js_request? 22 | request.format.js? 23 | end 24 | 25 | def verify_destroy_request 26 | # Ensure that the sing_out via get works only when 27 | # the is request has the Turbolinks User Agent 28 | if !request.delete? && !turbolinks_app? 29 | raise AbstractController::ActionNotFound 30 | end 31 | end 32 | 33 | def redirect_after_sign_out(turbolinks: false) 34 | redirect_to(after_sign_out_path_for(resource_name), turbolinks: turbolinks) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /app/controllers/users/settings_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::SettingsController < ApplicationController 2 | def change_locale 3 | locale = String(params[:locale]).tap(&:strip!)&.to_sym 4 | locale = I18n.default_locale unless I18n.available_locales.include?(locale) 5 | 6 | cookies.permanent[:current_locale] = locale 7 | 8 | redirect_back_with_default_fallback or: params[:back_to], turbolinks: :advance 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def body_data 3 | { controller: controller_name, action: action_name } 4 | end 5 | 6 | def body_class(class_names = nil) 7 | "#{controller_name} #{action_name} #{class_names}" 8 | end 9 | 10 | def mobile_variant? 11 | request.variant.any? { |key| key == :msite || key == :app } 12 | end 13 | 14 | def confirm_dialog_data(message, key=nil) 15 | confirm_key = key || :confirm 16 | 17 | { confirm_key => message, cancel_btn: t('cancel_btn'.freeze), confirm_btn: t('confirm_btn'.freeze) } 18 | end 19 | 20 | def confirm_dialog_data_to_delete 21 | confirm_dialog_data t('confirm_question'.freeze) 22 | end 23 | 24 | def confirm_dialog_data_to_action_destroy 25 | confirm_dialog_data(t('confirm_question'.freeze), :confirm_msg).merge!(destroy: :action) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/helpers/home_helper.rb: -------------------------------------------------------------------------------- 1 | module HomeHelper 2 | def link_to_recipes_filter(filter) 3 | link_params = home_params.to_recipes_filter(filter) 4 | 5 | class_when_active = 'active'.freeze if home_params.current_filter?(filter) 6 | 7 | link_to t(".#{filter}"), root_path(link_params), { 8 | class: "btn btn-default #{class_when_active}", 9 | data: { 'turbolinks-action'.freeze => :replace } 10 | } 11 | end 12 | 13 | def has_recipes? 14 | return false if !user_signed_in? 15 | 16 | @count_user_recipes ||= current_user.recipes.count 17 | @count_user_recipes > 0 18 | end 19 | 20 | def no_recipes_message_component(text=nil) 21 | result = content_tag :p do 22 | content_tag :h3, class: 'text-center'.freeze do 23 | if block_given? 24 | yield 25 | else 26 | text 27 | end 28 | end 29 | end 30 | 31 | content_tag(:hr) + result 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/helpers/recipes_helper.rb: -------------------------------------------------------------------------------- 1 | module RecipesHelper 2 | def human_attribute(name) 3 | Recipe.human_attribute_name(name) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/concerns/user_token.rb: -------------------------------------------------------------------------------- 1 | # FIXME: Very weak strategy for a production App. 2 | module UserToken 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | before_save :ensure_authentication_token 7 | end 8 | 9 | def ensure_authentication_token 10 | if authentication_token.blank? 11 | self.authentication_token = generate_authentication_token 12 | end 13 | end 14 | 15 | private 16 | 17 | def generate_authentication_token 18 | loop do 19 | token = Devise.friendly_token 20 | break token unless User.where(authentication_token: token).first 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/models/home/params.rb: -------------------------------------------------------------------------------- 1 | module Home 2 | class Params 3 | LATEST_FILTER = 'latest'.freeze 4 | MY_RECIPES_FILTER = 'my'.freeze 5 | 6 | attr_reader :params 7 | 8 | alias_method :raw, :params 9 | 10 | def initialize(params) 11 | @params = params 12 | end 13 | 14 | def search 15 | @search ||= params.permit(search: [:filter, :q])[:search] || {} 16 | end 17 | 18 | def search? 19 | search.present? 20 | end 21 | 22 | def to_search 23 | {search: {filter: MY_RECIPES_FILTER}} 24 | end 25 | 26 | def fetch_search(name, default = nil) 27 | return default unless search? 28 | 29 | search[name] || default 30 | end 31 | 32 | def current_filter 33 | @current_filter ||= fetch_search(:filter, LATEST_FILTER) 34 | end 35 | 36 | def current_filter?(filter) 37 | current_filter == String(filter) 38 | end 39 | 40 | def my_recipes_filter? 41 | current_filter == MY_RECIPES_FILTER 42 | end 43 | 44 | def build_filter_page_name(name) 45 | "#{name}_filter_page" 46 | end 47 | 48 | def current_filter_page_name 49 | @current_filter_page_name ||= build_filter_page_name(current_filter) 50 | end 51 | 52 | def current_filter_page_param 53 | params[current_filter_page_name] 54 | end 55 | 56 | def to_recipes_filter(filter) 57 | link_params = fetch_filter_page_params 58 | link_params[:search] = search.merge({filter: filter}).to_h 59 | link_params 60 | end 61 | 62 | private 63 | 64 | def fetch_filter_page_params 65 | @filter_pages ||= params.permit( 66 | build_filter_page_name(LATEST_FILTER), 67 | build_filter_page_name(MY_RECIPES_FILTER) 68 | ).to_h 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /app/models/recipe.rb: -------------------------------------------------------------------------------- 1 | class Recipe < ApplicationRecord 2 | extend FriendlyId 3 | 4 | SLUG_CANDIDATES = [:title, [:title, :id]].freeze 5 | 6 | friendly_id :slug_candidates, use: [:slugged, :finders] 7 | 8 | belongs_to :user 9 | 10 | validates :title, :ingredients, :instructions, presence: true 11 | 12 | protected 13 | 14 | def slug_candidates 15 | SLUG_CANDIDATES 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | include UserToken 3 | 4 | # Include default devise modules. Others available are: 5 | # :confirmable, :lockable, :timeoutable and :omniauthable 6 | devise :database_authenticatable, :registerable, 7 | :recoverable, :rememberable, :trackable, :validatable 8 | 9 | enum role: { user: 0, admin: 1 } 10 | 11 | has_many :recipes 12 | 13 | validates :name, presence: true 14 | 15 | def send_devise_notification(notification, *args) 16 | devise_mailer.send(notification, self, *args).deliver_later 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/policies/application_policy.rb: -------------------------------------------------------------------------------- 1 | class ApplicationPolicy 2 | attr_reader :user, :record 3 | alias_method :current_user, :user 4 | 5 | def initialize(user, record) 6 | @user = user 7 | @record = record 8 | end 9 | 10 | def index? 11 | false 12 | end 13 | 14 | def show? 15 | scope.where(:id => record.id).exists? 16 | end 17 | 18 | def create? 19 | false 20 | end 21 | 22 | def new? 23 | create? 24 | end 25 | 26 | def update? 27 | false 28 | end 29 | 30 | def edit? 31 | update? 32 | end 33 | 34 | def destroy? 35 | false 36 | end 37 | 38 | def scope 39 | Pundit.policy_scope!(user, record.class) 40 | end 41 | 42 | class Scope 43 | attr_reader :user, :scope 44 | 45 | def initialize(user, scope) 46 | @user = user 47 | @scope = scope 48 | end 49 | 50 | def resolve 51 | scope 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/policies/recipe_policy.rb: -------------------------------------------------------------------------------- 1 | # ** Attributes ** 2 | # user, current_user = current_user 3 | # record = recipe 4 | 5 | class RecipePolicy < ApplicationPolicy 6 | def new? 7 | current_user.persisted? 8 | end 9 | 10 | def create? 11 | record.user_id == current_user&.id 12 | end 13 | 14 | def edit? 15 | create? 16 | end 17 | 18 | def update? 19 | create? 20 | end 21 | 22 | def destroy? 23 | create? 24 | end 25 | 26 | class Scope < ApplicationPolicy::Scope 27 | def resolve 28 | scope 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/policies/user_policy.rb: -------------------------------------------------------------------------------- 1 | class UserPolicy < ApplicationPolicy 2 | def index? 3 | current_user.admin? 4 | end 5 | 6 | def show? 7 | current_user.admin? or current_user == record 8 | end 9 | 10 | def update? 11 | return false if current_user == record 12 | 13 | current_user.admin? 14 | end 15 | 16 | def destroy? 17 | return false if current_user == record 18 | 19 | current_user.admin? 20 | end 21 | 22 | class Scope < ApplicationPolicy::Scope 23 | def resolve 24 | scope 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @email %>!

2 | 3 |

You can confirm your account email through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/password_change.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

We're contacting you to notify you that your password has been changed.

4 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t(".hello", email: @resource.email) %>

2 | 3 |

<%= t(".announcement") %>

4 | 5 |

<%= link_to t(".change_my_password"), edit_password_url(@resource, reset_password_token: @token) %>

6 | 7 |

<%= t(".ignore_message") %>

8 |

<%= t(".notice_message") %>

9 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

8 | -------------------------------------------------------------------------------- /app/views/devise/passwords/create.js.erb: -------------------------------------------------------------------------------- 1 | <% if controller.send(:successfully_sent?, resource) %> 2 | Turbolinks.clearCache(); 3 | Turbolinks.visit("<%= j controller.send(:new_session_path, resource) %>"); 4 | <% end %> 5 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.slim: -------------------------------------------------------------------------------- 1 | div class="row" 2 | div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3 col-xs-12" 3 | div class="card sign_in" 4 | div class="header" 5 | h3 class="title" 6 | = t('.title') 7 | 8 | div class="content" 9 | = render('devise/passwords/edit/form') 10 | 11 | br 12 | 13 | p 14 | small= t('.sign_in_cta_html', url: new_session_path(resource_name)) + '.' 15 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit/_form.html.slim: -------------------------------------------------------------------------------- 1 | = bootstrap_form_for(resource, as: resource_name, url: password_path(resource_name), remote: true, html: { method: :put }) do |f| 2 | = f.hidden_field :reset_password_token 3 | 4 | - if @minimum_password_length 5 | = f.password_field :password, autocomplete: "off", placeholder: t(".password_confirmation_help", length: @minimum_password_length) 6 | - else 7 | = f.password_field :password, autocomplete: "off" 8 | 9 | = f.password_field :password_confirmation, autocomplete: "off" 10 | 11 | = f.submit t(".submit_button"), class: "btn btn-primary btn-block btn-fill" 12 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html+app.slim: -------------------------------------------------------------------------------- 1 | div class="row" 2 | div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3 col-xs-12" 3 | div class="card" 4 | div class="header" 5 | h3 class="title" 6 | = t('.welcome') 7 | 8 | div class="content" 9 | = bootstrap_form_for(resource, as: resource_name, url: password_path(resource_name), remote: true, html: { method: :post }) do |f| 10 | = f.email_field :email, autofocus: true, placeholder: User.human_attribute_name(:name), skip_label: true 11 | 12 | = f.submit t(".submit"), class: "btn btn-primary btn-block btn-fill" 13 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.slim: -------------------------------------------------------------------------------- 1 | div class="row" 2 | div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3 col-xs-12" 3 | div class="card" 4 | div class="header" 5 | h3 class="title" 6 | = t('.welcome') 7 | 8 | div class="content" 9 | = bootstrap_form_for(resource, as: resource_name, url: password_path(resource_name), remote: true, html: { method: :post }) do |f| 10 | = f.email_field :email, autofocus: true, placeholder: User.human_attribute_name(:name), skip_label: true 11 | 12 | = f.submit t(".submit"), class: "btn btn-primary btn-block btn-fill" 13 | 14 | br 15 | 16 | p 17 | small= t('.sign_in_cta_html', url: new_session_path(resource_name)) + '.' 18 | -------------------------------------------------------------------------------- /app/views/devise/passwords/update.js.erb: -------------------------------------------------------------------------------- 1 | <% if resource.errors.present? %> 2 | <% resource.errors.full_messages.each do |msg| %> 3 | RailsFlashMessagesAsNotifications.alert("<%= j msg %>"); 4 | <% end %> 5 | <% else %> 6 | Turbolinks.clearCache(); 7 | Turbolinks.visit("<%= j controller.send(:after_resetting_password_path_for, resource) %>"); 8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/devise/registrations/create.js+app.erb: -------------------------------------------------------------------------------- 1 | <% if resource.errors.present? %> 2 | $('form[class*="_user"]').replaceWith("<%= j render('devise/registrations/new/form') %>"); 3 | <% else %> 4 | <% if user_signed_in? %> 5 | TurbolinksNativeMessageHandler.postMessage({auth_token: "<%= j current_user.authentication_token %>"}); 6 | <% end %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/devise/registrations/create.js.erb: -------------------------------------------------------------------------------- 1 | <% if resource.errors.present? %> 2 | $('form[class*="_user"]').replaceWith("<%= j render('devise/registrations/new/form') %>"); 3 | <% else %> 4 | <% if resource.persisted? %> 5 | <% after_path_method = resource.active_for_authentication? ? :after_sign_up_path_for : :after_inactive_sign_up_path_for %> 6 | 7 | Turbolinks.clearCache(); 8 | Turbolinks.visit("<%= j controller.send(after_path_method, resource) %>"); 9 | <% end %> 10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/views/devise/registrations/destroy.js.erb: -------------------------------------------------------------------------------- 1 | <% unless resource.persisted? %> 2 | Turbolinks.clearCache(); 3 | Turbolinks.visit("<%= j controller.send(:after_sign_out_path_for, resource_name) %>"); 4 | <% end %> 5 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.slim: -------------------------------------------------------------------------------- 1 | div class="row" 2 | div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3 col-xs-12" 3 | div class="card sign_up" 4 | div class="header" 5 | h3 class="title" 6 | = "#{t('edit')} #{resource.class.model_name.human}" 7 | 8 | div class="content" 9 | = render('devise/registrations/edit/form') 10 | 11 | h4= t('.cancel_account.header') 12 | 13 | p 14 | small= t('.cancel_account.question') 15 | p= button_to t(".cancel_account.button"), registration_path(resource_name), data: confirm_dialog_data_to_action_destroy, class: "btn btn-danger btn-block btn-fill" 16 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit/_form.html.slim: -------------------------------------------------------------------------------- 1 | = bootstrap_form_for(resource, as: resource_name, url: registration_path(resource_name), remote: true, html: { method: :put }) do |f| 2 | = f.text_field :name 3 | = f.email_field :email 4 | 5 | - if devise_mapping.confirmable? && resource.pending_reconfirmation? 6 | div= t(".email_confirmation", email: resource.unconfirmed_email) 7 | 8 | - if @minimum_password_length 9 | = f.password_field :password, autocomplete: "off", placeholder: t(".password_confirmation.placeholder", length: @minimum_password_length), help: t(".password_confirmation.help") 10 | - else 11 | = f.password_field :password, autocomplete: "off", help: t(".password_confirmation.help") 12 | 13 | = f.password_field :password_confirmation, autocomplete: "off" 14 | = f.password_field :current_password, autocomplete: "off", help: t(".current_password_help") 15 | 16 | = f.submit t("update"), class: "btn btn-primary btn-block btn-fill" 17 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.slim: -------------------------------------------------------------------------------- 1 | div class="row" 2 | div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3 col-xs-12" 3 | div class="card sign_up" 4 | div class="header" 5 | h3 class="title" 6 | = t('.welcome') 7 | 8 | div class="content" 9 | = render('devise/registrations/new/form') 10 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new/_form.html+app.slim: -------------------------------------------------------------------------------- 1 | = bootstrap_form_for(resource, as: resource_name, url: registration_path(resource_name), remote: true) do |f| 2 | = f.text_field :name, autofocus: true, placeholder: User.human_attribute_name(:name), skip_label: true 3 | 4 | = f.email_field :email, placeholder: User.human_attribute_name(:email), skip_label: true 5 | 6 | = f.password_field :password, autocomplete: "off", placeholder: User.human_attribute_name(:password), skip_label: true 7 | 8 | - if @minimum_password_length 9 | = f.password_field :password_confirmation, autocomplete: "off", placeholder: User.human_attribute_name(:password_confirmation), skip_label: true, help: t(".password_confirmation_help", length: @minimum_password_length) 10 | - else 11 | = f.password_field :password_confirmation, autocomplete: "off", placeholder: User.human_attribute_name(:password_confirmation), skip_label: true 12 | 13 | - if ENV['HIDE_TERMS_AND_POLICY'.freeze].blank? 14 | div class="form-group" 15 | div class="checkbox" 16 | label 17 | input type="checkbox" 18 | = t(".terms_and_policy") 19 | 20 | = f.submit t(".sign_up"), class: "btn btn-primary btn-block btn-fill" 21 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new/_form.html.slim: -------------------------------------------------------------------------------- 1 | = bootstrap_form_for(resource, as: resource_name, url: registration_path(resource_name), remote: true) do |f| 2 | = f.text_field :name, autofocus: true, placeholder: User.human_attribute_name(:name), skip_label: true 3 | 4 | = f.email_field :email, placeholder: User.human_attribute_name(:email), skip_label: true 5 | 6 | = f.password_field :password, autocomplete: "off", placeholder: User.human_attribute_name(:password), skip_label: true 7 | 8 | - if @minimum_password_length 9 | = f.password_field :password_confirmation, autocomplete: "off", placeholder: User.human_attribute_name(:password_confirmation), skip_label: true, help: t(".password_confirmation_help", length: @minimum_password_length) 10 | - else 11 | = f.password_field :password_confirmation, autocomplete: "off", placeholder: User.human_attribute_name(:password_confirmation), skip_label: true 12 | 13 | - if ENV['HIDE_TERMS_AND_POLICY'.freeze].blank? 14 | div class="form-group" 15 | div class="checkbox" 16 | label 17 | input type="checkbox" 18 | = t(".terms_and_policy") 19 | 20 | = f.submit t(".sign_up"), class: "btn btn-primary btn-block btn-fill" 21 | 22 | p class="text-center" 23 | small= t(".sign_in_cta") 24 | 25 | - if controller_name != 'sessions' 26 | = link_to t(".sign_in"), new_session_path(resource_name), class: "btn btn-sm btn-block" 27 | -------------------------------------------------------------------------------- /app/views/devise/registrations/update.js.erb: -------------------------------------------------------------------------------- 1 | <% if resource.errors.present? %> 2 | $('form[class*="_user"]').replaceWith("<%= j render('devise/registrations/edit/form') %>"); 3 | <% else %> 4 | <% if resource.persisted? %> 5 | Turbolinks.clearCache(); 6 | Turbolinks.visit("<%= j controller.send(:after_update_path_for, resource) %>"); 7 | <% end %> 8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/devise/sessions/create.js+app.erb: -------------------------------------------------------------------------------- 1 | <% if resource.errors.present? %> 2 | $('form[class*="_user"]').replaceWith("<%= j render('devise/registrations/new/form') %>"); 3 | <% else %> 4 | TurbolinksNativeMessageHandler.postMessage({auth_token: "<%= j current_user.authentication_token %>"}); 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/devise/sessions/create.js.erb: -------------------------------------------------------------------------------- 1 | <% if user_signed_in? %> 2 | Turbolinks.clearCache(); 3 | Turbolinks.visit("<%= j controller.send(:after_sign_in_path_for, resource) %>"); 4 | <% end %> 5 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.slim: -------------------------------------------------------------------------------- 1 | div class="row" 2 | div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3 col-xs-12" 3 | div class="card sign_in" 4 | div class="header" 5 | h3 class="title" 6 | = t('.welcome') 7 | 8 | div class="content" 9 | = render('devise/sessions/new/form') 10 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.js.erb: -------------------------------------------------------------------------------- 1 | <% unless user_signed_in? %> 2 | <% 3 | # https://github.com/plataformatec/devise/blob/4c3838bb759ec741558ecf86bd6cf01465043e4c/lib/devise/failure_app.rb#L103 4 | scope_class = Devise.mappings[Devise.default_scope].to 5 | auth_keys = scope_class.authentication_keys 6 | keys = (auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys).map { |key| scope_class.human_attribute_name(key) } 7 | message = I18n.t("devise.failure.invalid", authentication_keys: keys.join(I18n.t(:"support.array.words_connector"))) 8 | %> 9 | 10 | RailsFlashMessagesAsNotifications.alert("<%= j message %>"); 11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new/_form.html.slim: -------------------------------------------------------------------------------- 1 | = bootstrap_form_for(resource, as: resource_name, url: session_path(resource_name), remote: true) do |f| 2 | = f.email_field :email, autofocus: true, placeholder: User.human_attribute_name(:email), skip_label: true 3 | 4 | = f.password_field :password, autocomplete: "off", placeholder: User.human_attribute_name(:password), skip_label: true 5 | 6 | - if devise_mapping.rememberable? && !turbolinks_app? 7 | = f.check_box :remember_me 8 | 9 | = f.submit t(".sign_in"), class: "btn btn-primary btn-fill btn-block" 10 | 11 | - if devise_mapping.recoverable? && controller_name != "passwords" && controller_name != "registrations" 12 | br 13 | p class="text-center" 14 | = link_to new_password_path(resource_name) do 15 | small= t(".forgot_password") 16 | 17 | p class="text-center" 18 | small= t(".account_question") 19 | 20 | - if devise_mapping.registerable? && controller_name != "registrations" 21 | = link_to t(".account_cta"), new_registration_path(resource_name), class: "btn btn-sm btn-block" 22 | -------------------------------------------------------------------------------- /app/views/errors/internal_server_error.html.slim: -------------------------------------------------------------------------------- 1 | div class="row errors" 2 | div class="col-md-8 col-md-offset-2 col-sm-8 col-sm-offset-2 col-xs-12" 3 | div class="card text-center" 4 | div class="header" 5 | h2 class="title" 6 | strong.text-danger= t(".status.code") 7 | 8 | div class="content" 9 | h4= t('.status.message') 10 | 11 | p= t('.description_html') 12 | p= link_to t('.homepage'), root_path, class: 'btn btn-primary btn-fill' 13 | -------------------------------------------------------------------------------- /app/views/errors/not_found.html.slim: -------------------------------------------------------------------------------- 1 | div class="row errors" 2 | div class="col-md-8 col-md-offset-2 col-sm-8 col-sm-offset-2 col-xs-12" 3 | div class="card text-center" 4 | div class="header" 5 | h2 class="title" 6 | strong.text-danger= t(".status.code") 7 | 8 | div class="content" 9 | h4= t('.status.message') 10 | 11 | div 12 | p= t('.description') 13 | 14 | p Que tal procurar uma receita deliciosa para compensar isso? 15 | 16 | = bootstrap_form_tag url: root_path, method: :get, data: {global_search: true} do |f| 17 | = f.text_field 'search[q]', placeholder: t('.form.placeholder'), hide_label: true 18 | = f.submit t('.form.submit'), class: 'btn btn-primary btn-fill' 19 | -------------------------------------------------------------------------------- /app/views/home/_recipe.html.slim: -------------------------------------------------------------------------------- 1 | div class="row recipe" 2 | div class="col-md-12 recipe" 3 | hr 4 | 5 | p class="user" 6 | - if !home_params.my_recipes_filter? 7 | span class="text-default name" 8 | = recipe.user.name 9 | 10 | - if policy(recipe).edit? 11 | = link_to edit_recipe_path(recipe), class: 'btn btn-warning btn-xs btn-fill edit' do 12 | i class="ti-pencil-alt" 13 | = t('edit') 14 | 15 | - if home_params.my_recipes_filter? 16 | = link_to(recipe_path(recipe, back_to: request.fullpath), data: confirm_dialog_data_to_action_destroy, class: 'btn btn-danger btn-xs destroy') do 17 | i> class="ti-trash" 18 | = t('destroy') 19 | 20 | h4 21 | = link_to recipe.title, recipe 22 | 23 | - if recipe.story.present? 24 | p 25 | = recipe.story 26 | -------------------------------------------------------------------------------- /app/views/home/_recipes.html.slim: -------------------------------------------------------------------------------- 1 | - if home_params.my_recipes_filter? && !user_signed_in? 2 | = no_recipes_message_component t('.my_recipes_guest_html'.freeze, url: new_user_registration_path) 3 | - elsif home_params.my_recipes_filter? && !has_recipes? 4 | = no_recipes_message_component t('.my_recipes_user_html'.freeze, url: new_recipe_path) 5 | 6 | - else 7 | - if @recipes.present? 8 | = render partial: 'recipes_relation'.freeze 9 | - else 10 | = render partial: 'recipes_not_found'.freeze 11 | -------------------------------------------------------------------------------- /app/views/home/_recipes_not_found.html.slim: -------------------------------------------------------------------------------- 1 | = no_recipes_message_component do 2 | - if home_params.my_recipes_filter? 3 | = t('.message_for_my_search_html'.freeze, url: root_path(search: {q: params.dig(:search, :q)})) 4 | - else 5 | = t('.message_for_latest_search_html'.freeze, url: root_path) 6 | -------------------------------------------------------------------------------- /app/views/home/_recipes_relation.html.slim: -------------------------------------------------------------------------------- 1 | div class="recipes" 2 | - @recipes.each do |recipe| 3 | = render partial: 'recipe'.freeze, locals: {recipe: recipe} 4 | 5 | = paginate @recipes, remote: true, params: home_params.search, param_name: home_params.current_filter_page_name 6 | 7 | / Workaround to fix Chrome scrollTop issue 8 | a.scrollTopFixer style="display: none;" href="#scrollTopTarget" data-turbolinks="false" Top 9 | -------------------------------------------------------------------------------- /app/views/home/_recipes_search_filters.html.slim: -------------------------------------------------------------------------------- 1 | div class="recipes-search-filters col-md-6 col-sm-5 col-xs-12" 2 | div class="btn-group" role="group" aria-label="..." 3 | = link_to_recipes_filter(Home::Params::LATEST_FILTER) 4 | = link_to_recipes_filter(Home::Params::MY_RECIPES_FILTER) 5 | -------------------------------------------------------------------------------- /app/views/home/index.html+app.slim: -------------------------------------------------------------------------------- 1 | section class="row" 2 | div class="col-xs-12" 3 | div class="card" 4 | div class="header text-center" 5 | / Workaround to fix Chrome scrollTop issue 6 | h1#scrollTopTarget= t('titles.application') 7 | 8 | div class="recipes-search row" 9 | - if home_params.fetch_search(:q).present? 10 | = render partial: 'recipes_search_filters'.freeze 11 | 12 | div class="content" 13 | = render partial: 'recipes'.freeze 14 | -------------------------------------------------------------------------------- /app/views/home/index.html.slim: -------------------------------------------------------------------------------- 1 | section class="row" 2 | div class="col-xs-12" 3 | div class="card" 4 | div class="header text-center" 5 | h1= t('titles.application') 6 | 7 | div class="recipes-search row" 8 | = render partial: 'recipes_search_filters'.freeze 9 | 10 | div class="recipes-search-form col-md-6 col-sm-7 col-xs-12" 11 | = bootstrap_form_tag url: root_path, layout: :inline, method: :get, data: {global_search: true } do |f| 12 | = f.hidden_field 'search[filter]', value: home_params.current_filter 13 | = f.text_field 'search[q]', value: home_params.fetch_search(:q), placeholder: t('.search.placeholder'), class: 'recipes-search-input', hide_label: true 14 | = f.submit t('.search.button'), class: "btn btn-default btn-fill" 15 | 16 | div class="content" 17 | = render partial: 'recipes'.freeze 18 | -------------------------------------------------------------------------------- /app/views/home/index.js.erb: -------------------------------------------------------------------------------- 1 | <% if has_recipes? %> 2 | $('.recipes-search-filters').replaceWith("<%= j render('home/recipes_search_filters'.freeze) %>"); 3 | <% end %> 4 | 5 | $('.recipes').replaceWith("<%= j render('home/recipes'.freeze) %>"); 6 | 7 | setTimeout( 8 | function() { 9 | // Workaround to fix Chrome scrollTop issue 10 | <% if turbolinks_app? %> 11 | TurbolinksNativeMessageHandler.postMessage({scrollTop: true}); 12 | <% else %> 13 | $('.scrollTopFixer')[0].click(); 14 | <% end %> 15 | $('[data-animate-css]').animateCSS('fadeIn'); 16 | }, 17 | 0 18 | ); 19 | -------------------------------------------------------------------------------- /app/views/kaminari/_first_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless current_page.first?, raw(t 'views.pagination.first'), 3 | url, remote: remote 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_gap.html.slim: -------------------------------------------------------------------------------- 1 | li.disabled 2 | = link_to raw(t 'views.pagination.truncate'), '#' 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_last_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless current_page.last?, raw(t 'views.pagination.last'), 3 | url, remote: remote 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_next_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless current_page.last?, raw(t 'views.pagination.next'), 3 | url, rel: 'next', remote: remote 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_page.html.slim: -------------------------------------------------------------------------------- 1 | li class="#{'active' if page.current?}" 2 | = link_to page, page.current? ? '#' : url, 3 | remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_paginator.html.slim: -------------------------------------------------------------------------------- 1 | = paginator.render do 2 | ul.pagination 3 | == first_page_tag unless current_page.first? || mobile_variant? 4 | /== prev_page_tag unless current_page.first? 5 | 6 | - each_page do |page| 7 | - if page.left_outer? || page.right_outer? || page.inside_window? 8 | == page_tag page 9 | - elsif !page.was_truncated? 10 | == gap_tag 11 | 12 | /== next_page_tag unless current_page.last? 13 | == last_page_tag unless current_page.last? || mobile_variant? 14 | -------------------------------------------------------------------------------- /app/views/kaminari/_prev_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), 3 | url, rel: 'prev', remote: remote 4 | -------------------------------------------------------------------------------- /app/views/layouts/_flash_messages.html.slim: -------------------------------------------------------------------------------- 1 | - if flash.present? 2 | ul.hidden id="flash-messages" 3 | - flash.each do |type, text| 4 | li data-type=type data-text=text 5 | -------------------------------------------------------------------------------- /app/views/layouts/_footer.html.slim: -------------------------------------------------------------------------------- 1 | footer class="footer" 2 | div class="container-fluid" data-turbolinks="false" 3 | nav class="pull-left" 4 | ul 5 | li= link_to 'https://github.com/themasterapp'.freeze, target: '_blank'.freeze do 6 | | Github 7 | 8 | div class="copyright pull-right" 9 | | © #{t('app.release_year'.freeze)}, 10 | | #{t('.made_with_love_html'.freeze, contact: mail_to(t('company.email'.freeze), t('company.name'.freeze)))} 11 | -------------------------------------------------------------------------------- /app/views/layouts/_sidebar.html.slim: -------------------------------------------------------------------------------- 1 | / Tip 1: you can change the color of the sidebar's background using: data-background-color="white | black" 2 | / Tip 2: you can change the color of the active button using the data-active-color="primary | info | success | warning | danger" 3 | 4 | div class="sidebar" data-background-color="white" data-active-color="danger" 5 | div class="sidebar-wrapper" 6 | div class="logo" 7 | = link_to t('titles.application'), root_path, class: "simple-text" 8 | 9 | ul class="nav" 10 | - if !user_signed_in? 11 | li class="#{'active' if current_page?(new_user_session_path)}" 12 | = link_to new_user_session_path do 13 | i> class="ti-key" 14 | p= t('app.nav.views.sessions.new') 15 | 16 | li class="#{'active' if current_page?(new_user_registration_path)}" 17 | = link_to new_user_registration_path do 18 | i> class="ti-user" 19 | p= t('app.nav.views.registrations.new') 20 | li class="#{'active' if current_page?(root_path) && !home_params.my_recipes_filter?}" 21 | = link_to root_path do 22 | i> class="ti-book" 23 | p= t('app.nav.views.home.index') 24 | 25 | li class="#{'active' if home_params.my_recipes_filter?}" 26 | = link_to root_path(home_params.to_search) do 27 | i> class="ti-write" 28 | p= t('home.recipes_search_filters.my') 29 | 30 | - if user_signed_in? 31 | li class="#{'active' if current_page?(new_recipe_path)}" 32 | = link_to new_recipe_path do 33 | i> class="ti-pencil" 34 | p= t('app.nav.views.recipes.new') 35 | -------------------------------------------------------------------------------- /app/views/layouts/_topnavbar.html.slim: -------------------------------------------------------------------------------- 1 | nav class="navbar navbar-default" 2 | div class="container-fluid" 3 | div.navbar-header data-turbolinks="false" 4 | = render('layouts/navbar/header') 5 | 6 | div class="collapse navbar-collapse" 7 | ul class="nav navbar-nav navbar-right" 8 | 9 | - if !user_signed_in? 10 | = render('layouts/navbar/locales/for_guests') 11 | 12 | - else 13 | = render('layouts/navbar/settings') 14 | -------------------------------------------------------------------------------- /app/views/layouts/application.html+app.slim: -------------------------------------------------------------------------------- 1 | doctype html 2 | html data-turbolinks-native-message-handler=ENV.fetch('TURBOLINKS_NATIVE_MESSAGE_HANDLER'.freeze) 3 | head 4 | / See meta-tags gem and MetaTagsHelpers concern. 5 | = display_meta_tags 6 | 7 | meta charset="utf-8" 8 | meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" 9 | meta http-equiv="X-UA-Compatible" content="IE=edge" 10 | 11 | = csrf_meta_tags 12 | 13 | = stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" 14 | link href='https://fonts.googleapis.com/css?family=Muli:400,300' rel='stylesheet' type='text/css' 15 | 16 | 17 | = javascript_include_tag "application", "data-turbolinks-track": "reload" 18 | 19 | body class=body_class("app-mode #{'android-app' if turbolinks_android_app?}") *body_data 20 | div class="wrapper" data-auto-navbar-collapse="true" 21 | = render "layouts/flash_messages" 22 | 23 | div class="main-panel" 24 | div class="content" data-animate-css="" 25 | div class="container-fluid" 26 | = yield 27 | -------------------------------------------------------------------------------- /app/views/layouts/application.html+msite.slim: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | / See meta-tags gem and MetaTagsHelpers concern. 5 | = display_meta_tags 6 | 7 | meta charset="utf-8" 8 | meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" 9 | meta http-equiv="X-UA-Compatible" content="IE=edge" 10 | 11 | = csrf_meta_tags 12 | 13 | = stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" 14 | link href='https://fonts.googleapis.com/css?family=Muli:400,300' rel='stylesheet' type='text/css' 15 | 16 | 17 | = javascript_include_tag "application", "data-turbolinks-track": "reload" 18 | 19 | body class=body_class('msite-mode'.freeze) *body_data 20 | div class="wrapper" data-auto-navbar-collapse="true" 21 | = render "layouts/flash_messages" 22 | 23 | = render "layouts/sidebar" 24 | 25 | div class="main-panel" 26 | 27 | = render "layouts/topnavbar" 28 | 29 | div class="content" data-animate-css="" 30 | div class="container-fluid" 31 | = yield 32 | 33 | = render "layouts/footer" 34 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.slim: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | / See meta-tags gem and MetaTagsHelpers concern. 5 | = display_meta_tags 6 | 7 | meta charset="utf-8" 8 | meta name="viewport" content="width=device-width, initial-scale=1.0" 9 | meta http-equiv="X-UA-Compatible" content="IE=edge" 10 | 11 | = csrf_meta_tags 12 | 13 | = stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" 14 | link href='https://fonts.googleapis.com/css?family=Muli:400,300' rel='stylesheet' type='text/css' 15 | 16 | 17 | = javascript_include_tag "application", "data-turbolinks-track": "reload" 18 | 19 | body class=body_class('site-mode'.freeze) *body_data 20 | div class="wrapper" 21 | = render "layouts/flash_messages" 22 | 23 | = render "layouts/sidebar" 24 | 25 | div class="main-panel" data-animate-css="" 26 | 27 | = render "layouts/topnavbar" 28 | 29 | div class="content" 30 | div class="container-fluid" 31 | = yield 32 | 33 | = render "layouts/footer" 34 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/layouts/navbar/_header.html.slim: -------------------------------------------------------------------------------- 1 | button type="button" class="navbar-toggle" 2 | span.sr-only= t('app.nav.toggle_navigation'.freeze) 3 | span class="icon-bar bar1" 4 | span class="icon-bar bar2" 5 | span class="icon-bar bar3" 6 | 7 | / Workaround to fix Chrome scrollTop issue 8 | a class="navbar-brand" id="scrollTopTarget" 9 | = t("app.nav.views.#{controller_name}.#{action_name}") 10 | -------------------------------------------------------------------------------- /app/views/layouts/navbar/_settings.html.slim: -------------------------------------------------------------------------------- 1 | li class="dropdown" 2 | a href="#" class="dropdown-toggle" data-toggle="dropdown" data-turbolinks="false" 3 | i> class="ti-settings" 4 | p= t('app.nav.settings') 5 | b class="caret" 6 | 7 | ul class="dropdown-menu" 8 | li= link_to edit_user_registration_path do 9 | = t('app.nav.views.registrations.edit') 10 | 11 | li= render('layouts/navbar/locales/for_users') 12 | 13 | li= link_to destroy_user_session_path, method: :delete, remote: true do 14 | = t('app.nav.log_out') 15 | -------------------------------------------------------------------------------- /app/views/layouts/navbar/locales/_for_guests.html.slim: -------------------------------------------------------------------------------- 1 | / # TODO: Create a component/helper and improve the layout according to the variant 2 | li 3 | = link_to url_for(params.to_unsafe_hash.merge!(locale: I18n.locale == :en ? :"pt-BR" : :en)), data: {locale: true} do 4 | i> class="ti-world" 5 | p= t('.call_to_action_html'.freeze) 6 | -------------------------------------------------------------------------------- /app/views/layouts/navbar/locales/_for_users.html.slim: -------------------------------------------------------------------------------- 1 | / # TODO: Create a component/helper and improve the layout according to the variant 2 | = link_to url_for(params.to_unsafe_hash.merge!(locale: I18n.locale == :en ? :"pt-BR" : :en)), data: {locale: true} do 3 | = t('.call_to_action_html'.freeze) 4 | -------------------------------------------------------------------------------- /app/views/recipes/_form.html.slim: -------------------------------------------------------------------------------- 1 | = bootstrap_form_for @recipe, remote: true do |f| 2 | 3 | = f.text_field :title, autofocus: true, placeholder: t('.placeholders.title'), label: human_attribute(:title) 4 | = f.text_area :story, autofocus: true, placeholder: t('.placeholders.story'), label: human_attribute(:story) 5 | = f.text_area :ingredients, placeholder: t('.placeholders.ingredients'), label: human_attribute(:ingredients) 6 | = f.text_area :instructions, placeholder: t('.placeholders.instructions'), label: human_attribute(:instructions) 7 | 8 | = f.submit t('.submit'), class: 'btn btn-primary btn-fill' 9 | -------------------------------------------------------------------------------- /app/views/recipes/edit.html.slim: -------------------------------------------------------------------------------- 1 | div class="row" 2 | div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3 col-xs-12" 3 | div class="card" 4 | div class="header" 5 | h3 class="title" 6 | = t('.welcome') 7 | i< class="ti-medall text-warning" 8 | 9 | div class="content" 10 | = render('recipes/form') 11 | -------------------------------------------------------------------------------- /app/views/recipes/new.html.slim: -------------------------------------------------------------------------------- 1 | div class="row" 2 | div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3 col-xs-12" 3 | div class="card" 4 | div class="header" 5 | h3 class="title" 6 | = t('.welcome') 7 | i< class="ti-heart text-danger" 8 | 9 | div class="content" 10 | = render('recipes/form') 11 | -------------------------------------------------------------------------------- /app/views/recipes/save.js.erb: -------------------------------------------------------------------------------- 1 | $('form[class*="_recipe"]').replaceWith("<%= j render('recipes/form'.freeze) %>"); 2 | -------------------------------------------------------------------------------- /app/views/recipes/show.html.slim: -------------------------------------------------------------------------------- 1 | div class="row recipe" 2 | div class="col-md-8 col-md-offset-2 col-sm-8 col-sm-offset-2 col-xs-12" 3 | div class="card" 4 | div class="header" 5 | - if policy(@recipe).edit? 6 | p class="user" 7 | = link_to edit_recipe_path(@recipe), class: 'btn btn-warning btn-xs btn-fill edit' do 8 | i class="ti-pencil-alt" 9 | = t('edit') 10 | 11 | = link_to(recipe_path(@recipe), data: confirm_dialog_data_to_action_destroy, class: 'btn btn-danger btn-xs destroy') do 12 | i> class="ti-trash" 13 | = t('destroy') 14 | 15 | h2 class="title" 16 | strong= @recipe.title 17 | 18 | div class="content" 19 | p 20 | small.text-info=@recipe.user.name 21 | 22 | = simple_format(@recipe.story) 23 | 24 | h3 class="text-primary" 25 | = human_attribute(:ingredients) 26 | 27 | = simple_format(@recipe.ingredients) 28 | 29 | h3 class="text-primary" 30 | = human_attribute(:instructions) 31 | 32 | = simple_format(@recipe.instructions) 33 | 34 | 35 | -------------------------------------------------------------------------------- /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 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) 11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) } 12 | gem 'spring', match[1] 13 | require 'spring/binstub' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module MasterApp 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Errors Pages 16 | config.exceptions_app = self.routes 17 | 18 | # Active Job 19 | config.active_job.queue_adapter = :sucker_punch 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 9.1 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: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 23 | 24 | development: 25 | <<: *default 26 | database: master-app_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: master-app 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: master-app_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: master-app_production 84 | username: master-app 85 | password: <%= ENV['MASTER-APP_DATABASE_PASSWORD'] %> 86 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 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. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | config.action_mailer.default_url_options = { host: ENV.fetch('APPLICATION_HOST'), port: ENV.fetch('PORT') } 30 | config.action_mailer.delivery_method = :letter_opener 31 | 32 | # Don't care if the mailer can't send. 33 | config.action_mailer.raise_delivery_errors = false 34 | 35 | config.action_mailer.perform_caching = false 36 | 37 | # Print deprecation notices to the Rails logger. 38 | config.active_support.deprecation = :log 39 | 40 | # Raise an error on page load if there are pending migrations. 41 | config.active_record.migration_error = :page_load 42 | 43 | # Debug mode disables concatenation and preprocessing of assets. 44 | # This option may cause significant delays in view rendering with a large 45 | # number of complex assets. 46 | config.assets.debug = true 47 | 48 | # Suppress logger output for asset requests. 49 | config.assets.quiet = true 50 | 51 | # Raises error for missing translations 52 | config.action_view.raise_on_missing_translations = true 53 | 54 | # Use an evented file watcher to asynchronously detect changes in source code, 55 | # routes, locales, etc. This feature depends on the listen gem. 56 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 57 | end 58 | -------------------------------------------------------------------------------- /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 | # Disable serving static files from the `/public` folder by default since 18 | # Apache or NGINX already handles this. 19 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 20 | 21 | # Compress JavaScripts and CSS. 22 | config.assets.js_compressor = :uglifier 23 | # config.assets.css_compressor = :sass 24 | 25 | # Do not fallback to assets pipeline if a precompiled asset is missed. 26 | config.assets.compile = false 27 | 28 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 29 | 30 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 31 | # config.action_controller.asset_host = 'http://assets.example.com' 32 | 33 | # Specifies the header that your server uses for sending files. 34 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 35 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 36 | 37 | # Mount Action Cable outside main process or domain 38 | # config.action_cable.mount_path = nil 39 | # config.action_cable.url = 'wss://example.com/cable' 40 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 41 | 42 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 43 | config.force_ssl = ENV['APPLICATION_FORCE_SSL'].present? 44 | 45 | # Use the lowest log level to ensure availability of diagnostic information 46 | # when problems arise. 47 | config.log_level = :debug 48 | 49 | # Prepend all log lines with the following tags. 50 | config.log_tags = [ :request_id ] 51 | 52 | # Use a different cache store in production. 53 | # config.cache_store = :mem_cache_store 54 | 55 | # Use a real queuing backend for Active Job (and separate queues per environment) 56 | # config.active_job.queue_adapter = :resque 57 | # config.active_job.queue_name_prefix = "master-app_#{Rails.env}" 58 | config.action_mailer.perform_caching = false 59 | 60 | # Ignore bad email addresses and do not raise email delivery errors. 61 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 62 | # config.action_mailer.raise_delivery_errors = false 63 | 64 | config.action_mailer.default_url_options = { host: ENV.fetch('APPLICATION_HOST') } 65 | 66 | config.action_mailer.smtp_settings = { 67 | :address => ENV.fetch('SMTP_ADDRESS'), 68 | :port => ENV.fetch('SMTP_PORT', 587), 69 | :authentication => :plain, 70 | :user_name => ENV.fetch('SMTP_USERNAME'), 71 | :password => ENV.fetch('SMTP_PASSWORD'), 72 | :domain => ENV.fetch('SMTP_DOMAIN'), 73 | :enable_starttls_auto => true 74 | } 75 | 76 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 77 | # the I18n.default_locale when a translation cannot be found). 78 | config.i18n.fallbacks = true 79 | 80 | # Send deprecation notices to registered listeners. 81 | config.active_support.deprecation = :notify 82 | 83 | # Use default logging formatter so that PID and timestamp are not suppressed. 84 | config.log_formatter = ::Logger::Formatter.new 85 | 86 | # Use a different logger for distributed setups. 87 | # require 'syslog/logger' 88 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 89 | 90 | if ENV["RAILS_LOG_TO_STDOUT"].present? 91 | logger = ActiveSupport::Logger.new(STDOUT) 92 | logger.formatter = config.log_formatter 93 | config.logger = ActiveSupport::TaggedLogging.new(logger) 94 | end 95 | 96 | # Do not dump schema after migrations. 97 | config.active_record.dump_schema_after_migration = false 98 | end 99 | -------------------------------------------------------------------------------- /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 public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | config.action_mailer.default_url_options = { host: ENV.fetch('APPLICATION_HOST'), port: ENV.fetch('PORT') } 38 | 39 | # Print deprecation notices to the stderr. 40 | config.active_support.deprecation = :stderr 41 | 42 | # Raises error for missing translations 43 | config.action_view.raise_on_missing_translations = true 44 | end 45 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /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( search.js ) 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 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/friendly_id.rb: -------------------------------------------------------------------------------- 1 | # FriendlyId Global Configuration 2 | # 3 | # Use this to set up shared configuration options for your entire application. 4 | # Any of the configuration options shown here can also be applied to single 5 | # models by passing arguments to the `friendly_id` class method or defining 6 | # methods in your model. 7 | # 8 | # To learn more, check out the guide: 9 | # 10 | # http://norman.github.io/friendly_id/file.Guide.html 11 | 12 | FriendlyId.defaults do |config| 13 | # ## Reserved Words 14 | # 15 | # Some words could conflict with Rails's routes when used as slugs, or are 16 | # undesirable to allow as slugs. Edit this list as needed for your app. 17 | config.use :reserved 18 | 19 | config.reserved_words = %w(new edit index session login logout users admin 20 | stylesheets assets javascripts images) 21 | 22 | # ## Friendly Finders 23 | # 24 | # Uncomment this to use friendly finders in all models. By default, if 25 | # you wish to find a record by its friendly id, you must do: 26 | # 27 | # MyModel.friendly.find('foo') 28 | # 29 | # If you uncomment this, you can do: 30 | # 31 | # MyModel.find('foo') 32 | # 33 | # This is significantly more convenient but may not be appropriate for 34 | # all applications, so you must explicity opt-in to this behavior. You can 35 | # always also configure it on a per-model basis if you prefer. 36 | # 37 | # Something else to consider is that using the :finders addon boosts 38 | # performance because it will avoid Rails-internal code that makes runtime 39 | # calls to `Module.extend`. 40 | # 41 | # config.use :finders 42 | # 43 | # ## Slugs 44 | # 45 | # Most applications will use the :slugged module everywhere. If you wish 46 | # to do so, uncomment the following line. 47 | # 48 | # config.use :slugged 49 | # 50 | # By default, FriendlyId's :slugged addon expects the slug column to be named 51 | # 'slug', but you can change it if you wish. 52 | # 53 | # config.slug_column = 'slug' 54 | # 55 | # When FriendlyId can not generate a unique ID from your base method, it appends 56 | # a UUID, separated by a single dash. You can configure the character used as the 57 | # separator. If you're upgrading from FriendlyId 4, you may wish to replace this 58 | # with two dashes. 59 | # 60 | # config.sequence_separator = '-' 61 | # 62 | # ## Tips and Tricks 63 | # 64 | # ### Controlling when slugs are generated 65 | # 66 | # As of FriendlyId 5.0, new slugs are generated only when the slug field is 67 | # nil, but if you're using a column as your base method can change this 68 | # behavior by overriding the `should_generate_new_friendly_id` method that 69 | # FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave 70 | # more like 4.0. 71 | # 72 | # config.use Module.new { 73 | # def should_generate_new_friendly_id? 74 | # slug.blank? || _changed? 75 | # end 76 | # } 77 | # 78 | # FriendlyId uses Rails's `parameterize` method to generate slugs, but for 79 | # languages that don't use the Roman alphabet, that's not usually sufficient. 80 | # Here we use the Babosa library to transliterate Russian Cyrillic slugs to 81 | # ASCII. If you use this, don't forget to add "babosa" to your Gemfile. 82 | # 83 | # config.use Module.new { 84 | # def normalize_friendly_id(text) 85 | # text.to_slug.normalize! :transliterations => [:russian, :latin] 86 | # end 87 | # } 88 | end 89 | -------------------------------------------------------------------------------- /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/kaminari_config.rb: -------------------------------------------------------------------------------- 1 | Kaminari.configure do |config| 2 | config.default_per_page = Integer(ENV.fetch('PAGINATION_DEFAULT_PER_PAGE')) 3 | # config.max_per_page = nil 4 | config.window = Integer(ENV.fetch('PAGINATION_DEFAULT_WINDOW')) 5 | # config.outer_window = 0 6 | # config.left = 0 7 | # config.right = 0 8 | # config.page_method_name = :page 9 | # config.param_name = :page 10 | end 11 | -------------------------------------------------------------------------------- /config/initializers/locales.rb: -------------------------------------------------------------------------------- 1 | I18n.load_path += Dir[Rails.root.join("config", "locales", "**", "*.{rb,yml}")] 2 | 3 | I18n.available_locales = [:en, :"pt-BR"] 4 | 5 | I18n.default_locale = :"pt-BR" 6 | -------------------------------------------------------------------------------- /config/initializers/meta_tags.rb: -------------------------------------------------------------------------------- 1 | MetaTags.configure do |config| 2 | config.title_limit = 70 3 | config.description_limit = 180 4 | config.keywords_limit = 255 5 | config.keywords_separator = ', '.freeze 6 | end 7 | -------------------------------------------------------------------------------- /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/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Read the Rails 5.0 release notes for more info on each option. 6 | 7 | # Enable per-form CSRF tokens. Previous versions had false. 8 | Rails.application.config.action_controller.per_form_csrf_tokens = true 9 | 10 | # Enable origin-checking CSRF mitigation. Previous versions had false. 11 | Rails.application.config.action_controller.forgery_protection_origin_check = true 12 | 13 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 14 | # Previous versions had false. 15 | ActiveSupport.to_time_preserves_timezone = true 16 | 17 | # Require `belongs_to` associations by default. Previous versions had false. 18 | Rails.application.config.active_record.belongs_to_required_by_default = true 19 | 20 | # Do not halt callback chains when a callback returns false. Previous versions had true. 21 | ActiveSupport.halt_callback_chains_on_return_false = false 22 | 23 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 24 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 25 | -------------------------------------------------------------------------------- /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: '_master-app_session' 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # 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/global.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | company: 4 | name: YSimplicity 5 | email: info@ysimplicity.com 6 | 7 | app: 8 | release_year: "2016" 9 | meta: 10 | description: Some of the best recipes you just find here. 11 | 12 | auth: 13 | access_denied: Access denied. 14 | 15 | mailer: 16 | no_reply_email: "no-reply@masterapp.mobi" 17 | 18 | # Commons 19 | back: Back 20 | show: Show 21 | edit: Edit 22 | update: Update 23 | destroy: Destroy 24 | confirm_question: Are you sure? 25 | confirm_btn: 'Yes, delete it!' # Sweet Alert confirmation dialog 26 | cancel_btn: 'Cancel' # Sweet Alert confirmation dialog 27 | -------------------------------------------------------------------------------- /config/locales/en/models/recipe.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | activerecord: 4 | models: 5 | recipe: 6 | one: Recipe 7 | other: Recipes 8 | 9 | attributes: 10 | recipe: 11 | title: Title 12 | story: Story 13 | ingredients: Ingredients 14 | instructions: Instructions 15 | -------------------------------------------------------------------------------- /config/locales/en/models/user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | activerecord: 4 | models: 5 | user: 6 | one: User 7 | other: Users 8 | 9 | attributes: 10 | user/role: 11 | user: User 12 | admin: Admin 13 | user: 14 | name: Name 15 | role: Role 16 | email: Email 17 | password: Password 18 | password_confirmation: Password Confirmation 19 | remember_me: Remember me 20 | current_sign_in_at: Current sign in at 21 | last_sign_in_at: Last sign in 22 | sign_in_count: Sign in counts 23 | reset_password_token: Reset password token 24 | encrypted_password: Encrypted password 25 | current_sign_in_ip: Current sign in IP 26 | last_sign_in_ip: Last sign in IP 27 | reset_password_sent_at: Reset password sent at 28 | remember_created_at: Remember created at 29 | created_at: Created at 30 | updated_at: Updated at 31 | -------------------------------------------------------------------------------- /config/locales/en/navigation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | app: 4 | nav: 5 | log_out: Log out 6 | settings: Settings 7 | toggle_navigation: Toggle navigation 8 | views: 9 | sessions: 10 | new: Sign in 11 | registrations: 12 | new: Sign up 13 | edit: My account 14 | passwords: 15 | new: I forgot my password 16 | edit: Change your password 17 | errors: 18 | not_found: Page Not Found 19 | internal_server_error: Internal Server Error 20 | home: 21 | index: Recipes 22 | recipes: 23 | new: New Recipe 24 | create: New Recipe 25 | edit: Edit Recipe 26 | update: Edit Recipe 27 | show: Recipe 28 | -------------------------------------------------------------------------------- /config/locales/en/pagination.yml: -------------------------------------------------------------------------------- 1 | en: 2 | views: 3 | pagination: 4 | first: "« First" 5 | last: "Last »" 6 | previous: "‹ Prev" 7 | next: "Next ›" 8 | truncate: "…" 9 | helpers: 10 | page_entries_info: 11 | one_page: 12 | display_entries: 13 | zero: "No %{entry_name} found" 14 | one: "Displaying 1 %{entry_name}" 15 | other: "Displaying all %{count} %{entry_name}" 16 | more_pages: 17 | display_entries: "Displaying %{entry_name} %{first} - %{last} of %{total} in total" 18 | -------------------------------------------------------------------------------- /config/locales/en/titles.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | titles: 4 | application: Master App 5 | home: 6 | index: Recipes 7 | recipes: 8 | show: Recipe 9 | -------------------------------------------------------------------------------- /config/locales/en/views/devise/mailer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | devise: 4 | mailer: 5 | reset_password_instructions: 6 | hello: Hello %{email}! 7 | announcement: Someone has requested a link to change your password. You can do this through the link below. 8 | change_my_password: Change my password 9 | ignore_message: If you didn't request this, please ignore this email. 10 | notice_message: Your password won't change until you access the link above and create a new one. 11 | -------------------------------------------------------------------------------- /config/locales/en/views/devise/passwords.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | devise: 4 | passwords: 5 | sign_in_cta_html: &sign_in_cta_html 6 | "Never mind, go back to the sign in screen" 7 | new: 8 | welcome: Forgot your password? 9 | submit: Send me reset password instructions 10 | sign_in_cta_html: *sign_in_cta_html 11 | 12 | edit: 13 | title: Register a new password 14 | sign_in_cta_html: *sign_in_cta_html 15 | form: 16 | submit_button: Change my password 17 | password_confirmation_help: (%{length} characters minimum) 18 | -------------------------------------------------------------------------------- /config/locales/en/views/devise/registrations.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | devise: 4 | registrations: 5 | password_confirmation_help: &password_confirmation_help 6 | "(%{length} characters minimum)" 7 | new: 8 | welcome: Sign up 9 | description: Create account to see it in action. 10 | form: 11 | sign_up: Sign up 12 | password_confirmation_help: *password_confirmation_help 13 | sign_in: Log in 14 | sign_in_cta: Already have an account? 15 | terms_and_policy: Agree the terms and policy 16 | 17 | edit: 18 | form: 19 | email_confirmation: "Currently waiting confirmation for: %{email}" 20 | current_password_help: (we need your current password to confirm your changes) 21 | password_confirmation: 22 | placeholder: *password_confirmation_help 23 | help: (leave blank if you don't want to change it) 24 | cancel_account: 25 | header: Cancel Account 26 | question: Unhappy? We'll be sad to see you go. 27 | button: Cancel my account 28 | -------------------------------------------------------------------------------- /config/locales/en/views/devise/sessions.yml: -------------------------------------------------------------------------------- 1 | # cta = call to action 2 | --- 3 | en: 4 | devise: 5 | sessions: 6 | new: 7 | welcome: Welcome 8 | form: 9 | sign_in: Log in 10 | forgot_password: Forgot password? 11 | account_cta: Sign up 12 | account_question: Do not have an account? 13 | -------------------------------------------------------------------------------- /config/locales/en/views/errors/internal_server_error.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | errors: 4 | internal_server_error: 5 | status: 6 | code: 500 7 | message: Internal Server Error 8 | 9 | description_html: | 10 | The server encountered something unexpected that didn't allow it to complete the request.
11 | We apologize. 12 | 13 | homepage: Back to recipes page 14 | -------------------------------------------------------------------------------- /config/locales/en/views/errors/not_found.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | errors: 4 | not_found: 5 | homepage: Recipes 6 | 7 | status: 8 | code: 404 9 | message: 10 | Sorry, the page you requested was not found. 11 | 12 | description: 13 | You may have clicked an expired link or mistyped the address. 14 | 15 | form: 16 | placeholder: What do you wish? 17 | submit: Search 18 | -------------------------------------------------------------------------------- /config/locales/en/views/home.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | home: 4 | index: 5 | titles: &recipes_search_filters 6 | my: My Recipes 7 | latest: Latest 8 | search: 9 | placeholder: Look for a delicious recipe! 10 | button: Search 11 | 12 | recipes_search_filters: 13 | <<: *recipes_search_filters 14 | 15 | recipes: 16 | my_recipes_guest_html: | 17 | Hi, 18 | sign up 19 | to publish your recipes! 20 | 21 | my_recipes_user_html: | 22 | You haven't published any recipe yet.
23 | What about 24 | publishing one 25 | now? 26 | 27 | recipes_not_found: 28 | message_for_my_search_html: | 29 | Would you like 30 | to search about this 31 | in all recipes? 32 | 33 | message_for_latest_search_html: | 34 | Sorry!
35 | The recipe were looking for could not be found.
36 | Look all recipes!. 37 | -------------------------------------------------------------------------------- /config/locales/en/views/layouts/footer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | layouts: 4 | footer: 5 | made_with_love_html: 6 | "made with by %{contact}" 7 | -------------------------------------------------------------------------------- /config/locales/en/views/layouts/navbar.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | layouts: 4 | navbar: 5 | locales: 6 | defaults: &call_to_action 7 | call_to_action_html: 8 | Switch to portuguese 9 | 10 | for_users: 11 | <<: *call_to_action 12 | for_guests: 13 | <<: *call_to_action 14 | -------------------------------------------------------------------------------- /config/locales/en/views/recipes.yml: -------------------------------------------------------------------------------- 1 | --- 2 | en: 3 | recipes: 4 | new: 5 | welcome: Share your recipe with us! 6 | edit: 7 | welcome: Make your recipe even better! 8 | 9 | create: 10 | notice: Recipe was successfully published. 11 | 12 | update: 13 | notice: Recipe was successfully updated. 14 | 15 | destroy: 16 | notice: Recipe was successfully destroyed. 17 | 18 | form: 19 | submit: Publish 20 | placeholders: 21 | title: Recipe title... 22 | story: Tell the story of the recipe... 23 | ingredients: '- 1 large egg' 24 | instructions: '1. In a large mixing bowl, combine the egg...' 25 | -------------------------------------------------------------------------------- /config/locales/pt-BR/global.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | company: 4 | name: YSimplicity 5 | email: info@ysimplicity.com 6 | 7 | app: 8 | release_year: "2016" 9 | meta: 10 | description: Algumas das melhores receitas você só encontra aqui. 11 | 12 | auth: 13 | access_denied: Acesso negado. 14 | 15 | mailer: 16 | no_reply_email: "nao-responda@masterapp.mobi" 17 | 18 | # Commons 19 | back: Voltar 20 | show: Visualizar 21 | edit: Editar 22 | update: Atualizar 23 | destroy: Remover 24 | confirm_question: Você tem certeza? 25 | confirm_btn: 'Sim, remova!' # Sweet Alert confirmation dialog 26 | cancel_btn: 'Cancelar' # Sweet Alert confirmation dialog 27 | -------------------------------------------------------------------------------- /config/locales/pt-BR/models/recipe.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | activerecord: 4 | models: 5 | recipe: 6 | one: Receita 7 | other: Receitas 8 | 9 | attributes: 10 | recipe: 11 | title: Título 12 | story: Comentários 13 | ingredients: Ingredientes 14 | instructions: Instruções 15 | -------------------------------------------------------------------------------- /config/locales/pt-BR/models/user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | activerecord: 4 | models: 5 | user: 6 | one: Usuário 7 | other: Usuários 8 | 9 | attributes: 10 | user/role: 11 | user: Usuário 12 | admin: Administrador 13 | user: 14 | name: Nome 15 | role: Nível de acesso 16 | email: Email 17 | password: Senha 18 | password_confirmation: Confirmar senha 19 | remember_me: Permanecer conectado 20 | current_sign_in_at: Login atual 21 | last_sign_in_at: Último login 22 | sign_in_count: Número de logins 23 | reset_password_token: Token de redefinição de senha 24 | encrypted_password: Senha Encriptada 25 | current_sign_in_ip: Login atual no IP 26 | last_sign_in_ip: Último login no IP 27 | reset_password_sent_at: Redefinição da senha enviada em 28 | remember_created_at: Lembrete criado em 29 | created_at: Criado em 30 | updated_at: Atualizado em 31 | -------------------------------------------------------------------------------- /config/locales/pt-BR/navigation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | app: 4 | nav: 5 | log_out: Logout 6 | settings: Configurações 7 | toggle_navigation: Alternar navegação 8 | views: 9 | sessions: 10 | new: Login 11 | registrations: 12 | new: Cadastro 13 | edit: Minha conta 14 | passwords: 15 | new: Esqueci minha senha 16 | edit: Altere sua senha 17 | errors: 18 | not_found: Página Não Encontrada 19 | internal_server_error: Erro Interno do Servidor 20 | home: 21 | index: Receitas 22 | recipes: 23 | new: Nova Receita 24 | create: Nova Receita 25 | edit: Editar Receita 26 | update: Editar Receita 27 | show: Receita 28 | -------------------------------------------------------------------------------- /config/locales/pt-BR/pagination.yml: -------------------------------------------------------------------------------- 1 | pt-BR: 2 | views: 3 | pagination: 4 | first: "« Primeira" 5 | last: "Última »" 6 | next: Próxima › 7 | previous: "‹ Anterior" 8 | truncate: "…" 9 | helpers: 10 | page_entries_info: 11 | one_page: 12 | display_entries: 13 | one: Exibindo 1 %{entry_name} 14 | other: Exibindo %{count} %{entry_name} 15 | zero: Nenhum %{entry_name} encontrado 16 | more_pages: 17 | display_entries: Exibindo %{entry_name} %{first} - %{last} de um total de %{total} 18 | -------------------------------------------------------------------------------- /config/locales/pt-BR/titles.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | titles: 4 | application: Master App 5 | home: 6 | index: Receitas 7 | recipes: 8 | show: Receita 9 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/devise/mailer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | devise: 4 | mailer: 5 | reset_password_instructions: 6 | hello: Olá %{email}! 7 | announcement: Alguém pediu um link para alterar sua senha. Você pode fazer isso através do link abaixo. 8 | change_my_password: Altere minha senha 9 | ignore_message: Se você não solicitou isto, por favor ignore este e-mail. 10 | notice_message: Sua senha não vai mudar até que você acessar o link acima e criar um novo. 11 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/devise/passwords.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | devise: 4 | passwords: 5 | sign_in_cta_html: &sign_in_cta_html 6 | "Deixa pra lá, voltar para a tela de login" 7 | new: 8 | welcome: Esqueceu sua senha? 9 | submit: Me enviar instruções para redefinir a senha 10 | sign_in_cta_html: *sign_in_cta_html 11 | 12 | edit: 13 | title: Cadastre uma nova senha 14 | sign_in_cta_html: *sign_in_cta_html 15 | form: 16 | submit_button: Altere minha senha 17 | password_confirmation_help: (%{length} caracteres no mínimo) 18 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/devise/registrations.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | devise: 4 | registrations: 5 | password_confirmation_help: &password_confirmation_help 6 | "(%{length} caracteres no mínimo)" 7 | new: 8 | welcome: Cadastre-se 9 | description: Crie uma conta para ver em ação. 10 | form: 11 | sign_up: Cadastre-se 12 | password_confirmation_help: *password_confirmation_help 13 | sign_in: Login 14 | sign_in_cta: Já tem uma conta? 15 | terms_and_policy: Concordo com os termos e política 16 | 17 | edit: 18 | form: 19 | email_confirmation: "Atualmente aguardando confirmação para: %{email}" 20 | current_password_help: (precisamos de sua senha atual para confirmar as alterações) 21 | password_confirmation: 22 | placeholder: *password_confirmation_help 23 | help: (deixe em branco se você não quer mudá-la) 24 | cancel_account: 25 | header: Cancele a conta 26 | question: Infeliz? Vamos ficar triste em te ver sair. 27 | button: Cancele minha conta 28 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/devise/sessions.yml: -------------------------------------------------------------------------------- 1 | # cta = call to action 2 | --- 3 | pt-BR: 4 | devise: 5 | sessions: 6 | new: 7 | welcome: Bem-vindo 8 | form: 9 | sign_in: Login 10 | forgot_password: Esqueceu a senha? 11 | account_cta: Cadastre-se 12 | account_question: Não possui uma conta? 13 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/errors/internal_server_error.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | errors: 4 | internal_server_error: 5 | status: 6 | code: 500 7 | message: Erro Interno do Servidor 8 | 9 | description_html: | 10 | O servidor encontrou algo inesperado que não permitirá concluir a requisição.
11 | Nós pedimos desculpas. 12 | 13 | homepage: Voltar para receitas 14 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/errors/not_found.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | errors: 4 | not_found: 5 | homepage: Receitas 6 | 7 | status: 8 | code: 404 9 | message: 10 | Desculpe, mas a página que você solicitou não foi encontrada. 11 | 12 | description: 13 | Você pode ter acessado um link expirado ou digitado incorretamente o endereço. 14 | 15 | form: 16 | placeholder: O que você deseja? 17 | submit: Buscar 18 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/home.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | home: 4 | index: 5 | titles: &recipes_search_filters 6 | my: Minhas Receitas 7 | latest: Receitas 8 | 9 | search: 10 | placeholder: Procure uma receita deliciosa! 11 | button: Busque 12 | 13 | recipes_search_filters: 14 | <<: *recipes_search_filters 15 | 16 | recipes: 17 | my_recipes_guest_html: | 18 | Olá, 19 | cadastre-se 20 | para publicar suas receitas! 21 | 22 | my_recipes_user_html: | 23 | Você ainda não publicou nenhuma receita.
24 | Que tal publicar 25 | uma agora? 26 | 27 | recipes_not_found: 28 | message_for_my_search_html: | 29 | Que tal 30 | pesquisar por isso 31 | em todas as receitas? 32 | 33 | message_for_latest_search_html: | 34 | Desculpe!
35 | A receita que você está procurando não foi encontrada.
36 | Ver todas as receitas! 37 | 38 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/layouts/footer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | layouts: 4 | footer: 5 | made_with_love_html: 6 | "feito com pela %{contact}" 7 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/layouts/navbar.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | layouts: 4 | navbar: 5 | locales: 6 | defaults: &call_to_action 7 | call_to_action_html: 8 | Trocar para inglês 9 | 10 | for_users: 11 | <<: *call_to_action 12 | for_guests: 13 | <<: *call_to_action 14 | -------------------------------------------------------------------------------- /config/locales/pt-BR/views/recipes.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pt-BR: 3 | recipes: 4 | new: 5 | welcome: Compartilhe sua receita conosco! 6 | edit: 7 | welcome: Torne sua receita ainda melhor! 8 | 9 | create: 10 | notice: Receita publicada com sucesso. 11 | 12 | update: 13 | notice: Receita atualizada com sucesso. 14 | 15 | destroy: 16 | notice: Receita removida com sucesso. 17 | 18 | form: 19 | submit: Publicar 20 | placeholders: 21 | title: Título da receita... 22 | story: Comente sobre essa receita... 23 | ingredients: '- 1 xícara de farinha de milho' 24 | instructions: '1. Forno pré-aquecido à 180 graus...' 25 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum, this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests, default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # The code in the `on_worker_boot` will be called if you are using 36 | # clustered mode by specifying a number of `workers`. After each worker 37 | # process is booted this block will be run, if you are using `preload_app!` 38 | # option you will want to use this block to reconnect to any threads 39 | # or connections that may have been created at application boot, Ruby 40 | # cannot share connections between processes. 41 | # 42 | # on_worker_boot do 43 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 44 | # end 45 | 46 | # Allow puma to be restarted by `rails restart` command. 47 | plugin :tmp_restart 48 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | devise_for :users, controllers: { 3 | sessions: 'users/sessions', 4 | passwords: 'users/passwords', 5 | registrations: 'users/registrations' 6 | } 7 | 8 | devise_scope :user do 9 | get '/users/sign_out', to: 'users/sessions#destroy' 10 | end 11 | 12 | get 'users/settings/change_locale/:locale', to: 'users/settings#change_locale', as: :change_locale 13 | 14 | match '/404', to: 'errors#not_found', via: :all 15 | match '/500', to: 'errors#internal_server_error', via: :all 16 | 17 | resources :recipes, except: :index 18 | 19 | get 'home/index', to: 'home#index', path: '/' 20 | 21 | get '/recipes', to: redirect('/') 22 | 23 | root to: 'home#index' 24 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 25 | end 26 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 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 `rails 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: 026fa3fca81c27f4102f786a90606498264ae78422b1a2512786de8abc29e018719a0fc6e13c169fa9a5de3787856222bb352a43ac211ecb44f1d99d876aac2a 15 | 16 | test: 17 | secret_key_base: 862f19aa31011ab11bfc25a28837cdc163f0495dec8c684ed00be788ec19f262e0c1b3619685844d3b93643cb4fe65d154894f04bd3c6ad43713d938f8ae17fb 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 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /db/migrate/20160806032155_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :users do |t| 4 | ## Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, default: 0, null: false 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.inet :current_sign_in_ip 20 | t.inet :last_sign_in_ip 21 | 22 | ## Confirmable 23 | # t.string :confirmation_token 24 | # t.datetime :confirmed_at 25 | # t.datetime :confirmation_sent_at 26 | # t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | t.timestamps null: false 34 | end 35 | 36 | add_index :users, :email, unique: true 37 | add_index :users, :reset_password_token, unique: true 38 | # add_index :users, :confirmation_token, unique: true 39 | # add_index :users, :unlock_token, unique: true 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /db/migrate/20160806033435_add_role_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddRoleToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :role, :integer, default: 0, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160806200056_add_name_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddNameToUser < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :name, :string, default: "", null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160827062724_add_authentication_token_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAuthenticationTokenToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :authentication_token, :string, limit: 30 4 | add_index :users, :authentication_token, unique: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20161111224106_create_recipes.rb: -------------------------------------------------------------------------------- 1 | class CreateRecipes < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :recipes do |t| 4 | t.string :title 5 | t.text :ingredients 6 | t.text :instructions 7 | t.references :user, foreign_key: true 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20161112004523_add_story_to_recipes.rb: -------------------------------------------------------------------------------- 1 | class AddStoryToRecipes < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :recipes, :story, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161115142009_create_friendly_id_slugs.rb: -------------------------------------------------------------------------------- 1 | class CreateFriendlyIdSlugs < ActiveRecord::Migration 2 | def change 3 | create_table :friendly_id_slugs do |t| 4 | t.string :slug, :null => false 5 | t.integer :sluggable_id, :null => false 6 | t.string :sluggable_type, :limit => 50 7 | t.string :scope 8 | t.datetime :created_at 9 | end 10 | add_index :friendly_id_slugs, :sluggable_id 11 | add_index :friendly_id_slugs, [:slug, :sluggable_type] 12 | add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], :unique => true 13 | add_index :friendly_id_slugs, :sluggable_type 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20161115142053_add_slug_to_recipes.rb: -------------------------------------------------------------------------------- 1 | class AddSlugToRecipes < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :recipes, :slug, :string 4 | add_index :recipes, :slug, unique: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20161115142053) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | 18 | create_table "friendly_id_slugs", force: :cascade do |t| 19 | t.string "slug", null: false 20 | t.integer "sluggable_id", null: false 21 | t.string "sluggable_type", limit: 50 22 | t.string "scope" 23 | t.datetime "created_at" 24 | t.index ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true, using: :btree 25 | t.index ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type", using: :btree 26 | t.index ["sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_id", using: :btree 27 | t.index ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type", using: :btree 28 | end 29 | 30 | create_table "recipes", force: :cascade do |t| 31 | t.string "title" 32 | t.text "ingredients" 33 | t.text "instructions" 34 | t.integer "user_id" 35 | t.datetime "created_at", null: false 36 | t.datetime "updated_at", null: false 37 | t.text "story" 38 | t.string "slug" 39 | t.index ["slug"], name: "index_recipes_on_slug", unique: true, using: :btree 40 | t.index ["user_id"], name: "index_recipes_on_user_id", using: :btree 41 | end 42 | 43 | create_table "users", force: :cascade do |t| 44 | t.string "email", default: "", null: false 45 | t.string "encrypted_password", default: "", null: false 46 | t.string "reset_password_token" 47 | t.datetime "reset_password_sent_at" 48 | t.datetime "remember_created_at" 49 | t.integer "sign_in_count", default: 0, null: false 50 | t.datetime "current_sign_in_at" 51 | t.datetime "last_sign_in_at" 52 | t.inet "current_sign_in_ip" 53 | t.inet "last_sign_in_ip" 54 | t.datetime "created_at", null: false 55 | t.datetime "updated_at", null: false 56 | t.integer "role", default: 0, null: false 57 | t.string "name", default: "", null: false 58 | t.string "authentication_token", limit: 30 59 | t.index ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree 60 | t.index ["email"], name: "index_users_on_email", unique: true, using: :btree 61 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree 62 | end 63 | 64 | add_foreign_key "recipes", "users" 65 | end 66 | -------------------------------------------------------------------------------- /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 rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | 9 | if Rails.env.development? || ENV['SEED_WITH_FAKE_RECIPES'].present? 10 | admin = User.admin.find_or_create_by(email: "admin@example.com", name: "Admin Example") do |user| 11 | password = "123456" 12 | 13 | user.password = password 14 | user.password_confirmation = password 15 | end 16 | 17 | if Recipe.count < 100 18 | total_with_story = rand(20..40) 19 | total_without_story = 100 - total_with_story 20 | 21 | users = [admin] 22 | 4.times { users << FactoryGirl.create(:user) } 23 | 24 | total_with_story.times do 25 | FactoryGirl.create(:recipe, user: users.sample) 26 | end 27 | 28 | total_without_story.times do 29 | FactoryGirl.create(:recipe_without_story, user: users.sample) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/lib/tasks/.keep -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/log/.keep -------------------------------------------------------------------------------- /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/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/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 | -------------------------------------------------------------------------------- /sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | APPLICATION_HOST=localhost 3 | APPLICATION_FORCE_SSL=false 4 | # HIDE_TERMS_AND_POLICY=true 5 | 6 | SMTP_PORT=587 7 | SMTP_ADDRESS=smtp.example.com 8 | SMTP_DOMAIN=example.com 9 | SMTP_PASSWORD=password 10 | SMTP_USERNAME=username 11 | 12 | SECRET_KEY_BASE=development_secret 13 | 14 | TURBOLINKS_IOS_APP_USER_AGENT=TurbolinksDemo 15 | TURBOLINKS_ANDROID_APP_USER_AGENT=TurbolinksDemo 16 | TURBOLINKS_NATIVE_MESSAGE_HANDLER=masterApp 17 | 18 | PAGINATION_DEFAULT_WINDOW=1 19 | PAGINATION_DEFAULT_PER_PAGE=10 20 | 21 | DEFAULT_RECIPE_IMAGE='https://cdn.pixabay.com/photo/2016/09/11/22/47/chefs-1662712_1280.jpg' 22 | -------------------------------------------------------------------------------- /spec/factories/recipes.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :recipe do 3 | user 4 | 5 | title { Faker::Hipster.sentence } 6 | 7 | story { Faker::Lorem.sentence } 8 | 9 | ingredients do 10 | number = rand(4..10) 11 | collection = Faker::Lorem.words(number).map do |sentence| 12 | number = rand(1..10) 13 | 14 | "- #{number} #{sentence}" 15 | end 16 | 17 | collection.join("\n") 18 | end 19 | 20 | instructions do 21 | number = rand(4..12) 22 | collection = [] 23 | Faker::Lorem.sentences(number).each_with_index do |sentence, index| 24 | collection << "#{index}. #{sentence}\n" 25 | end 26 | 27 | collection.join("\n") 28 | end 29 | end 30 | 31 | factory :recipe_without_story, parent: :recipe do 32 | story nil 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :user do 3 | role :user 4 | name { Faker::Name.name } 5 | email { Faker::Internet.email(name) } 6 | password "123456" 7 | password_confirmation { password } 8 | end 9 | 10 | factory :admin, parent: :user do 11 | role :admin 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/models/recipe_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Recipe, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe User, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/policies/user_policy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe UserPolicy do 4 | 5 | let(:user) { User.new } 6 | 7 | subject { described_class } 8 | 9 | permissions ".scope" do 10 | pending "add some examples to (or delete) #{__FILE__}" 11 | end 12 | 13 | permissions :show? do 14 | pending "add some examples to (or delete) #{__FILE__}" 15 | end 16 | 17 | permissions :create? do 18 | pending "add some examples to (or delete) #{__FILE__}" 19 | end 20 | 21 | permissions :update? do 22 | pending "add some examples to (or delete) #{__FILE__}" 23 | end 24 | 25 | permissions :destroy? do 26 | pending "add some examples to (or delete) #{__FILE__}" 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /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 'rspec/rails' 8 | # Add additional requires below this line. Rails is not loaded until this point! 9 | 10 | # Requires supporting ruby files with custom matchers and macros, etc, in 11 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 12 | # run as spec files by default. This means that files in spec/support that end 13 | # in _spec.rb will both be required and run as specs, causing the specs to be 14 | # run twice. It is recommended that you do not name files matching this glob to 15 | # end with _spec.rb. You can configure this pattern with the --pattern 16 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 17 | # 18 | # The following line is provided for convenience purposes. It has the downside 19 | # of increasing the boot-up time by auto-requiring all files in the support 20 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 21 | # require only the support files necessary. 22 | # 23 | # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 24 | 25 | # Checks for pending migration and applies them before tests are run. 26 | # If you are not using ActiveRecord, you can remove this line. 27 | ActiveRecord::Migration.maintain_test_schema! 28 | 29 | RSpec.configure do |config| 30 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 31 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 32 | 33 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 34 | # examples within a transaction, remove the following line or assign false 35 | # instead of true. 36 | config.use_transactional_fixtures = true 37 | 38 | # RSpec Rails can automatically mix in different behaviours to your tests 39 | # based on their file location, for example enabling you to call `get` and 40 | # `post` in specs under `spec/controllers`. 41 | # 42 | # You can disable this behaviour by removing the line below, and instead 43 | # explicitly tag your specs with their type, e.g.: 44 | # 45 | # RSpec.describe UsersController, :type => :controller do 46 | # # ... 47 | # end 48 | # 49 | # The different available types are documented in the features, such as in 50 | # https://relishapp.com/rspec/rspec-rails/docs 51 | config.infer_spec_type_from_file_location! 52 | 53 | # Filter lines from Rails gems in backtraces. 54 | config.filter_rails_from_backtrace! 55 | # arbitrary gems may also be filtered via: 56 | # config.filter_gems_from_backtrace("gem name") 57 | end 58 | -------------------------------------------------------------------------------- /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 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 44 | # have no way to turn it off -- the option exists only for backwards 45 | # compatibility in RSpec 3). It causes shared context metadata to be 46 | # inherited by the metadata hash of host groups and examples, rather than 47 | # triggering implicit auto-inclusion in groups with matching metadata. 48 | config.shared_context_metadata_behavior = :apply_to_host_groups 49 | 50 | # The settings below are suggested to provide a good initial experience 51 | # with RSpec, but feel free to customize to your heart's content. 52 | =begin 53 | # This allows you to limit a spec run to individual examples or groups 54 | # you care about by tagging them with `:focus` metadata. When nothing 55 | # is tagged with `:focus`, all examples get run. RSpec also provides 56 | # aliases for `it`, `describe`, and `context` that include `:focus` 57 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 58 | config.filter_run_when_matching :focus 59 | 60 | # Allows RSpec to persist some state between runs in order to support 61 | # the `--only-failures` and `--next-failure` CLI options. We recommend 62 | # you configure your source control system to ignore this file. 63 | config.example_status_persistence_file_path = "spec/examples.txt" 64 | 65 | # Limits the available syntax to the non-monkey patched syntax that is 66 | # recommended. For more details, see: 67 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 68 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 69 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 70 | config.disable_monkey_patching! 71 | 72 | # Many RSpec users commonly either run the entire suite or an individual 73 | # file, and it's useful to allow more verbose output when running an 74 | # individual spec file. 75 | if config.files_to_run.one? 76 | # Use the documentation formatter for detailed output, 77 | # unless a formatter has already been configured 78 | # (e.g. via a command-line flag). 79 | config.default_formatter = 'doc' 80 | end 81 | 82 | # Print the 10 slowest examples and example groups at the 83 | # end of the spec run, to help surface which specs are running 84 | # particularly slow. 85 | config.profile_examples = 10 86 | 87 | # Run specs in random order to surface order dependencies. If you find an 88 | # order dependency and want to debug it, you can fix the order by providing 89 | # the seed, which is printed after each run. 90 | # --seed 1234 91 | config.order = :random 92 | 93 | # Seed global randomization in this process using the `--seed` CLI option. 94 | # Setting this allows you to use `--seed` to deterministically reproduce 95 | # test failures related to randomization by passing the same `--seed` value 96 | # as the one that triggered the failure. 97 | Kernel.srand config.seed 98 | =end 99 | end 100 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/tmp/.keep -------------------------------------------------------------------------------- /vendor/assets/fonts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/fonts/.DS_Store -------------------------------------------------------------------------------- /vendor/assets/fonts/themify.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/fonts/themify.eot -------------------------------------------------------------------------------- /vendor/assets/fonts/themify.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/fonts/themify.ttf -------------------------------------------------------------------------------- /vendor/assets/fonts/themify.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/fonts/themify.woff -------------------------------------------------------------------------------- /vendor/assets/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/.DS_Store -------------------------------------------------------------------------------- /vendor/assets/images/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/apple-icon.png -------------------------------------------------------------------------------- /vendor/assets/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/background.jpg -------------------------------------------------------------------------------- /vendor/assets/images/faces/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/faces/.DS_Store -------------------------------------------------------------------------------- /vendor/assets/images/faces/face-0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/faces/face-0.jpg -------------------------------------------------------------------------------- /vendor/assets/images/faces/face-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/faces/face-1.jpg -------------------------------------------------------------------------------- /vendor/assets/images/faces/face-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/faces/face-2.jpg -------------------------------------------------------------------------------- /vendor/assets/images/faces/face-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/faces/face-3.jpg -------------------------------------------------------------------------------- /vendor/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/favicon.png -------------------------------------------------------------------------------- /vendor/assets/images/new_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/new_logo.png -------------------------------------------------------------------------------- /vendor/assets/images/tim_80x80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/images/tim_80x80.png -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/javascripts/paper-dashboard.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var navbar_initialized; 3 | 4 | $(document).on("turbolinks:load", function(){ 5 | navbar_initialized = false; 6 | 7 | var window_width = $(window).width(); 8 | 9 | // Init navigation toggle for small screens 10 | if(window_width <= 991){ 11 | lbd.initRightMenu(); 12 | } 13 | 14 | // Activate the tooltips 15 | $('[rel="tooltip"]').tooltip(); 16 | 17 | }); 18 | 19 | // activate collapse right menu when the windows is resized 20 | $(window).resize(function(){ 21 | if($(window).width() <= 991){ 22 | lbd.initRightMenu(); 23 | } 24 | }); 25 | 26 | var lbd = { 27 | initRightMenu: function(){ 28 | var $sidebar = $('.sidebar'); 29 | 30 | if($sidebar.length && !navbar_initialized){ 31 | var $off_canvas_sidebar = $('nav').find('.navbar-collapse').first().clone(true); 32 | 33 | var sidebar_bg_color = $sidebar.data('background-color'); 34 | var sidebar_active_color = $sidebar.data('active-color'); 35 | 36 | var $logo = $sidebar.find('.logo').first(); 37 | var logo_content = $logo[0].outerHTML; 38 | 39 | var ul_content = ''; 40 | 41 | // set the bg color and active color from the default sidebar to the off canvas sidebar; 42 | $off_canvas_sidebar.attr('data-background-color',sidebar_bg_color); 43 | $off_canvas_sidebar.attr('data-active-color',sidebar_active_color); 44 | 45 | $off_canvas_sidebar.addClass('off-canvas-sidebar'); 46 | 47 | //add the content from the regular header to the right menu 48 | $off_canvas_sidebar.children('ul').each(function(){ 49 | content_buff = $(this).html(); 50 | ul_content = ul_content + content_buff; 51 | }); 52 | 53 | // add the content from the sidebar to the right menu 54 | var content_buff = $sidebar.find('.nav').html(); 55 | var ul_content = ul_content + '
  • '+ content_buff; 56 | 57 | var ul_content = ''; 58 | 59 | var navbar_content = logo_content + ul_content; 60 | var navbar_content = ''; 61 | 62 | $off_canvas_sidebar.html(navbar_content); 63 | 64 | $('body').append($off_canvas_sidebar); 65 | 66 | var $toggle = $('.navbar-toggle'); 67 | 68 | $off_canvas_sidebar.find('a').removeClass('btn btn-round btn-default'); 69 | $off_canvas_sidebar.find('button').removeClass('btn-round btn-fill btn-info btn-primary btn-success btn-danger btn-warning btn-neutral'); 70 | $off_canvas_sidebar.find('button').addClass('btn-simple btn-block'); 71 | 72 | $toggle.click(function (){ 73 | var NAV_OPEN = 'nav-open', 74 | BODY_CLICK = 'bodyClick'; 75 | 76 | var $html = $('html'); 77 | 78 | var handleCloseSidebar = function() { 79 | $html.removeClass(NAV_OPEN); 80 | 81 | $('#' + BODY_CLICK).remove(); 82 | 83 | setTimeout(function(){ 84 | $toggle.removeClass('toggled'); 85 | }, 400); 86 | } 87 | 88 | if($html.hasClass(NAV_OPEN)) { 89 | handleCloseSidebar(); 90 | } else { 91 | setTimeout(function(){ 92 | $toggle.addClass('toggled'); 93 | }, 430); 94 | 95 | var BODY_CLICK_DIV = '
    '; 96 | 97 | $(BODY_CLICK_DIV).appendTo("body").click(handleCloseSidebar); 98 | 99 | $html.addClass(NAV_OPEN); 100 | } 101 | }); 102 | navbar_initialized = true; 103 | } 104 | } 105 | } 106 | })(); 107 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themasterapp/master-app-rails-server/95d3ba06134df577276f01468cd71e4120c61d6b/vendor/assets/stylesheets/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper-dashboard.scss: -------------------------------------------------------------------------------- 1 | @import "paper/variables"; 2 | @import "paper/mixins"; 3 | 4 | @import "paper/typography"; 5 | 6 | // Core CSS 7 | @import "paper/misc"; 8 | @import "paper/sidebar-and-main-panel"; 9 | @import "paper/buttons"; 10 | @import "paper/inputs"; 11 | 12 | @import "paper/alerts"; 13 | @import "paper/tables"; 14 | 15 | @import "paper/checkbox-radio"; 16 | @import "paper/navbars"; 17 | @import "paper/footers"; 18 | 19 | // Fancy Stuff 20 | 21 | @import "paper/dropdown"; 22 | @import "paper/cards"; 23 | @import "paper/chartist"; 24 | @import "paper/tabs-navs-pagination"; 25 | @import "paper/responsive"; 26 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_alerts.scss: -------------------------------------------------------------------------------- 1 | .alert{ 2 | border: 0; 3 | border-radius: 0; 4 | color: #FFFFFF; 5 | padding: 10px 15px; 6 | font-size: 14px; 7 | 8 | .container &{ 9 | border-radius: 4px; 10 | 11 | } 12 | .navbar &{ 13 | border-radius: 0; 14 | left: 0; 15 | position: absolute; 16 | right: 0; 17 | top: 85px; 18 | width: 100%; 19 | z-index: 3; 20 | } 21 | .navbar:not(.navbar-transparent) &{ 22 | top: 70px; 23 | } 24 | 25 | span[data-notify="icon"]{ 26 | font-size: 30px; 27 | display: block; 28 | left: 15px; 29 | position: absolute; 30 | top: 50%; 31 | margin-top: -20px; 32 | } 33 | 34 | .close ~ span{ 35 | display: block; 36 | max-width: 89%; 37 | } 38 | 39 | &[data-notify="container"]{ 40 | padding: 10px 10px 10px 20px; 41 | border-radius: $border-radius-base; 42 | } 43 | 44 | &.alert-with-icon{ 45 | padding-left: 65px; 46 | } 47 | } 48 | .alert-info{ 49 | background-color: $bg-info; 50 | color: $info-states-color; 51 | } 52 | .alert-success { 53 | background-color: $bg-success; 54 | color: $success-states-color; 55 | } 56 | .alert-warning { 57 | background-color: $bg-warning; 58 | color: $warning-states-color; 59 | } 60 | .alert-danger { 61 | background-color: $bg-danger; 62 | color: $danger-states-color; 63 | } 64 | 65 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_buttons.scss: -------------------------------------------------------------------------------- 1 | .btn, 2 | .navbar .navbar-nav > li > a.btn{ 3 | border-radius: $border-radius-btn-base; 4 | box-sizing: border-box; 5 | border-width: $border-thick; 6 | background-color: $transparent-bg; 7 | font-size: $font-size-base; 8 | font-weight: $font-weight-semi; 9 | 10 | padding: $padding-base-vertical $padding-base-horizontal; 11 | 12 | @include btn-styles($default-color, $default-states-color); 13 | @include transition($fast-transition-time, linear); 14 | 15 | &:hover, 16 | &:focus{ 17 | outline: 0 !important; 18 | } 19 | &:active, 20 | &.active, 21 | .open > &.dropdown-toggle { 22 | @include box-shadow(none); 23 | outline: 0 !important; 24 | } 25 | 26 | &.btn-icon{ 27 | padding: $padding-base-vertical; 28 | } 29 | } 30 | 31 | .btn-group .btn + .btn, 32 | .btn-group .btn + .btn-group, 33 | .btn-group .btn-group + .btn, 34 | .btn-group .btn-group + .btn-group{ 35 | margin-left: -2px; 36 | } 37 | 38 | // Apply the mixin to the buttons 39 | //.btn-default { @include btn-styles($default-color, $default-states-color); } 40 | .navbar .navbar-nav > li > a.btn-primary, .btn-primary { @include btn-styles($primary-color, $primary-states-color); } 41 | .navbar .navbar-nav > li > a.btn-success, .btn-success { @include btn-styles($success-color, $success-states-color); } 42 | .navbar .navbar-nav > li > a.btn-info, .btn-info { @include btn-styles($info-color, $info-states-color); } 43 | .navbar .navbar-nav > li > a.btn-warning, .btn-warning { @include btn-styles($warning-color, $warning-states-color); } 44 | .navbar .navbar-nav > li > a.btn-danger, .btn-danger { @include btn-styles($danger-color, $danger-states-color); } 45 | .btn-neutral { 46 | @include btn-styles($white-color, $white-color); 47 | 48 | &:hover, 49 | &:focus{ 50 | color: $default-color; 51 | } 52 | 53 | &:active, 54 | &.active, 55 | .open > &.dropdown-toggle{ 56 | background-color: $white-color; 57 | color: $default-color; 58 | } 59 | 60 | &.btn-fill{ 61 | color: $default-color; 62 | } 63 | &.btn-fill:hover, 64 | &.btn-fill:focus{ 65 | color: $default-states-color; 66 | } 67 | 68 | &.btn-simple:active, 69 | &.btn-simple.active{ 70 | background-color: transparent; 71 | } 72 | } 73 | 74 | .btn{ 75 | &:disabled, 76 | &[disabled], 77 | &.disabled{ 78 | @include opacity(.5); 79 | } 80 | } 81 | .btn-simple{ 82 | border: $none; 83 | padding: $padding-base-vertical $padding-base-horizontal; 84 | 85 | &.btn-icon{ 86 | padding: $padding-base-vertical; 87 | } 88 | } 89 | .btn-lg{ 90 | @include btn-size($padding-large-vertical, $padding-large-horizontal, $font-size-large, $border-radius-btn-large, $line-height-small); 91 | font-weight: $font-weight-normal; 92 | } 93 | .btn-sm{ 94 | @include btn-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $border-radius-btn-small, $line-height-small); 95 | } 96 | .btn-xs { 97 | @include btn-size($padding-xs-vertical, $padding-xs-horizontal, $font-size-xs, $border-radius-btn-small, $line-height-small); 98 | } 99 | .btn-wd { 100 | min-width: 140px; 101 | } 102 | 103 | .btn-group.select{ 104 | width: 100%; 105 | } 106 | .btn-group.select .btn{ 107 | text-align: left; 108 | } 109 | .btn-group.select .caret{ 110 | position: absolute; 111 | top: 50%; 112 | margin-top: -1px; 113 | right: 8px; 114 | } 115 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_checkbox-radio.scss: -------------------------------------------------------------------------------- 1 | /* Checkbox and radio */ 2 | .checkbox, 3 | .radio { 4 | margin-bottom: 12px; 5 | padding-left: 30px; 6 | position: relative; 7 | -webkit-transition: color,opacity 0.25s linear; 8 | transition: color,opacity 0.25s linear; 9 | font-size: $font-size-base; 10 | font-weight: normal; 11 | line-height: 1.5; 12 | color: $font-color; 13 | cursor: pointer; 14 | 15 | .icons { 16 | color: $font-color; 17 | display: block; 18 | height: 20px; 19 | left: 0; 20 | position: absolute; 21 | top: 0; 22 | width: 20px; 23 | text-align: center; 24 | line-height: 21px; 25 | font-size: 20px; 26 | cursor: pointer; 27 | -webkit-transition: color,opacity 0.15s linear; 28 | transition: color,opacity 0.15s linear; 29 | 30 | opacity: .50; 31 | } 32 | 33 | 34 | &.checked{ 35 | .icons{ 36 | opacity: 1; 37 | } 38 | } 39 | 40 | input{ 41 | outline: none !important; 42 | display: none; 43 | } 44 | } 45 | 46 | .checkbox, 47 | .radio{ 48 | label{ 49 | padding-left: 10px; 50 | } 51 | } 52 | 53 | .checkbox .icons .first-icon, 54 | .radio .icons .first-icon, 55 | .checkbox .icons .second-icon, 56 | .radio .icons .second-icon { 57 | display: inline-table; 58 | position: absolute; 59 | left: 0; 60 | top: 0; 61 | background-color: transparent; 62 | margin: 0; 63 | @include opacity(1); 64 | } 65 | .checkbox .icons .second-icon, 66 | .radio .icons .second-icon { 67 | @include opacity(0); 68 | } 69 | .checkbox:hover, 70 | .radio:hover { 71 | -webkit-transition: color 0.2s linear; 72 | transition: color 0.2s linear; 73 | } 74 | .checkbox:hover .first-icon, 75 | .radio:hover .first-icon { 76 | @include opacity(0); 77 | } 78 | .checkbox:hover .second-icon, 79 | .radio:hover .second-icon { 80 | @include opacity (1); 81 | } 82 | .checkbox.checked, 83 | .radio.checked { 84 | // color: $info-color; 85 | } 86 | .checkbox.checked .first-icon, 87 | .radio.checked .first-icon { 88 | opacity: 0; 89 | filter: alpha(opacity=0); 90 | } 91 | .checkbox.checked .second-icon, 92 | .radio.checked .second-icon { 93 | opacity: 1; 94 | filter: alpha(opacity=100); 95 | // color: $info-color; 96 | -webkit-transition: color 0.2s linear; 97 | transition: color 0.2s linear; 98 | } 99 | .checkbox.disabled, 100 | .radio.disabled { 101 | cursor: default; 102 | color: $medium-gray; 103 | } 104 | .checkbox.disabled .icons, 105 | .radio.disabled .icons { 106 | color: $medium-gray; 107 | } 108 | .checkbox.disabled .first-icon, 109 | .radio.disabled .first-icon { 110 | opacity: 1; 111 | filter: alpha(opacity=100); 112 | } 113 | .checkbox.disabled .second-icon, 114 | .radio.disabled .second-icon { 115 | opacity: 0; 116 | filter: alpha(opacity=0); 117 | } 118 | .checkbox.disabled.checked .icons, 119 | .radio.disabled.checked .icons { 120 | color: $medium-gray; 121 | } 122 | .checkbox.disabled.checked .first-icon, 123 | .radio.disabled.checked .first-icon { 124 | opacity: 0; 125 | filter: alpha(opacity=0); 126 | } 127 | .checkbox.disabled.checked .second-icon, 128 | .radio.disabled.checked .second-icon { 129 | opacity: 1; 130 | color: $medium-gray; 131 | filter: alpha(opacity=100); 132 | } 133 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_dropdown.scss: -------------------------------------------------------------------------------- 1 | .dropdown-menu{ 2 | background-color: $pale-bg; 3 | border: 0 none; 4 | border-radius: $border-radius-extreme; 5 | display: block; 6 | margin-top: 10px; 7 | padding: 0px; 8 | position: absolute; 9 | visibility: hidden; 10 | z-index: 9000; 11 | 12 | @include opacity(0); 13 | @include box-shadow($dropdown-shadow); 14 | 15 | // the style for opening dropdowns on mobile devices; for the desktop version check the _responsive.scss file 16 | .open &{ 17 | @include opacity(1); 18 | visibility: visible; 19 | } 20 | 21 | .divider{ 22 | background-color: $medium-pale-bg; 23 | margin: 0px; 24 | } 25 | 26 | .dropdown-header{ 27 | color: $dark-gray; 28 | font-size: $font-size-small; 29 | padding: $padding-dropdown-vertical $padding-dropdown-horizontal; 30 | } 31 | 32 | // the style for the dropdown menu that appears under select, it is different from the default one 33 | .select &{ 34 | border-radius: $border-radius-bottom; 35 | @include box-shadow(none); 36 | @include transform-origin($select-coordinates); 37 | @include transform-scale(1); 38 | @include transition($fast-transition-time, $transition-linear); 39 | margin-top: -20px; 40 | } 41 | .select.open &{ 42 | margin-top: -1px; 43 | } 44 | 45 | > li > a { 46 | color: $font-color; 47 | font-size: $font-size-base; 48 | padding: $padding-dropdown-vertical $padding-dropdown-horizontal; 49 | @include transition-none(); 50 | 51 | img{ 52 | margin-top: -3px; 53 | } 54 | } 55 | > li > a:focus{ 56 | outline: 0 !important; 57 | } 58 | 59 | .btn-group.select &{ 60 | min-width: 100%; 61 | } 62 | 63 | > li:first-child > a{ 64 | border-top-left-radius: $border-radius-extreme; 65 | border-top-right-radius: $border-radius-extreme; 66 | } 67 | 68 | > li:last-child > a{ 69 | border-bottom-left-radius: $border-radius-extreme; 70 | border-bottom-right-radius: $border-radius-extreme; 71 | } 72 | 73 | .select & > li:first-child > a{ 74 | border-radius: 0; 75 | border-bottom: 0 none; 76 | } 77 | 78 | > li > a:hover, 79 | > li > a:focus { 80 | background-color: $default-color; 81 | color: $fill-font-color; 82 | opacity: 1; 83 | text-decoration: none; 84 | } 85 | 86 | &.dropdown-primary > li > a:hover, 87 | &.dropdown-primary > li > a:focus{ 88 | background-color: $primary-color; 89 | } 90 | &.dropdown-info > li > a:hover, 91 | &.dropdown-info > li > a:focus{ 92 | background-color: $info-color; 93 | } 94 | &.dropdown-success > li > a:hover, 95 | &.dropdown-success > li > a:focus{ 96 | background-color: $success-color; 97 | } 98 | &.dropdown-warning > li > a:hover, 99 | &.dropdown-warning > li > a:focus{ 100 | background-color: $warning-color; 101 | } 102 | &.dropdown-danger > li > a:hover, 103 | &.dropdown-danger > li > a:focus{ 104 | background-color: $danger-color; 105 | } 106 | 107 | } 108 | 109 | //fix bug for the select items in btn-group 110 | .btn-group.select{ 111 | overflow: hidden; 112 | } 113 | .btn-group.select.open{ 114 | overflow: visible; 115 | } 116 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_footers.scss: -------------------------------------------------------------------------------- 1 | .footer{ 2 | background-attachment: fixed; 3 | position: relative; 4 | line-height: 20px; 5 | nav { 6 | ul { 7 | list-style: none; 8 | margin: 0; 9 | padding: 0; 10 | font-weight: normal; 11 | li{ 12 | display: inline-block; 13 | padding: 10px 15px; 14 | margin: 15px 3px; 15 | line-height: 20px; 16 | text-align: center; 17 | } 18 | a:not(.btn){ 19 | color: $font-color; 20 | display: block; 21 | margin-bottom: 3px; 22 | 23 | &:focus, 24 | &:hover{ 25 | color: $default-states-color; 26 | } 27 | } 28 | } 29 | } 30 | .copyright{ 31 | color: $font-color; 32 | padding: 10px 15px; 33 | font-size: 14px; 34 | white-space: nowrap; 35 | margin: 15px 3px; 36 | line-height: 20px; 37 | text-align: center; 38 | } 39 | .heart{ 40 | color: $danger-color; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_inputs.scss: -------------------------------------------------------------------------------- 1 | .form-control::-moz-placeholder{ 2 | @include placeholder($medium-gray,1); 3 | } 4 | .form-control:-moz-placeholder{ 5 | @include placeholder($medium-gray,1); 6 | } 7 | .form-control::-webkit-input-placeholder{ 8 | @include placeholder($medium-gray,1); 9 | } 10 | .form-control:-ms-input-placeholder{ 11 | @include placeholder($medium-gray,1); 12 | } 13 | 14 | .form-control { 15 | background-color: $gray-input-bg; 16 | border: medium none; 17 | border-radius: $border-radius-base; 18 | color: $font-color; 19 | font-size: $font-size-base; 20 | transition: background-color 0.3s ease 0s; 21 | @include input-size($padding-base-vertical, $padding-base-horizontal, $height-base); 22 | @include box-shadow(none); 23 | 24 | &:focus{ 25 | background-color: $white-bg; 26 | @include box-shadow(none); 27 | outline: 0 !important; 28 | } 29 | 30 | .has-success &, 31 | .has-error &, 32 | .has-success &:focus, 33 | .has-error &:focus{ 34 | @include box-shadow(none); 35 | } 36 | 37 | .has-success &{ 38 | background-color: $success-input-bg; 39 | color: $success-color; 40 | &.border-input{ 41 | border: 1px solid $success-color; 42 | } 43 | } 44 | .has-success &:focus{ 45 | background-color: $white-bg; 46 | } 47 | .has-error &{ 48 | background-color: $danger-input-bg; 49 | color: $danger-color; 50 | &.border-input{ 51 | border: 1px solid $danger-color; 52 | } 53 | } 54 | .has-error &:focus{ 55 | background-color: $white-bg; 56 | } 57 | 58 | & + .form-control-feedback{ 59 | border-radius: $border-radius-large; 60 | font-size: $font-size-base; 61 | margin-top: -7px; 62 | position: absolute; 63 | right: 10px; 64 | top: 50%; 65 | vertical-align: middle; 66 | } 67 | &.border-input{ 68 | border: 1px solid $table-line-color; 69 | } 70 | .open &{ 71 | border-bottom-color: transparent; 72 | } 73 | } 74 | 75 | .input-lg{ 76 | height: 55px; 77 | padding: $padding-large-vertical $padding-large-horizontal; 78 | } 79 | 80 | .has-error{ 81 | .form-control-feedback, .control-label{ 82 | color: $danger-color; 83 | } 84 | } 85 | .has-success{ 86 | .form-control-feedback, .control-label{ 87 | color: $success-color; 88 | } 89 | } 90 | 91 | 92 | .input-group-addon { 93 | background-color: $gray-input-bg; 94 | border: medium none; 95 | border-radius: $border-radius-base; 96 | 97 | 98 | .has-success &, 99 | .has-error &{ 100 | background-color: $white-color; 101 | } 102 | .has-error .form-control:focus + &{ 103 | color: $danger-color; 104 | } 105 | .has-success .form-control:focus + &{ 106 | color: $success-color; 107 | } 108 | .form-control:focus + &, 109 | .form-control:focus ~ &{ 110 | background-color: $white-color; 111 | } 112 | } 113 | .border-input{ 114 | .input-group-addon{ 115 | border: solid 1px $table-line-color; 116 | } 117 | } 118 | .input-group{ 119 | margin-bottom: 15px; 120 | } 121 | .input-group[disabled]{ 122 | .input-group-addon{ 123 | background-color: $light-gray; 124 | } 125 | } 126 | .input-group .form-control:first-child, 127 | .input-group-addon:first-child, 128 | .input-group-btn:first-child > .dropdown-toggle, 129 | .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { 130 | border-right: 0 none; 131 | } 132 | .input-group .form-control:last-child, 133 | .input-group-addon:last-child, 134 | .input-group-btn:last-child > .dropdown-toggle, 135 | .input-group-btn:first-child > .btn:not(:first-child) { 136 | border-left: 0 none; 137 | } 138 | .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { 139 | background-color: $light-gray; 140 | cursor: not-allowed; 141 | @include placeholder($dark-gray,1); 142 | } 143 | .form-control[disabled]::-moz-placeholder{ 144 | @include placeholder($dark-gray,1); 145 | } 146 | .form-control[disabled]:-moz-placeholder{ 147 | @include placeholder($medium-gray,1); 148 | } 149 | .form-control[disabled]::-webkit-input-placeholder{ 150 | @include placeholder($medium-gray,1); 151 | } 152 | .form-control[disabled]:-ms-input-placeholder{ 153 | @include placeholder($medium-gray,1); 154 | } 155 | .input-group-btn .btn{ 156 | border-width: $border-thin; 157 | padding: $padding-round-vertical $padding-base-horizontal; 158 | } 159 | .input-group-btn .btn-default:not(.btn-fill){ 160 | border-color: $medium-gray; 161 | } 162 | 163 | .input-group-btn:last-child > .btn{ 164 | margin-left: 0; 165 | } 166 | textarea.form-control{ 167 | max-width: 100%; 168 | padding: 10px 18px; 169 | resize: none; 170 | } 171 | 172 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_misc.scss: -------------------------------------------------------------------------------- 1 | /* General overwrite */ 2 | body{ 3 | color: $font-color; 4 | font-size: $font-size-base; 5 | font-family: 'Muli', Arial, sans-serif; 6 | .wrapper{ 7 | min-height: 100vh; 8 | position: relative; 9 | } 10 | } 11 | a{ 12 | color: $info-color; 13 | 14 | &:hover, &:focus{ 15 | color: $info-states-color; 16 | text-decoration: none; 17 | } 18 | } 19 | 20 | a:focus, a:active, 21 | button::-moz-focus-inner, 22 | input::-moz-focus-inner, 23 | select::-moz-focus-inner, 24 | input[type="file"] > input[type="button"]::-moz-focus-inner{ 25 | outline:0 !important; 26 | } 27 | .ui-slider-handle:focus, 28 | .navbar-toggle, 29 | input:focus, 30 | button:focus { 31 | outline : 0 !important; 32 | } 33 | 34 | /* Animations */ 35 | .form-control, 36 | .input-group-addon, 37 | .tagsinput, 38 | .navbar, 39 | .navbar .alert{ 40 | @include transition($general-transition-time, $transition-linear); 41 | } 42 | 43 | .sidebar .nav a, 44 | .table > tbody > tr .td-actions .btn{ 45 | @include transition($fast-transition-time, $transition-ease-in); 46 | } 47 | 48 | .btn{ 49 | @include transition($ultra-fast-transition-time, $transition-ease-in); 50 | } 51 | .fa{ 52 | width: 21px; 53 | text-align: center; 54 | } 55 | .fa-base{ 56 | font-size: 1.25em !important; 57 | } 58 | 59 | .margin-top{ 60 | margin-top: 50px; 61 | } 62 | hr{ 63 | border-color: $medium-pale-bg; 64 | } 65 | .wrapper{ 66 | position: relative; 67 | top: 0; 68 | height: 100vh; 69 | } 70 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_mixins.scss: -------------------------------------------------------------------------------- 1 | //Utilities 2 | 3 | @import "mixins/transparency"; 4 | @import "mixins/vendor-prefixes"; 5 | 6 | 7 | //Components 8 | 9 | @import "mixins/buttons"; 10 | @import "mixins/inputs"; 11 | @import "mixins/labels"; 12 | @import "mixins/tabs"; 13 | @import "mixins/navbars"; 14 | @import "mixins/icons"; 15 | @import "mixins/cards"; 16 | @import "mixins/chartist"; 17 | @import "mixins/sidebar"; -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_navbars.scss: -------------------------------------------------------------------------------- 1 | .nav { 2 | > li{ 3 | > a:hover, 4 | > a:focus{ 5 | background-color: transparent; 6 | } 7 | } 8 | } 9 | .navbar{ 10 | border: $none; 11 | border-radius: 0; 12 | font-size: $font-size-navbar; 13 | z-index: 3; 14 | 15 | .navbar-brand{ 16 | font-weight: $font-weight-bold; 17 | margin: $navbar-margin-brand; 18 | padding: $navbar-padding-brand; 19 | font-size: $font-size-large-navbar; 20 | } 21 | .navbar-nav{ 22 | > li > a { 23 | line-height: 1.42857; 24 | margin: $navbar-margin-a; 25 | padding: $navbar-padding-a; 26 | 27 | i, 28 | p{ 29 | display: inline-block; 30 | margin: 0; 31 | } 32 | i{ 33 | position: relative; 34 | top: 1px; 35 | } 36 | } 37 | > li > a.btn{ 38 | margin: $navbar-margin-a-btn; 39 | padding: $padding-base-vertical $padding-base-horizontal; 40 | } 41 | } 42 | .btn{ 43 | margin: $navbar-margin-btn; 44 | font-size: $font-size-base; 45 | } 46 | .btn-simple{ 47 | font-size: $font-size-medium; 48 | } 49 | } 50 | 51 | .navbar-nav > li > .dropdown-menu{ 52 | border-radius: $border-radius-extreme; 53 | margin-top: -5px; 54 | } 55 | 56 | .navbar-default { 57 | background-color: $bg-nude; 58 | border-bottom: 1px solid $medium-gray; 59 | 60 | .brand{ 61 | color: $font-color !important; 62 | } 63 | .navbar-nav{ 64 | > li > a:not(.btn){ 65 | color: $dark-gray; 66 | } 67 | 68 | > .active > a, 69 | > .active > a:not(.btn):hover, 70 | > .active > a:not(.btn):focus, 71 | > li > a:not(.btn):hover, 72 | > li > a:not(.btn):focus { 73 | background-color: transparent; 74 | border-radius: 3px; 75 | color: $info-color; 76 | @include opacity(1); 77 | } 78 | 79 | > .dropdown > a:hover .caret, 80 | > .dropdown > a:focus .caret { 81 | border-bottom-color: $info-color; 82 | border-top-color: $info-color; 83 | 84 | } 85 | 86 | > .open > a, 87 | > .open > a:hover, 88 | > .open > a:focus{ 89 | background-color: transparent; 90 | color: $info-color; 91 | } 92 | 93 | .navbar-toggle:hover,.navbar-toggle:focus { 94 | background-color: transparent; 95 | } 96 | 97 | } 98 | 99 | &:not(.navbar-transparent) .btn-default:hover{ 100 | color: $info-color; 101 | border-color: $info-color; 102 | } 103 | &:not(.navbar-transparent) .btn-neutral, 104 | &:not(.navbar-transparent) .btn-neutral:hover, 105 | &:not(.navbar-transparent) .btn-neutral:active{ 106 | color: $dark-gray; 107 | } 108 | } 109 | 110 | .navbar-form{ 111 | @include box-shadow(none); 112 | .form-control{ 113 | @include light-form(); 114 | height: 22px; 115 | font-size: $font-size-navbar; 116 | line-height: $line-height-general; 117 | color: $light-gray; 118 | } 119 | .navbar-transparent & .form-control, 120 | [class*="navbar-ct"] & .form-control{ 121 | color: $white-color; 122 | border: $none; 123 | border-bottom: 1px solid rgba($white-color,.6); 124 | } 125 | 126 | } 127 | 128 | .navbar-ct-primary{ 129 | @include navbar-color($bg-primary); 130 | } 131 | .navbar-ct-info{ 132 | @include navbar-color($bg-info); 133 | } 134 | .navbar-ct-success{ 135 | @include navbar-color($bg-success); 136 | } 137 | .navbar-ct-warning{ 138 | @include navbar-color($bg-warning); 139 | } 140 | .navbar-ct-danger{ 141 | @include navbar-color($bg-danger); 142 | } 143 | 144 | .navbar-transparent{ 145 | padding-top: 15px; 146 | background-color: transparent; 147 | border-bottom: 1px solid transparent; 148 | } 149 | 150 | .navbar-toggle{ 151 | margin-top: 19px; 152 | margin-bottom: 19px; 153 | border: $none; 154 | 155 | .icon-bar { 156 | background-color: $white-color; 157 | } 158 | .navbar-collapse, 159 | .navbar-form { 160 | border-color: transparent; 161 | } 162 | 163 | &.navbar-default .navbar-toggle:hover, 164 | &.navbar-default .navbar-toggle:focus { 165 | background-color: transparent; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_sidebar-and-main-panel.scss: -------------------------------------------------------------------------------- 1 | .sidebar{ 2 | position: absolute; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | z-index: 1; 7 | background-size: cover; 8 | background-position: center center; 9 | .sidebar-wrapper{ 10 | position: relative; 11 | max-height: none; 12 | min-height: 100%; 13 | overflow: hidden; 14 | width: 260px; 15 | z-index: 4; 16 | box-shadow: inset -1px 0px 0px 0px $medium-gray; 17 | } 18 | .sidebar-background{ 19 | position: absolute; 20 | z-index: 1; 21 | height: 100%; 22 | width: 100%; 23 | display: block; 24 | top: 0; 25 | left: 0; 26 | background-size: cover; 27 | background-position: center center; 28 | } 29 | 30 | } 31 | .sidebar, 32 | .off-canvas-sidebar{ 33 | width: 260px; 34 | display: block; 35 | font-weight: 200; 36 | 37 | .logo{ 38 | padding: 18px 0px; 39 | margin: 0 20px; 40 | 41 | p{ 42 | float: left; 43 | font-size: 20px; 44 | margin: 10px 10px; 45 | line-height: 20px; 46 | } 47 | 48 | .simple-text{ 49 | text-transform: uppercase; 50 | padding: $padding-small-vertical $padding-zero; 51 | display: block; 52 | font-size: $font-size-large; 53 | text-align: center; 54 | font-weight: $font-weight-normal; 55 | line-height: 30px; 56 | } 57 | } 58 | 59 | .nav{ 60 | margin-top: 20px; 61 | 62 | li{ 63 | > a{ 64 | margin: 10px 0px; 65 | padding-left: 25px; 66 | padding-right: 25px; 67 | 68 | opacity: .7; 69 | } 70 | 71 | &:hover > a{ 72 | opacity: 1; 73 | } 74 | 75 | &.active > a{ 76 | color: $primary-color; 77 | opacity: 1; 78 | 79 | &:before{ 80 | border-right: 17px solid $medium-gray; 81 | border-top: 17px solid transparent; 82 | border-bottom: 17px solid transparent; 83 | content: ""; 84 | display: inline-block; 85 | position: absolute; 86 | right: 0; 87 | top: 8px; 88 | } 89 | 90 | &:after{ 91 | border-right: 17px solid $bg-nude; 92 | border-top: 17px solid transparent; 93 | border-bottom: 17px solid transparent; 94 | content: ""; 95 | display: inline-block; 96 | position: absolute; 97 | right: -1px; 98 | top: 8px; 99 | } 100 | } 101 | } 102 | 103 | p{ 104 | margin: 0; 105 | line-height: 30px; 106 | font-size: 12px; 107 | font-weight: 600; 108 | text-transform: uppercase; 109 | } 110 | 111 | i{ 112 | font-size: 24px; 113 | float: left; 114 | margin-right: 15px; 115 | line-height: 30px; 116 | width: 30px; 117 | text-align: center; 118 | } 119 | } 120 | 121 | &:after, 122 | &:before{ 123 | display: block; 124 | content: ""; 125 | position: absolute; 126 | width: 100%; 127 | height: 100%; 128 | top: 0; 129 | left: 0; 130 | z-index: 2; 131 | background: $white-background-color; 132 | } 133 | 134 | &, 135 | &[data-background-color="white"]{ 136 | @include sidebar-background-color($white-background-color, $default-color); 137 | } 138 | &[data-background-color="black"]{ 139 | @include sidebar-background-color($black-background-color, $white-color); 140 | } 141 | 142 | &[data-active-color="primary"]{ 143 | @include sidebar-active-color($primary-color); 144 | } 145 | &[data-active-color="info"]{ 146 | @include sidebar-active-color($info-color); 147 | } 148 | &[data-active-color="success"]{ 149 | @include sidebar-active-color($success-color); 150 | } 151 | &[data-active-color="warning"]{ 152 | @include sidebar-active-color($warning-color); 153 | } 154 | &[data-active-color="danger"]{ 155 | @include sidebar-active-color($danger-color); 156 | } 157 | 158 | } 159 | 160 | .main-panel{ 161 | background-color: $bg-nude; 162 | position: relative; 163 | z-index: 2; 164 | float: right; 165 | width: $sidebar-width; 166 | min-height: 100%; 167 | 168 | > .content{ 169 | padding: 30px 15px; 170 | min-height: calc(100% - 123px); 171 | } 172 | 173 | > .footer{ 174 | border-top: 1px solid rgba(0, 0, 0, 0.1); 175 | } 176 | 177 | .navbar{ 178 | margin-bottom: 0; 179 | } 180 | } 181 | 182 | .sidebar, 183 | .main-panel{ 184 | overflow: auto; 185 | max-height: 100%; 186 | height: 100%; 187 | -webkit-transition-property: top,bottom; 188 | transition-property: top,bottom; 189 | -webkit-transition-duration: .2s,.2s; 190 | transition-duration: .2s,.2s; 191 | -webkit-transition-timing-function: linear,linear; 192 | transition-timing-function: linear,linear; 193 | -webkit-overflow-scrolling: touch; 194 | } 195 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_tables.scss: -------------------------------------------------------------------------------- 1 | .table{ 2 | thead, 3 | tbody, 4 | tfoot{ 5 | tr > th, 6 | tr > td{ 7 | border-top: 1px solid $table-line-color; 8 | } 9 | } 10 | > thead > tr > th{ 11 | border-bottom-width: 0; 12 | font-size: $font-size-h5; 13 | font-weight: $font-weight-light; 14 | } 15 | 16 | .radio, 17 | .checkbox{ 18 | margin-top: 0; 19 | margin-bottom: 22px; 20 | padding: 0; 21 | width: 15px; 22 | } 23 | > thead > tr > th, 24 | > tbody > tr > th, 25 | > tfoot > tr > th, 26 | > thead > tr > td, 27 | > tbody > tr > td, 28 | > tfoot > tr > td{ 29 | padding: 12px; 30 | vertical-align: middle; 31 | } 32 | 33 | .th-description{ 34 | max-width: 150px; 35 | } 36 | .td-price{ 37 | font-size: 26px; 38 | font-weight: $font-weight-light; 39 | margin-top: 5px; 40 | text-align: right; 41 | } 42 | .td-total{ 43 | font-weight: $font-weight-bold; 44 | font-size: $font-size-h5; 45 | padding-top: 20px; 46 | text-align: right; 47 | } 48 | 49 | .td-actions .btn{ 50 | 51 | &.btn-sm, 52 | &.btn-xs{ 53 | padding-left: 3px; 54 | padding-right: 3px; 55 | } 56 | } 57 | 58 | > tbody > tr{ 59 | position: relative; 60 | } 61 | } 62 | .table-striped{ 63 | tbody > tr:nth-of-type(2n+1) { 64 | background-color: #fff; 65 | } 66 | tbody > tr:nth-of-type(2n) { 67 | background-color: $pale-bg; 68 | } 69 | > thead > tr > th, 70 | > tbody > tr > th, 71 | > tfoot > tr > th, 72 | > thead > tr > td, 73 | > tbody > tr > td, 74 | > tfoot > tr > td{ 75 | padding: 15px 8px; 76 | } 77 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/_typography.scss: -------------------------------------------------------------------------------- 1 | h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6, p, .navbar, .brand, a, .td-name, td{ 2 | -moz-osx-font-smoothing: grayscale; 3 | -webkit-font-smoothing: antialiased; 4 | font-family: 'Muli', "Helvetica", Arial, sans-serif; 5 | } 6 | 7 | h1, .h1, h2, .h2, h3, .h3, h4, .h4{ 8 | font-weight: $font-weight-normal; 9 | margin: $margin-large-vertical 0 $margin-base-vertical; 10 | } 11 | 12 | h1, .h1 { 13 | font-size: $font-size-h1; 14 | } 15 | h2, .h2{ 16 | font-size: $font-size-h2; 17 | } 18 | h3, .h3{ 19 | font-size: $font-size-h3; 20 | line-height: 1.4; 21 | margin: 20px 0 10px; 22 | } 23 | h4, .h4{ 24 | font-size: $font-size-h4; 25 | font-weight: $font-weight-bold; 26 | line-height: 1.2em; 27 | } 28 | h5, .h5 { 29 | font-size: $font-size-h5; 30 | font-weight: $font-weight-normal; 31 | line-height: 1.4em; 32 | margin-bottom: 15px; 33 | } 34 | h6, .h6{ 35 | font-size: $font-size-h6; 36 | font-weight: $font-weight-bold; 37 | text-transform: uppercase; 38 | } 39 | p{ 40 | font-size: $font-paragraph; 41 | line-height: $line-height-general; 42 | } 43 | 44 | h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { 45 | color: $dark-gray; 46 | font-weight: $font-weight-light; 47 | line-height: $line-height-general; 48 | } 49 | 50 | h1 small, h2 small, h3 small, h1 .small, h2 .small, h3 .small { 51 | font-size: 60%; 52 | } 53 | .title-uppercase{ 54 | text-transform: uppercase; 55 | } 56 | blockquote{ 57 | font-style: italic; 58 | } 59 | blockquote small{ 60 | font-style: normal; 61 | } 62 | .text-muted{ 63 | color: $medium-gray; 64 | } 65 | .text-primary, .text-primary:hover{ 66 | color: $primary-states-color; 67 | } 68 | .text-info, .text-info:hover{ 69 | color: $info-states-color; 70 | } 71 | .text-success, .text-success:hover{ 72 | color: $success-states-color; 73 | } 74 | .text-warning, .text-warning:hover{ 75 | color: $warning-states-color; 76 | } 77 | .text-danger, .text-danger:hover{ 78 | color: $danger-states-color; 79 | } 80 | .glyphicon{ 81 | line-height: 1; 82 | } 83 | strong{ 84 | color: $default-states-color; 85 | } 86 | .icon-primary{ 87 | color: $primary-color; 88 | } 89 | .icon-info{ 90 | color: $info-color; 91 | } 92 | .icon-success{ 93 | color: $success-color; 94 | } 95 | .icon-warning{ 96 | color: $warning-color; 97 | } 98 | .icon-danger{ 99 | color: $danger-color; 100 | } 101 | .chart-legend{ 102 | .text-primary, .text-primary:hover{ 103 | color: $primary-color; 104 | } 105 | .text-info, .text-info:hover{ 106 | color: $info-color; 107 | } 108 | .text-success, .text-success:hover{ 109 | color: $success-color; 110 | } 111 | .text-warning, .text-warning:hover{ 112 | color: $warning-color; 113 | } 114 | .text-danger, .text-danger:hover{ 115 | color: $danger-color; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_buttons.scss: -------------------------------------------------------------------------------- 1 | // Mixin for generating new styles 2 | @mixin btn-styles($btn-color, $btn-states-color) { 3 | border-color: $btn-color; 4 | color: $btn-color; 5 | 6 | &:hover, 7 | &:focus, 8 | &:active, 9 | &.active, 10 | .open > &.dropdown-toggle { 11 | background-color: $btn-color; 12 | color: $fill-font-color; 13 | border-color: $btn-color; 14 | .caret{ 15 | border-top-color: $fill-font-color; 16 | } 17 | } 18 | 19 | &.disabled, 20 | &:disabled, 21 | &[disabled], 22 | fieldset[disabled] & { 23 | &, 24 | &:hover, 25 | &:focus, 26 | &.focus, 27 | &:active, 28 | &.active { 29 | background-color: $transparent-bg; 30 | border-color: $btn-color; 31 | } 32 | } 33 | 34 | 35 | &.btn-fill { 36 | color: $white-color; 37 | background-color: $btn-color; 38 | @include opacity(1); 39 | 40 | &:hover, 41 | &:focus, 42 | &:active, 43 | &.active, 44 | .open > &.dropdown-toggle{ 45 | background-color: $btn-states-color; 46 | color: $white-color; 47 | border-color: $btn-states-color; 48 | } 49 | 50 | .caret{ 51 | border-top-color: $white-color; 52 | } 53 | } 54 | 55 | &.btn-simple { 56 | &:hover, 57 | &:focus, 58 | &:active, 59 | &.active, 60 | .open > &.dropdown-toggle{ 61 | background-color: $transparent-bg; 62 | color: $btn-states-color; 63 | } 64 | 65 | .caret{ 66 | border-top-color: $white-color; 67 | } 68 | } 69 | 70 | .caret{ 71 | border-top-color: $btn-color; 72 | } 73 | } 74 | 75 | 76 | @mixin btn-size($padding-vertical, $padding-horizontal, $font-size, $border, $line-height){ 77 | font-size: $font-size; 78 | border-radius: $border; 79 | padding: $padding-vertical $padding-horizontal; 80 | 81 | &.btn-simple{ 82 | padding: $padding-vertical + 2 $padding-horizontal; 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_cards.scss: -------------------------------------------------------------------------------- 1 | @mixin filter($color){ 2 | @if $color == #FFFFFF{ 3 | background-color: rgba($color,.91); 4 | } @else { 5 | background-color: rgba($color,.69); 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_chartist.scss: -------------------------------------------------------------------------------- 1 | // Scales for responsive SVG containers 2 | $ct-scales: ((1), (15/16), (8/9), (5/6), (4/5), (3/4), (2/3), (5/8), (1/1.618), (3/5), (9/16), (8/15), (1/2), (2/5), (3/8), (1/3), (1/4)) !default; 3 | $ct-scales-names: (ct-square, ct-minor-second, ct-major-second, ct-minor-third, ct-major-third, ct-perfect-fourth, ct-perfect-fifth, ct-minor-sixth, ct-golden-section, ct-major-sixth, ct-minor-seventh, ct-major-seventh, ct-octave, ct-major-tenth, ct-major-eleventh, ct-major-twelfth, ct-double-octave) !default; 4 | 5 | // Class names to be used when generating CSS 6 | $ct-class-chart: ct-chart !default; 7 | $ct-class-chart-line: ct-chart-line !default; 8 | $ct-class-chart-bar: ct-chart-bar !default; 9 | $ct-class-horizontal-bars: ct-horizontal-bars !default; 10 | $ct-class-chart-pie: ct-chart-pie !default; 11 | $ct-class-chart-donut: ct-chart-donut !default; 12 | $ct-class-label: ct-label !default; 13 | $ct-class-series: ct-series !default; 14 | $ct-class-line: ct-line !default; 15 | $ct-class-point: ct-point !default; 16 | $ct-class-area: ct-area !default; 17 | $ct-class-bar: ct-bar !default; 18 | $ct-class-slice-pie: ct-slice-pie !default; 19 | $ct-class-slice-donut: ct-slice-donut !default; 20 | $ct-class-grid: ct-grid !default; 21 | $ct-class-vertical: ct-vertical !default; 22 | $ct-class-horizontal: ct-horizontal !default; 23 | $ct-class-start: ct-start !default; 24 | $ct-class-end: ct-end !default; 25 | 26 | // Container ratio 27 | $ct-container-ratio: (1/1.618) !default; 28 | 29 | // Text styles for labels 30 | $ct-text-color: rgba(0, 0, 0, 0.4) !default; 31 | $ct-text-size: 0.9em !default; 32 | $ct-text-align: flex-start !default; 33 | $ct-text-justify: flex-start !default; 34 | $ct-text-line-height: 1; 35 | 36 | // Grid styles 37 | $ct-grid-color: rgba(0, 0, 0, 0.2) !default; 38 | $ct-grid-dasharray: 2px !default; 39 | $ct-grid-width: 1px !default; 40 | 41 | // Line chart properties 42 | $ct-line-width: 4px !default; 43 | $ct-line-dasharray: false !default; 44 | $ct-point-size: 10px !default; 45 | // Line chart point, can be either round or square 46 | $ct-point-shape: round !default; 47 | // Area fill transparency between 0 and 1 48 | $ct-area-opacity: 0.7 !default; 49 | 50 | // Bar chart bar width 51 | $ct-bar-width: 10px !default; 52 | 53 | // Donut width (If donut width is to big it can cause issues where the shape gets distorted) 54 | $ct-donut-width: 60px !default; 55 | 56 | // If set to true it will include the default classes and generate CSS output. If you're planning to use the mixins you 57 | // should set this property to false 58 | $ct-include-classes: true !default; 59 | 60 | // If this is set to true the CSS will contain colored series. You can extend or change the color with the 61 | // properties below 62 | $ct-include-colored-series: $ct-include-classes !default; 63 | 64 | // If set to true this will include all responsive container variations using the scales defined at the top of the script 65 | $ct-include-alternative-responsive-containers: $ct-include-classes !default; 66 | 67 | // Series names and colors. This can be extended or customized as desired. Just add more series and colors. 68 | $ct-series-names: (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) !default; 69 | $ct-series-colors: ( 70 | $info-color, 71 | $warning-color, 72 | $danger-color, 73 | $success-color, 74 | $primary-color, 75 | rgba($info-color,.8), 76 | rgba($success-color,.8), 77 | rgba($warning-color,.8), 78 | rgba($danger-color,.8), 79 | rgba($primary-color,.8), 80 | rgba($info-color,.6), 81 | rgba($success-color,.6), 82 | rgba($warning-color,.6), 83 | rgba($danger-color,.6), 84 | rgba($primary-color,.6) 85 | 86 | ) !default; 87 | 88 | // Paper Kit Colors 89 | 90 | .ct-blue{ 91 | stroke: $primary-color !important; 92 | } 93 | .ct-azure{ 94 | stroke: $info-color !important; 95 | } 96 | .ct-green{ 97 | stroke: $success-color !important; 98 | } 99 | .ct-orange{ 100 | stroke: $warning-color !important; 101 | } 102 | .ct-red{ 103 | stroke: $danger-color !important; 104 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_icons.scss: -------------------------------------------------------------------------------- 1 | @mixin icon-background ($icon-url){ 2 | background-image : url($icon-url); 3 | 4 | } 5 | 6 | @mixin icon-shape ($size, $padding, $border-radius) { 7 | height: $size; 8 | width: $size; 9 | padding: $padding; 10 | border-radius: $border-radius; 11 | display: inline-table; 12 | 13 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_inputs.scss: -------------------------------------------------------------------------------- 1 | @mixin input-size($padding-vertical, $padding-horizontal, $height){ 2 | padding: $padding-vertical $padding-horizontal; 3 | height: $height; 4 | } 5 | 6 | @mixin placeholder($color, $opacity){ 7 | color: $color; 8 | @include opacity(1); 9 | } 10 | 11 | @mixin light-form(){ 12 | border-radius: 0; 13 | border:0; 14 | padding: 0; 15 | background-color: transparent; 16 | 17 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_labels.scss: -------------------------------------------------------------------------------- 1 | @mixin label-style(){ 2 | padding: $padding-label-vertical $padding-label-horizontal; 3 | border: 1px solid $default-color; 4 | border-radius: $border-radius-small; 5 | color: $default-color; 6 | font-weight: $font-weight-semi; 7 | font-size: $font-size-small; 8 | text-transform: uppercase; 9 | display: inline-block; 10 | vertical-align: middle; 11 | } 12 | 13 | @mixin label-color($color){ 14 | border-color: $color; 15 | color: $color; 16 | } 17 | @mixin label-color-fill($color){ 18 | border-color: $color; 19 | color: $white-color; 20 | background-color: $color; 21 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_navbars.scss: -------------------------------------------------------------------------------- 1 | @mixin navbar-color($color){ 2 | background-color: $color; 3 | } 4 | 5 | @mixin center-item(){ 6 | left: 0; 7 | right: 0; 8 | margin-right: auto; 9 | margin-left: auto; 10 | position: absolute; 11 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_sidebar.scss: -------------------------------------------------------------------------------- 1 | @mixin sidebar-background-color($background-color, $font-color){ 2 | &:after, 3 | &:before{ 4 | background-color: $background-color; 5 | } 6 | 7 | .logo{ 8 | border-bottom: 1px solid rgba($font-color,.3); 9 | 10 | p{ 11 | color: $font-color; 12 | } 13 | 14 | .simple-text{ 15 | color: $font-color; 16 | } 17 | } 18 | 19 | .nav{ 20 | li:not(.active){ 21 | > a{ 22 | color: $font-color; 23 | } 24 | } 25 | .divider{ 26 | background-color: rgba($font-color,.2); 27 | } 28 | 29 | } 30 | 31 | } 32 | 33 | @mixin sidebar-active-color($font-color){ 34 | .nav{ 35 | li{ 36 | &.active > a{ 37 | color: $font-color; 38 | opacity: 1; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_tabs.scss: -------------------------------------------------------------------------------- 1 | @mixin pill-style($color){ 2 | border: 1px solid $color; 3 | color: $color; 4 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/paper/mixins/_transparency.scss: -------------------------------------------------------------------------------- 1 | // Opacity 2 | 3 | @mixin opacity($opacity) { 4 | opacity: $opacity; 5 | // IE8 filter 6 | $opacity-ie: ($opacity * 100); 7 | filter: #{alpha(opacity=$opacity-ie)}; 8 | } 9 | 10 | @mixin black-filter($opacity){ 11 | top: 0; 12 | left: 0; 13 | height: 100%; 14 | width: 100%; 15 | position: absolute; 16 | background-color: rgba(17,17,17,$opacity); 17 | display: block; 18 | content: ""; 19 | z-index: 1; 20 | } --------------------------------------------------------------------------------