diff --git a/.ruby-version b/.ruby-version index 2bf1c1c..005119b 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.1 +2.4.1 diff --git a/Dockerfile b/Dockerfile index 8599c96..6c4f61c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM phusion/passenger-ruby23:latest +FROM phusion/passenger-ruby24:latest # Use baseimage-docker's init process. CMD ["/sbin/my_init"] diff --git a/Gemfile b/Gemfile index c5301f9..7d15567 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,8 @@ source 'https://rubygems.org' -gem 'rails', '5.0.0' +gem 'rails', '5.0.2' gem 'sqlite3' -gem 'pg', '~> 0.18.4' +gem 'pg', '~> 0.20.0' gem 'sass-rails', '~> 5.0' gem 'uglifier', '>= 1.3.0' @@ -12,11 +12,11 @@ gem 'puma' gem 'therubyracer', platforms: :ruby # Use jquery as the JavaScript library -gem 'jquery-rails', '~> 4.1.1' -gem 'bootstrap-sass', '~> 3.3.6' -gem 'kaminari', '~> 0.17.0' -gem 'turbolinks', '~> 5.0.0' -gem 'jbuilder', '~> 2.5' +gem 'jquery-rails', '~> 4.3.1' +gem 'bootstrap-sass', '~> 3.3.7' +gem 'kaminari', '~> 1.0.1' +gem 'turbolinks', '~> 5.0.1' +gem 'jbuilder', '~> 2.6' gem 'cocoon', '~> 1.2.9' gem 'unitwise', '~> 2.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 3c1d53c..88b48b9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,59 +1,59 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.0.0) - actionpack (= 5.0.0) - nio4r (~> 1.2) + actioncable (5.0.2) + actionpack (= 5.0.2) + nio4r (>= 1.2, < 3.0) websocket-driver (~> 0.6.1) - actionmailer (5.0.0) - actionpack (= 5.0.0) - actionview (= 5.0.0) - activejob (= 5.0.0) + actionmailer (5.0.2) + actionpack (= 5.0.2) + actionview (= 5.0.2) + activejob (= 5.0.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.0.0) - actionview (= 5.0.0) - activesupport (= 5.0.0) + actionpack (5.0.2) + actionview (= 5.0.2) + activesupport (= 5.0.2) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.0) - activesupport (= 5.0.0) + actionview (5.0.2) + activesupport (= 5.0.2) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - activejob (5.0.0) - activesupport (= 5.0.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.0.2) + activesupport (= 5.0.2) globalid (>= 0.3.6) - activemodel (5.0.0) - activesupport (= 5.0.0) - activerecord (5.0.0) - activemodel (= 5.0.0) - activesupport (= 5.0.0) + activemodel (5.0.2) + activesupport (= 5.0.2) + activerecord (5.0.2) + activemodel (= 5.0.2) + activesupport (= 5.0.2) arel (~> 7.0) - activesupport (5.0.0) + activesupport (5.0.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) - arel (7.1.2) - autoprefixer-rails (6.5.0) + arel (7.1.4) + autoprefixer-rails (6.7.7.1) execjs bcrypt (3.1.11) blankslate (3.1.3) bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) sass (>= 3.3.4) - builder (3.2.2) - byebug (9.0.5) + builder (3.2.3) + byebug (9.0.6) cocoon (1.2.9) coderay (1.1.1) - concurrent-ruby (1.0.2) + concurrent-ruby (1.0.5) database_cleaner (1.5.3) debug_inspector (0.0.2) - diff-lcs (1.2.5) + diff-lcs (1.3) erubis (2.7.0) execjs (2.7.0) factory_girl (4.7.0) @@ -61,11 +61,11 @@ GEM factory_girl_rails (4.7.0) factory_girl (~> 4.7.0) railties (>= 3.0.0) - ffi (1.9.14) + ffi (1.9.18) formatador (0.2.5) globalid (0.3.7) activesupport (>= 4.1.0) - guard (2.14.0) + guard (2.14.1) formatador (>= 0.2.4) listen (>= 2.7, < 4.0) lumberjack (~> 1.0) @@ -79,18 +79,27 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) - i18n (0.7.0) - jbuilder (2.6.0) - activesupport (>= 3.0.0, < 5.1) + i18n (0.8.1) + jbuilder (2.6.3) + activesupport (>= 3.0.0, < 5.2) multi_json (~> 1.2) - jquery-rails (4.1.1) + jquery-rails (4.3.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - kaminari (0.17.0) - actionpack (>= 3.0.0) - activesupport (>= 3.0.0) - libv8 (3.16.14.15) + kaminari (1.0.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.0.1) + kaminari-activerecord (= 1.0.1) + kaminari-core (= 1.0.1) + kaminari-actionview (1.0.1) + actionview + kaminari-core (= 1.0.1) + kaminari-activerecord (1.0.1) + activerecord + kaminari-core (= 1.0.1) + kaminari-core (1.0.1) + libv8 (3.16.14.19) liner (0.2.4) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) @@ -98,7 +107,7 @@ GEM ruby_dep (~> 1.2) loofah (2.0.3) nokogiri (>= 1.5.9) - lumberjack (1.0.10) + lumberjack (1.0.11) mail (2.6.4) mime-types (>= 1.16, < 4) memoizable (0.4.2) @@ -108,65 +117,63 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.1.0) - minitest (5.9.1) + minitest (5.10.1) multi_json (1.12.1) nenv (0.3.0) - nio4r (1.2.1) - nokogiri (1.6.8) + nio4r (2.0.0) + nokogiri (1.7.1) mini_portile2 (~> 2.1.0) - pkg-config (~> 1.1.7) notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) parslet (1.7.1) blankslate (>= 2.0, <= 4.0) - pg (0.18.4) - pkg-config (1.1.7) + pg (0.20.0) pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - puma (3.6.0) + puma (3.8.2) rack (2.0.1) rack-test (0.6.3) rack (>= 1.0) - rails (5.0.0) - actioncable (= 5.0.0) - actionmailer (= 5.0.0) - actionpack (= 5.0.0) - actionview (= 5.0.0) - activejob (= 5.0.0) - activemodel (= 5.0.0) - activerecord (= 5.0.0) - activesupport (= 5.0.0) + rails (5.0.2) + actioncable (= 5.0.2) + actionmailer (= 5.0.2) + actionpack (= 5.0.2) + actionview (= 5.0.2) + activejob (= 5.0.2) + activemodel (= 5.0.2) + activerecord (= 5.0.2) + activesupport (= 5.0.2) bundler (>= 1.3.0, < 2.0) - railties (= 5.0.0) + railties (= 5.0.2) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.1) actionpack (~> 5.x) actionview (~> 5.x) activesupport (~> 5.x) - rails-dom-testing (2.0.1) + rails-dom-testing (2.0.2) activesupport (>= 4.2.0, < 6.0) - nokogiri (~> 1.6.0) + nokogiri (~> 1.6) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - railties (5.0.0) - actionpack (= 5.0.0) - activesupport (= 5.0.0) + railties (5.0.2) + actionpack (= 5.0.2) + activesupport (= 5.0.2) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (11.3.0) - rb-fsevent (0.9.7) - rb-inotify (0.9.7) + rake (12.0.0) + rb-fsevent (0.9.8) + rb-inotify (0.9.8) ffi (>= 0.5.0) ref (2.0.0) rspec (3.5.0) rspec-core (~> 3.5.0) rspec-expectations (~> 3.5.0) rspec-mocks (~> 3.5.0) - rspec-core (3.5.3) + rspec-core (3.5.4) rspec-support (~> 3.5.0) rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) @@ -183,8 +190,8 @@ GEM rspec-mocks (~> 3.5.0) rspec-support (~> 3.5.0) rspec-support (3.5.0) - ruby_dep (1.4.0) - sass (3.4.22) + ruby_dep (1.5.0) + sass (3.4.23) sass-rails (5.0.6) railties (>= 4.0.0, < 6) sass (~> 3.1) @@ -194,26 +201,26 @@ GEM shellany (0.0.1) signed_multiset (0.2.1) slop (3.6.0) - sprockets (3.7.0) + sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - sqlite3 (1.3.11) - therubyracer (0.12.2) - libv8 (~> 3.16.14.0) + sqlite3 (1.3.13) + therubyracer (0.12.3) + libv8 (~> 3.16.14.15) ref - thor (0.19.1) - thread_safe (0.3.5) - tilt (2.0.5) + thor (0.19.4) + thread_safe (0.3.6) + tilt (2.0.7) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.0) - tzinfo (1.2.2) + tzinfo (1.2.3) thread_safe (~> 0.1) - uglifier (3.0.2) + uglifier (3.1.11) execjs (>= 0.3.0, < 3) unitwise (2.0.0) liner (~> 0.2) @@ -225,7 +232,7 @@ GEM activemodel (>= 5.0) debug_inspector railties (>= 5.0) - websocket-driver (0.6.4) + websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) @@ -234,28 +241,28 @@ PLATFORMS DEPENDENCIES bcrypt (~> 3.1.11) - bootstrap-sass (~> 3.3.6) + bootstrap-sass (~> 3.3.7) byebug cocoon (~> 1.2.9) database_cleaner (~> 1.5.3) factory_girl_rails (~> 4.7.0) guard (~> 2.14.0) guard-rspec - jbuilder (~> 2.5) - jquery-rails (~> 4.1.1) - kaminari (~> 0.17.0) - pg (~> 0.18.4) + jbuilder (~> 2.6) + jquery-rails (~> 4.3.1) + kaminari (~> 1.0.1) + pg (~> 0.20.0) puma - rails (= 5.0.0) + rails (= 5.0.2) rails-controller-testing rspec-rails (~> 3.5.0) sass-rails (~> 5.0) sqlite3 therubyracer - turbolinks (~> 5.0.0) + turbolinks (~> 5.0.1) uglifier (>= 1.3.0) unitwise (~> 2.0.0) web-console (~> 3.3.1) BUNDLED WITH - 1.13.1 + 1.14.6 diff --git a/app/assets/javascripts/recipes.js b/app/assets/javascripts/recipes.js index e9eacfb..5122c7b 100644 --- a/app/assets/javascripts/recipes.js +++ b/app/assets/javascripts/recipes.js @@ -3,6 +3,31 @@ $(document).on("turbolinks:load", function() { $(".recipe-view ul.ingredients").checkable(); $(".recipe-view ol.steps").checkable(); + + var $searchBtn = $("#recipe_index_search_button"); + + if ($searchBtn.length) { + var $form = $("#search_form"); + var $nameInput = $("#name_search"); + var $tagsInput = $("#tags_search"); + + $form.submit(function() { + $("#criteria_name").val($nameInput.val()); + $("#criteria_tags").val($tagsInput.val()); + }); + + $searchBtn.click(function(evt) { + $form.submit(); + }); + + $nameInput.add($tagsInput).on('keydown', function(evt) { + if (evt.which == 13) { + console.log('keydown enter pressed'); + $form.submit(); + } + }); + + } }); })(jQuery); diff --git a/app/models/recipe.rb b/app/models/recipe.rb index 72501f1..2e4c771 100644 --- a/app/models/recipe.rb +++ b/app/models/recipe.rb @@ -1,4 +1,5 @@ class Recipe < ApplicationRecord + include TokenizedLike has_many :recipe_ingredients, -> { order :sort_order }, inverse_of: :recipe, dependent: :destroy has_many :recipe_steps, -> { order :sort_order }, inverse_of: :recipe, dependent: :destroy @@ -9,7 +10,6 @@ class Recipe < ApplicationRecord scope :undeleted, -> { where('deleted <> ? OR deleted IS NULL', true) } scope :not_log, -> { where('is_log <> ? OR is_log IS NULL', true) } scope :active, -> { undeleted.not_log } - scope :for_criteria, ->(criteria) { active.order(criteria.sort_column => criteria.sort_direction).page(criteria.page).per(criteria.per) } accepts_nested_attributes_for :recipe_ingredients, allow_destroy: true accepts_nested_attributes_for :recipe_steps, allow_destroy: true @@ -104,6 +104,21 @@ class Recipe < ApplicationRecord copy end + def self.for_criteria(criteria) + query = active.order(criteria.sort_column => criteria.sort_direction).page(criteria.page).per(criteria.per) + + if criteria.name.present? + query = query.matches_token(:name, criteria.name) + end + + if criteria.tags.present? + tags = Tag.by_name(criteria.tags.split) + query = query.where(id: tags.joins(:recipes).pluck('recipes.id')) + end + + query + end + private def calculate_nutrition_data diff --git a/app/models/tag.rb b/app/models/tag.rb index dcfe6ca..5eb9065 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -1,6 +1,8 @@ class Tag < ApplicationRecord include TokenizedLike + has_and_belongs_to_many :recipes + scope :by_name, ->(names) { where(lowercase_name: Array.wrap(names).map { |n| n.downcase }) } validates :name, presence: true, length: {maximum: 250}, uniqueness: { case_sensitive: false } diff --git a/app/models/view_models/recipe_criteria.rb b/app/models/view_models/recipe_criteria.rb index 76c98be..a33a5e4 100644 --- a/app/models/view_models/recipe_criteria.rb +++ b/app/models/view_models/recipe_criteria.rb @@ -2,13 +2,13 @@ module ViewModels class RecipeCriteria SORT_COLUMNS = :created_at, :name, :rating, :total_time + PARAMS = [:sort_column, :sort_direction, :page, :per, :name, :tags] - attr_writer :sort_column, :sort_direction - attr_writer :page, :per + attr_accessor *PARAMS - def initialize(params = {}) + def initialize(params = nil) params ||= {} - ([:sort_column, :sort_direction, :page, :per]).each do |attr| + PARAMS.each do |attr| setter = "#{attr}=" if params[attr] self.send(setter, params[attr]) @@ -17,31 +17,29 @@ module ViewModels end def sort_column - @sort_column ||= SORT_COLUMNS.first - @sort_column = @sort_column.to_sym - unless SORT_COLUMNS.include? @sort_column - @sort_column = SORT_COLUMNS.first - end - - @sort_column + default(@sort_column.to_s.to_sym, SORT_COLUMNS.first, ->(v) {SORT_COLUMNS.include?(v)} ) end def sort_direction - @sort_direction ||= :asc - @sort_direction = @sort_direction.to_sym - unless [:asc, :desc].include? @sort_direction - @sort_direction = :asc - end - - @sort_direction + default(@sort_direction.to_s.to_sym, :desc, ->(v) {[:asc, :desc].include?(v)} ) end def page - @page.to_i || 1 + default(@page, 1, ->(v) { v.to_i > 0 }) end def per - @per.to_i || 50 + default(@per, 50, ->(v) { v.to_i > 0 }) + end + + protected + + def default(val, default_val, valid_proc) + if val.blank? || !valid_proc.call(val) + default_val + else + val + end end end diff --git a/app/views/recipes/index.html.erb b/app/views/recipes/index.html.erb index b946bc6..77d15c2 100644 --- a/app/views/recipes/index.html.erb +++ b/app/views/recipes/index.html.erb @@ -5,70 +5,85 @@
<%= index_sort_header('Name', :name, @criteria) %> | +Tags | +<%= index_sort_header('Rating', :rating, @criteria) %> | +Yields | +<%= index_sort_header('Time', :total_time, @criteria) %> | +<%= index_sort_header('Created', :created_at, @criteria) %> | ++ |
---|---|---|---|---|---|---|
<%= text_field_tag('name_search', @criteria.name) %> | +<%= text_field_tag('tags_search', @criteria.tags) %> | ++ | + | + | + | + |
<%= link_to recipe.short_name, recipe %> | +<%= recipe.tag_names %> | ++ <% if recipe.rating %> + <%= text_field_tag('rating', recipe.rating, disabled: true, data: {rating: true, size: '20px', interval: '0.25'}) %> + <% else %> + -- + <% end %> + | +<%= recipe.yields %> | +<%= recipe_time(recipe) %> | +<%= timestamp(recipe.created_at) %> | + ++ <% if current_user? %> + <%= link_to new_recipe_log_path(recipe), class: 'btn btn-sm btn-primary' do %> + + <% end %> + <%= link_to edit_recipe_path(recipe), class: 'btn btn-sm btn-primary' do %> + + <% end %> + <%= link_to recipe, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-sm btn-danger' do %> + + <% end %> + <% end %> + | +
No Recipes
- <% else %> - - <% if current_user? %> - <%= link_to 'New Recipe', new_recipe_path, class: 'btn btn-default' %><%= index_sort_header('Name', :name, @criteria) %> | -Tags | -<%= index_sort_header('Rating', :rating, @criteria) %> | -Yields | -<%= index_sort_header('Time', :total_time, @criteria) %> | -<%= index_sort_header('Created', :created_at, @criteria) %> | - <% if current_user? %> -- <% end %> - |
---|---|---|---|---|---|---|
<%= link_to recipe.short_name, recipe %> | -<%= recipe.tag_names %> | -- <% if recipe.rating %> - <%= text_field_tag('rating', recipe.rating, disabled: true, data: {rating: true, size: '20px', interval: '0.25'}) %> - <% else %> - -- - <% end %> - | -<%= recipe.yields %> | -<%= recipe_time(recipe) %> | -<%= timestamp(recipe.created_at) %> | - <% if current_user? %> -- <%= link_to new_recipe_log_path(recipe), class: 'btn btn-sm btn-primary' do %> - - <% end %> - <%= link_to edit_recipe_path(recipe), class: 'btn btn-sm btn-primary' do %> - - <% end %> - <%= link_to recipe, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-sm btn-danger' do %> - - <% end %> - | - <% end %> -