diff --git a/.dockerignore b/.dockerignore index f9df56c..015453b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,7 @@ .git/ -logs/*.* +log/*.* db/*.sqlite* tmp/*.* public/assets +public/packs +node_modules/ \ No newline at end of file diff --git a/.postcssrc.yml b/.postcssrc.yml index 150dac3..e43bed1 100644 --- a/.postcssrc.yml +++ b/.postcssrc.yml @@ -1,3 +1,7 @@ plugins: postcss-import: {} - postcss-cssnext: {} + postcss-cssnext: { + features: { + customProperties: false + } + } \ No newline at end of file diff --git a/.ruby-version b/.ruby-version index 4fd0fe3..fad066f 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.5.1 \ No newline at end of file +2.5.0 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 82ede88..59b886a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,16 @@ FROM phusion/passenger-ruby25:latest # Use baseimage-docker's init process. CMD ["/sbin/my_init"] +# Install Node +RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \ + curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ + echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends nodejs yarn && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN gem update --system && gem install bundler + # Enable Nginx / Passenger RUN rm -f /etc/service/nginx/down @@ -21,25 +31,20 @@ ENV RAILS_ENV docker ENV PASSENGER_APP_ENV docker # Setup directory and install gems -RUN mkdir -p /home/app/parsley/ -COPY Gemfile /home/app/parsley/ -COPY Gemfile.lock /home/app/parsley/ -RUN gem install bundler -RUN cd /home/app/parsley/ && bundle install --jobs 4 - -# Copy the app into the image -COPY . /home/app/parsley/ +RUN mkdir -p /home/app/parsley/log /home/app/parsley/tmp +RUN chown -R app:app /home/app/parsley/ WORKDIR /home/app/parsley/ -# Set log permissions -RUN mkdir -p /home/app/parsley/log -RUN chmod 0777 /home/app/parsley/log +COPY Gemfile* ./ +RUN bundle install --deployment --jobs 4 --without development test + +COPY package.json yarn.lock ./ +RUN yarn install --production=true + +# Copy the app into the image +COPY --chown="app" . . # Compile assets -RUN env RAILS_ENV=production bundle exec rake assets:clobber assets:precompile +RUN su app -c "bundle exec rails webpacker:clobber webpacker:compile" -# Set ownership of the tmp folder -RUN chown -R app:app /home/app/parsley/tmp -# Clean up APT when done. -RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* diff --git a/Gemfile b/Gemfile index 85859eb..23b69a7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,14 +1,15 @@ source 'https://rubygems.org' gem 'rails', '5.2.0.rc2' -gem 'sqlite3' gem 'pg', '~> 0.21.0' -gem 'puma', '~> 3.11' - gem 'webpacker', '3.4.1' gem 'bootsnap', '>= 1.1.0', require: false gem 'jbuilder', '~> 2.7' + +gem 'oj', '~> 3.5.0' + +gem 'kaminari', '~> 1.1.1' gem 'unitwise', '~> 2.2.0' gem 'redcarpet', '~> 3.4.0' @@ -19,6 +20,9 @@ gem 'tzinfo-data' group :development, :test do + gem 'puma', '~> 3.11' + gem 'sqlite3' + gem 'guard', '~> 2.14.0' gem 'guard-rspec', require: false gem 'rspec-rails', '~> 3.7.2' diff --git a/Gemfile.lock b/Gemfile.lock index 893a607..30485f1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -81,6 +81,18 @@ GEM jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) + kaminari (1.1.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.1.1) + kaminari-activerecord (= 1.1.1) + kaminari-core (= 1.1.1) + kaminari-actionview (1.1.1) + actionview + kaminari-core (= 1.1.1) + kaminari-activerecord (1.1.1) + activerecord + kaminari-core (= 1.1.1) + kaminari-core (1.1.1) liner (0.2.4) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) @@ -89,7 +101,7 @@ GEM loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) - lumberjack (1.0.12) + lumberjack (1.0.13) mail (2.7.0) mini_mime (>= 0.1.1) marcel (0.3.2) @@ -110,6 +122,7 @@ GEM notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) + oj (3.5.0) parslet (1.8.2) pg (0.21.0) pry (0.11.3) @@ -216,6 +229,8 @@ DEPENDENCIES guard (~> 2.14.0) guard-rspec jbuilder (~> 2.7) + kaminari (~> 1.1.1) + oj (~> 3.5.0) pg (~> 0.21.0) puma (~> 3.11) rails (= 5.2.0.rc2) diff --git a/app/javascript/components/App.vue b/app/javascript/components/App.vue index f57063b..559ebfb 100644 --- a/app/javascript/components/App.vue +++ b/app/javascript/components/App.vue @@ -1,13 +1,34 @@ \ No newline at end of file diff --git a/app/javascript/components/AppNavbar.vue b/app/javascript/components/AppNavbar.vue new file mode 100644 index 0000000..1b0d62b --- /dev/null +++ b/app/javascript/components/AppNavbar.vue @@ -0,0 +1,54 @@ + + + \ No newline at end of file diff --git a/app/javascript/components/RecipeEdit.vue b/app/javascript/components/RecipeEdit.vue new file mode 100644 index 0000000..b49d99e --- /dev/null +++ b/app/javascript/components/RecipeEdit.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/javascript/components/RecipeShow.vue b/app/javascript/components/RecipeShow.vue new file mode 100644 index 0000000..b49d99e --- /dev/null +++ b/app/javascript/components/RecipeShow.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/javascript/components/TheAboutPage.vue b/app/javascript/components/TheAboutPage.vue new file mode 100644 index 0000000..b49d99e --- /dev/null +++ b/app/javascript/components/TheAboutPage.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/javascript/components/TheCalculator.vue b/app/javascript/components/TheCalculator.vue new file mode 100644 index 0000000..b49d99e --- /dev/null +++ b/app/javascript/components/TheCalculator.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/javascript/components/TheIngredientList.vue b/app/javascript/components/TheIngredientList.vue new file mode 100644 index 0000000..b49d99e --- /dev/null +++ b/app/javascript/components/TheIngredientList.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/javascript/components/TheNotesList.vue b/app/javascript/components/TheNotesList.vue new file mode 100644 index 0000000..b49d99e --- /dev/null +++ b/app/javascript/components/TheNotesList.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/javascript/components/TheRecipeList.vue b/app/javascript/components/TheRecipeList.vue index 49647c7..1407417 100644 --- a/app/javascript/components/TheRecipeList.vue +++ b/app/javascript/components/TheRecipeList.vue @@ -1,13 +1,51 @@ \ No newline at end of file diff --git a/app/javascript/lib/Api.js b/app/javascript/lib/Api.js index f2d3c2a..e05f28b 100644 --- a/app/javascript/lib/Api.js +++ b/app/javascript/lib/Api.js @@ -66,7 +66,18 @@ class Api { return this.performRequest(url, "GET"); } - + getRecipeList(page, per, sortColumn, sortDirection, name, tags) { + const params = { + page: page || null, + per: per || null, + sort_column: sortColumn || null, + sort_direction: sortDirection || null, + name: name || null, + tags: tags || null + }; + + return this.get("/recipes", params); + } } const api = new Api(); diff --git a/app/javascript/lib/GlobalMixins.js b/app/javascript/lib/GlobalMixins.js new file mode 100644 index 0000000..bb8b118 --- /dev/null +++ b/app/javascript/lib/GlobalMixins.js @@ -0,0 +1,26 @@ + +import Vue from 'vue'; +import { mapMutations, mapState } from 'vuex'; + +Vue.mixin({ + computed: { + ...mapState({ + isLoading: state => state.loading + }) + }, + methods: { + ...mapMutations([ + 'setError', + 'setLoading' + ]), + + loadResource(promise, successFunc) { + this.setLoading(true); + + return promise + .then(successFunc) + .catch(err => this.setError(err)) + .then(() => this.setLoading(false)); + } + } +}); \ No newline at end of file diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 8050479..01215d6 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -2,9 +2,9 @@ import '../styles'; import Vue from 'vue' import config from '../config'; -/// import 'App/lib/Fontawesome'; import store from '../store'; import router from '../router'; +import '../lib/GlobalMixins'; import App from '../components/App'; //Vue.use(VueClipboard); diff --git a/app/javascript/router.js b/app/javascript/router.js index 8d0d1cc..cd9ebc8 100644 --- a/app/javascript/router.js +++ b/app/javascript/router.js @@ -1,7 +1,13 @@ import Vue from 'vue'; import Router from 'vue-router'; +import RecipeEdit from './components/RecipeEdit'; +import RecipeShow from './components/RecipeShow'; import The404Page from './components/The404Page'; +import TheAboutPage from './components/TheAboutPage'; +import TheCalculator from './components/TheCalculator'; +import TheIngredientList from './components/TheIngredientList'; +import TheNotesList from './components/TheNotesList'; import TheRecipeList from './components/TheRecipeList'; Vue.use(Router); @@ -13,6 +19,36 @@ export default new Router({ name: 'recipeList', component: TheRecipeList }, + { + path: '/recipes/:id/edit', + name: 'edit_recipe', + component: RecipeEdit + }, + { + path: '/recipe/:id', + name: 'recipe', + component: RecipeShow + }, + { + path: "/about", + name: "about", + component: TheAboutPage + }, + { + path: "/calculator", + name: "calculator", + component: TheCalculator + }, + { + path: "/ingredients", + name: "ingredients", + component: TheIngredientList + }, + { + path: "/notes", + name: "notes", + component: TheNotesList + }, { path: '*', component: The404Page diff --git a/app/javascript/store/index.js b/app/javascript/store/index.js index 5dfebc8..a6162cf 100644 --- a/app/javascript/store/index.js +++ b/app/javascript/store/index.js @@ -6,13 +6,20 @@ Vue.use(Vuex); export default new Vuex.Store({ strict: process.env.NODE_ENV !== 'production', state: { - + loading: false, + error: null }, getters: { }, mutations: { + setLoading(state, value) { + state.loading = value; + }, + setError(state, value) { + state.error = value; + } }, actions: { diff --git a/app/javascript/styles/_variables.scss b/app/javascript/styles/_variables.scss new file mode 100644 index 0000000..399355a --- /dev/null +++ b/app/javascript/styles/_variables.scss @@ -0,0 +1,10 @@ + +// Bluma default overrides +$green: #79A736; +$primary: $green; + + +// Make all Bulma variables and functions available +@import "~bulma/sass/utilities/initial-variables"; +@import "~bulma/sass/utilities/functions"; + diff --git a/app/javascript/styles/index.scss b/app/javascript/styles/index.scss index e69de29..e674b93 100644 --- a/app/javascript/styles/index.scss +++ b/app/javascript/styles/index.scss @@ -0,0 +1,17 @@ +@import "./variables"; + +@import "~bulma/sass/utilities/_all"; +@import "~bulma/sass/base/_all"; +@import "~bulma/sass/components/navbar"; +@import "~bulma/sass/elements/_all"; +@import "~bulma/sass/grid/columns"; +@import "~bulma/sass/layout/section"; + +body { + background-color: $grey-dark; +} + +#app .container { + padding: 1rem; + background-color: $background; +} \ No newline at end of file diff --git a/app/views/recipes/index.json.jbuilder b/app/views/recipes/index.json.jbuilder new file mode 100644 index 0000000..10d8688 --- /dev/null +++ b/app/views/recipes/index.json.jbuilder @@ -0,0 +1,10 @@ + +json.extract! @recipes, :total_count, :total_pages, :current_page +json.page_size @recipes.limit_value + +json.recipes @recipes do |r| + json.extract! r, :id, :name, :rating, :yields, :total_time, :active_time, :created_at, :updated_at + + json.tags r.tag_names +end + diff --git a/config/environments/docker.rb b/config/environments/docker.rb index 949d5a3..9251fc1 100644 --- a/config/environments/docker.rb +++ b/config/environments/docker.rb @@ -1,54 +1,88 @@ Rails.application.configure do + # Verifies that versions and hashed value of the package contents in the project's package.json + config.webpacker.check_yarn_integrity = false # Settings specified here will take precedence over those in config/application.rb. - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. - config.cache_classes = false + # Code is not reloaded between requests. + config.cache_classes = true - # Do not eager load code on boot. - config.eager_load = false + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true - # Show full error reports. - config.consider_all_requests_local = true + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = true + config.action_controller.perform_caching = true - # Enable/disable caching. By default caching is disabled. - if Rails.root.join('tmp/caching-dev.txt').exist? - config.action_controller.perform_caching = true + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - config.cache_store = :memory_store - config.public_file_server.headers = { - 'Cache-Control' => 'public, max-age=172800' - } - else - config.action_controller.perform_caching = false + # # Compress JavaScripts and CSS. + # config.assets.js_compressor = :uglifier + # # config.assets.css_compressor = :sass + # + # # Do not fallback to assets pipeline if a precompiled asset is missed. + # config.assets.compile = false - config.cache_store = :null_store - end + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb - # Don't care if the mailer can't send. - config.action_mailer.raise_delivery_errors = false + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "parsley_#{Rails.env}" config.action_mailer.perform_caching = false - # Print deprecation notices to the Rails logger. - config.active_support.deprecation = :log + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false - # Raise an error on page load if there are pending migrations. - config.active_record.migration_error = :page_load + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true - # # Debug mode disables concatenation and preprocessing of assets. - # # This option may cause significant delays in view rendering with a large - # # number of complex assets. - # config.assets.debug = true - # - # # Suppress logger output for asset requests. - # config.assets.quiet = true + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. - # config.file_watcher = ActiveSupport::EventedFileUpdateChecker + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false end diff --git a/docker-compose.yml b/docker-compose.yml index af0348a..9db182f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '3' services: postgres: @@ -12,8 +12,6 @@ services: build: . ports: - "3000:80" - volumes: - - .:/home/app/parsley/ links: - postgres volumes: diff --git a/docker/nginx_server.conf b/docker/nginx_server.conf index d8c86f3..6643427 100644 --- a/docker/nginx_server.conf +++ b/docker/nginx_server.conf @@ -7,7 +7,7 @@ server { passenger_ruby /usr/bin/ruby; - location ~ ^/(assets)/ { + location ~ ^/(assets|packs)/ { gzip_static on; expires max; add_header Cache-Control public; diff --git a/package.json b/package.json index 794d4b7..982e4a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { "@rails/webpacker": "^3.4.1", + "bulma": "^0.6.2", "caniuse-lite": "^1.0.30000815", "css-loader": "^0.28.11", "vue": "^2.5.16", diff --git a/yarn.lock b/yarn.lock index 7920adb..47069e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1028,6 +1028,10 @@ builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" +bulma@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.6.2.tgz#f4b1d11d5acc51a79644eb0a2b0b10649d3d71f5" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -2626,7 +2630,7 @@ http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" -http-errors@1.6.2, http-errors@~1.6.2: +http-errors@1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" dependencies: @@ -2635,6 +2639,15 @@ http-errors@1.6.2, http-errors@~1.6.2: setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-parser-js@>=0.4.0: version "0.4.11" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.11.tgz#5b720849c650903c27e521633d94696ee95f3529" @@ -5361,7 +5374,7 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.3.1 < 2": +"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"