├── log
└── .keep
├── tmp
└── .keep
├── lib
├── assets
│ └── .keep
└── tasks
│ └── .keep
├── public
├── favicon.ico
├── apple-touch-icon.png
├── apple-touch-icon-precomposed.png
├── robots.txt
└── 422.html
├── .ruby-version
├── app
├── assets
│ ├── images
│ │ └── .keep
│ ├── javascripts
│ │ ├── channels
│ │ │ └── .keep
│ │ ├── app
│ │ │ ├── init.js
│ │ │ ├── helpers
│ │ │ │ ├── pagination.js
│ │ │ │ ├── locales.js
│ │ │ │ ├── global_search.js
│ │ │ │ └── turbolinks
│ │ │ │ │ ├── auto_navbar_collapse.js
│ │ │ │ │ └── transitions.js
│ │ │ ├── initializers
│ │ │ │ └── bootstrap-checkbox-radio.js
│ │ │ └── lib
│ │ │ │ ├── turbolinks_native_message_handler.js
│ │ │ │ └── notifications.js
│ │ ├── ext
│ │ │ ├── jquery
│ │ │ │ ├── once.js
│ │ │ │ └── animateCSS.js
│ │ │ ├── turbolinks.coffee
│ │ │ └── rails.js
│ │ ├── cable.js
│ │ └── application.js
│ ├── config
│ │ └── manifest.js
│ └── stylesheets
│ │ ├── mixins.scss
│ │ ├── views
│ │ ├── devise.scss
│ │ ├── errors.scss
│ │ └── home.scss
│ │ ├── ext
│ │ └── paper-dashboard.scss
│ │ └── application.scss
├── models
│ ├── concerns
│ │ ├── .keep
│ │ └── user_token.rb
│ ├── application_record.rb
│ ├── recipe.rb
│ ├── user.rb
│ └── home
│ │ └── params.rb
├── controllers
│ ├── concerns
│ │ ├── .keep
│ │ ├── pundit_helpers.rb
│ │ ├── devise_permitted_parameters.rb
│ │ ├── token_authentication.rb
│ │ ├── turbolinks_helpers.rb
│ │ ├── meta_tags_helpers.rb
│ │ └── locale_manager.rb
│ ├── users
│ │ ├── passwords_controller.rb
│ │ ├── registrations_controller.rb
│ │ ├── settings_controller.rb
│ │ └── sessions_controller.rb
│ ├── errors_controller.rb
│ ├── application_controller.rb
│ ├── recipes_controller.rb
│ └── home_controller.rb
├── views
│ ├── layouts
│ │ ├── mailer.text.erb
│ │ ├── _flash_messages.html.slim
│ │ ├── navbar
│ │ │ ├── locales
│ │ │ │ ├── _for_users.html.slim
│ │ │ │ └── _for_guests.html.slim
│ │ │ ├── _header.html.slim
│ │ │ └── _settings.html.slim
│ │ ├── mailer.html.erb
│ │ ├── _topnavbar.html.slim
│ │ ├── _footer.html.slim
│ │ ├── application.html.slim
│ │ ├── application.html+app.slim
│ │ ├── application.html+msite.slim
│ │ └── _sidebar.html.slim
│ ├── kaminari
│ │ ├── _gap.html.slim
│ │ ├── _first_page.html.slim
│ │ ├── _last_page.html.slim
│ │ ├── _next_page.html.slim
│ │ ├── _prev_page.html.slim
│ │ ├── _page.html.slim
│ │ └── _paginator.html.slim
│ ├── recipes
│ │ ├── save.js.erb
│ │ ├── new.html.slim
│ │ ├── edit.html.slim
│ │ ├── _form.html.slim
│ │ └── show.html.slim
│ ├── devise
│ │ ├── mailer
│ │ │ ├── password_change.html.erb
│ │ │ ├── confirmation_instructions.html.erb
│ │ │ ├── reset_password_instructions.html.erb
│ │ │ └── unlock_instructions.html.erb
│ │ ├── sessions
│ │ │ ├── create.js.erb
│ │ │ ├── create.js+app.erb
│ │ │ ├── new.html.slim
│ │ │ ├── new.js.erb
│ │ │ └── new
│ │ │ │ └── _form.html.slim
│ │ ├── registrations
│ │ │ ├── destroy.js.erb
│ │ │ ├── new.html.slim
│ │ │ ├── create.js+app.erb
│ │ │ ├── update.js.erb
│ │ │ ├── create.js.erb
│ │ │ ├── edit.html.slim
│ │ │ ├── edit
│ │ │ │ └── _form.html.slim
│ │ │ └── new
│ │ │ │ ├── _form.html+app.slim
│ │ │ │ └── _form.html.slim
│ │ └── passwords
│ │ │ ├── create.js.erb
│ │ │ ├── update.js.erb
│ │ │ ├── edit.html.slim
│ │ │ ├── edit
│ │ │ └── _form.html.slim
│ │ │ ├── new.html+app.slim
│ │ │ └── new.html.slim
│ ├── home
│ │ ├── _recipes_search_filters.html.slim
│ │ ├── _recipes_not_found.html.slim
│ │ ├── _recipes_relation.html.slim
│ │ ├── _recipes.html.slim
│ │ ├── index.html+app.slim
│ │ ├── index.js.erb
│ │ ├── _recipe.html.slim
│ │ └── index.html.slim
│ └── errors
│ │ ├── internal_server_error.html.slim
│ │ └── not_found.html.slim
├── jobs
│ └── application_job.rb
├── channels
│ └── application_cable
│ │ ├── channel.rb
│ │ └── connection.rb
├── helpers
│ ├── recipes_helper.rb
│ ├── application_helper.rb
│ └── home_helper.rb
├── mailers
│ └── application_mailer.rb
└── policies
│ ├── user_policy.rb
│ ├── recipe_policy.rb
│ └── application_policy.rb
├── .ruby-gemset
├── vendor
└── assets
│ ├── javascripts
│ ├── .keep
│ └── paper-dashboard.js
│ ├── stylesheets
│ ├── .keep
│ ├── paper
│ │ ├── mixins
│ │ │ ├── _tabs.scss
│ │ │ ├── _cards.scss
│ │ │ ├── _navbars.scss
│ │ │ ├── _icons.scss
│ │ │ ├── _inputs.scss
│ │ │ ├── _transparency.scss
│ │ │ ├── _labels.scss
│ │ │ ├── _sidebar.scss
│ │ │ ├── _buttons.scss
│ │ │ └── _chartist.scss
│ │ ├── _mixins.scss
│ │ ├── _footers.scss
│ │ ├── _alerts.scss
│ │ ├── _misc.scss
│ │ ├── _tables.scss
│ │ ├── _typography.scss
│ │ ├── _checkbox-radio.scss
│ │ ├── _dropdown.scss
│ │ ├── _buttons.scss
│ │ ├── _navbars.scss
│ │ ├── _inputs.scss
│ │ └── _sidebar-and-main-panel.scss
│ └── paper-dashboard.scss
│ ├── fonts
│ ├── .DS_Store
│ ├── themify.eot
│ ├── themify.ttf
│ └── themify.woff
│ └── images
│ ├── .DS_Store
│ ├── favicon.png
│ ├── new_logo.png
│ ├── apple-icon.png
│ ├── background.jpg
│ ├── tim_80x80.png
│ └── faces
│ ├── .DS_Store
│ ├── face-0.jpg
│ ├── face-1.jpg
│ ├── face-2.jpg
│ └── face-3.jpg
├── bin
├── bundle
├── rake
├── rails
├── spring
├── update
└── setup
├── config
├── spring.rb
├── boot.rb
├── environment.rb
├── locales
│ ├── en
│ │ ├── titles.yml
│ │ ├── views
│ │ │ ├── layouts
│ │ │ │ ├── footer.yml
│ │ │ │ └── navbar.yml
│ │ │ ├── devise
│ │ │ │ ├── sessions.yml
│ │ │ │ ├── mailer.yml
│ │ │ │ ├── passwords.yml
│ │ │ │ └── registrations.yml
│ │ │ ├── errors
│ │ │ │ ├── internal_server_error.yml
│ │ │ │ └── not_found.yml
│ │ │ ├── recipes.yml
│ │ │ └── home.yml
│ │ ├── models
│ │ │ ├── recipe.yml
│ │ │ └── user.yml
│ │ ├── pagination.yml
│ │ ├── global.yml
│ │ └── navigation.yml
│ └── pt-BR
│ │ ├── titles.yml
│ │ ├── views
│ │ ├── layouts
│ │ │ ├── footer.yml
│ │ │ └── navbar.yml
│ │ ├── devise
│ │ │ ├── sessions.yml
│ │ │ ├── mailer.yml
│ │ │ ├── passwords.yml
│ │ │ └── registrations.yml
│ │ ├── errors
│ │ │ ├── internal_server_error.yml
│ │ │ └── not_found.yml
│ │ ├── recipes.yml
│ │ └── home.yml
│ │ ├── models
│ │ ├── recipe.yml
│ │ └── user.yml
│ │ ├── pagination.yml
│ │ ├── global.yml
│ │ └── navigation.yml
├── cable.yml
├── initializers
│ ├── session_store.rb
│ ├── locales.rb
│ ├── mime_types.rb
│ ├── meta_tags.rb
│ ├── application_controller_renderer.rb
│ ├── filter_parameter_logging.rb
│ ├── cookies_serializer.rb
│ ├── kaminari_config.rb
│ ├── backtrace_silencers.rb
│ ├── assets.rb
│ ├── wrap_parameters.rb
│ ├── inflections.rb
│ ├── new_framework_defaults.rb
│ └── friendly_id.rb
├── application.rb
├── routes.rb
├── secrets.yml
├── environments
│ ├── test.rb
│ ├── development.rb
│ └── production.rb
├── puma.rb
└── database.yml
├── config.ru
├── spec
├── models
│ ├── user_spec.rb
│ └── recipe_spec.rb
├── factories
│ ├── users.rb
│ └── recipes.rb
├── policies
│ └── user_policy_spec.rb
├── rails_helper.rb
└── spec_helper.rb
├── db
├── migrate
│ ├── 20161112004523_add_story_to_recipes.rb
│ ├── 20160806200056_add_name_to_user.rb
│ ├── 20160806033435_add_role_to_users.rb
│ ├── 20161115142053_add_slug_to_recipes.rb
│ ├── 20160827062724_add_authentication_token_to_users.rb
│ ├── 20161111224106_create_recipes.rb
│ ├── 20161115142009_create_friendly_id_slugs.rb
│ └── 20160806032155_devise_create_users.rb
├── seeds.rb
└── schema.rb
├── Rakefile
├── .gitignore
├── sample.env
├── README.md
├── LICENSE
└── Gemfile
/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.3.2
2 |
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.ruby-gemset:
--------------------------------------------------------------------------------
1 | master-app
2 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/javascripts/channels/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 |
--------------------------------------------------------------------------------
/app/views/kaminari/_gap.html.slim:
--------------------------------------------------------------------------------
1 | li.disabled
2 | = link_to raw(t 'views.pagination.truncate'), '#'
3 |
--------------------------------------------------------------------------------
/app/views/recipes/save.js.erb:
--------------------------------------------------------------------------------
1 | $('form[class*="_recipe"]').replaceWith("<%= j render('recipes/form'.freeze) %>");
2 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/app/init.js:
--------------------------------------------------------------------------------
1 | //= require_tree ./initializers
2 | //= require_tree ./helpers
3 | //= require_tree ./lib
4 |
--------------------------------------------------------------------------------
/vendor/assets/fonts/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/fonts/.DS_Store
--------------------------------------------------------------------------------
/vendor/assets/images/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/.DS_Store
--------------------------------------------------------------------------------
/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/vendor/assets/fonts/themify.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/fonts/themify.eot
--------------------------------------------------------------------------------
/vendor/assets/fonts/themify.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/fonts/themify.ttf
--------------------------------------------------------------------------------
/vendor/assets/fonts/themify.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/fonts/themify.woff
--------------------------------------------------------------------------------
/vendor/assets/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/favicon.png
--------------------------------------------------------------------------------
/vendor/assets/images/new_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/new_logo.png
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/paper/mixins/_tabs.scss:
--------------------------------------------------------------------------------
1 | @mixin pill-style($color){
2 | border: 1px solid $color;
3 | color: $color;
4 | }
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/vendor/assets/images/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/apple-icon.png
--------------------------------------------------------------------------------
/vendor/assets/images/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/background.jpg
--------------------------------------------------------------------------------
/vendor/assets/images/tim_80x80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/tim_80x80.png
--------------------------------------------------------------------------------
/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/helpers/recipes_helper.rb:
--------------------------------------------------------------------------------
1 | module RecipesHelper
2 | def human_attribute(name)
3 | Recipe.human_attribute_name(name)
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/vendor/assets/images/faces/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/faces/.DS_Store
--------------------------------------------------------------------------------
/vendor/assets/images/faces/face-0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/faces/face-0.jpg
--------------------------------------------------------------------------------
/vendor/assets/images/faces/face-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/faces/face-1.jpg
--------------------------------------------------------------------------------
/vendor/assets/images/faces/face-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/faces/face-2.jpg
--------------------------------------------------------------------------------
/vendor/assets/images/faces/face-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/themasterapp/master-app-rails-server/HEAD/vendor/assets/images/faces/face-3.jpg
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/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/_last_page.html.slim:
--------------------------------------------------------------------------------
1 | li
2 | = link_to_unless current_page.last?, raw(t 'views.pagination.last'),
3 | url, remote: remote
4 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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/controllers/users/passwords_controller.rb:
--------------------------------------------------------------------------------
1 | class Users::PasswordsController < Devise::PasswordsController
2 | include MetaTagsHelpers
3 |
4 | respond_to :html, :js
5 | end
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/locales/en/views/layouts/footer.yml:
--------------------------------------------------------------------------------
1 | ---
2 | en:
3 | layouts:
4 | footer:
5 | made_with_love_html:
6 | "made with by %{contact}"
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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";
--------------------------------------------------------------------------------
/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------