catch up with upstream dev

This commit is contained in:
Taylor Griffin
2015-02-04 21:41:42 +11:00
27 changed files with 305 additions and 104 deletions

View File

@@ -14,6 +14,8 @@ before_script:
script:
- bundle exec rake db:migrate --trace
- bundle exec rspec spec/
services:
- elasticsearch
before_deploy:
- bundle exec script/heroku_maintenance.rb on
deploy:
@@ -32,3 +34,4 @@ deploy:
- restart
after_deploy:
- bundle exec script/heroku_maintenance.rb off

View File

@@ -31,6 +31,7 @@ gem 'figaro' # for handling config via ENV variables
gem 'cancancan', '~> 1.9' # for checking member privileges
gem 'gibbon' # for Mailchimp newsletter subscriptions
gem 'csv_shaper' # CSV export
gem 'ruby-units' # for unit conversion
# vendored activemerchant for testing- needed for bogus paypal
# gateway monkeypatch
@@ -76,6 +77,12 @@ gem 'omniauth'
gem 'omniauth-twitter'
gem 'omniauth-flickr', '>= 0.0.15'
# client for Elasticsearch. Elasticsearch is a flexible
# and powerful, distributed, real-time search and analytics engine.
# An example of the use in the project is fuzzy crop search.
gem "elasticsearch-model"
gem "elasticsearch-rails"
gem 'rake', '>= 10.0.0'
group :production, :staging do
@@ -83,6 +90,7 @@ group :production, :staging do
gem 'dalli'
gem 'memcachier'
gem 'rails_12factor' # supresses heroku plugin injection
gem 'bonsai-elasticsearch-rails' # Integration with Bonsa-Elasticsearch on heroku
end
group :development do

View File

@@ -56,6 +56,7 @@ GEM
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
bluecloth (2.2.0)
bonsai-elasticsearch-rails (0.0.4)
bootstrap-datepicker-rails (1.3.0.2)
railties (>= 3.0)
builder (3.2.2)
@@ -109,6 +110,19 @@ GEM
json
thread
thread_safe
elasticsearch (1.0.6)
elasticsearch-api (= 1.0.6)
elasticsearch-transport (= 1.0.6)
elasticsearch-api (1.0.6)
multi_json
elasticsearch-model (0.1.6)
activesupport (> 3)
elasticsearch (> 0.4)
hashie
elasticsearch-rails (0.1.6)
elasticsearch-transport (1.0.6)
faraday
multi_json
erubis (2.7.0)
excon (0.43.0)
execjs (2.2.2)
@@ -117,6 +131,8 @@ GEM
factory_girl_rails (4.5.0)
factory_girl (~> 4.5.0)
railties (>= 3.0.0)
faraday (0.9.1)
multipart-post (>= 1.2, < 3)
figaro (1.0.0)
thor (~> 0.14)
flickraw (0.9.8)
@@ -195,6 +211,7 @@ GEM
minitest (5.5.1)
multi_json (1.10.1)
multi_xml (0.5.5)
multipart-post (2.0.0)
netrc (0.10.0)
newrelic_rpm (3.9.8.273)
nokogiri (1.6.5)
@@ -275,6 +292,7 @@ GEM
rspec-mocks (~> 3.1.0)
rspec-support (~> 3.1.0)
rspec-support (3.1.2)
ruby-units (1.4.5)
ruby_parser (3.1.3)
sexp_processor (~> 4.1)
sass (3.2.19)
@@ -341,6 +359,7 @@ DEPENDENCIES
better_errors
binding_of_caller
bluecloth
bonsai-elasticsearch-rails
bootstrap-datepicker-rails
bundler (>= 1.1.5)
byebug
@@ -353,6 +372,8 @@ DEPENDENCIES
dalli
database_cleaner (~> 1.3.0)
devise (~> 3.4.1)
elasticsearch-model
elasticsearch-rails
factory_girl_rails (~> 4.5.0)
figaro
flickraw
@@ -388,6 +409,7 @@ DEPENDENCIES
rake (>= 10.0.0)
rspec-activemodel-mocks
rspec-rails (~> 3.1.0)
ruby-units
sass-rails (~> 4.0.4)
therubyracer (~> 0.12)
uglifier (~> 2.5.3)

View File

@@ -60,18 +60,16 @@ class CropsController < ApplicationController
# GET /crops/search
def search
@search = params[:search]
@exact_match = Crop.find_by_name(params[:search])
@partial_matches = Crop.search(params[:search])
# exclude exact match from partial match list
@partial_matches = @partial_matches.reject{ |r| @exact_match && r.eql?(@exact_match) }
@fuzzy = Crop.search(params[:term])
@all_matches = Crop.search(params[:search])
exact_match = Crop.find_by_name(params[:search])
if exact_match
@all_matches.delete(exact_match)
@all_matches.unshift(exact_match)
end
respond_to do |format|
format.html
format.json { render :json => @fuzzy }
format.json { render :json => Crop.search(params[:term]) }
end
end

View File

@@ -107,6 +107,6 @@ class HarvestsController < ApplicationController
def harvest_params
params.require(:harvest).permit(:crop_id, :harvested_at, :description, :owner_id,
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug)
:quantity, :unit, :weight_quantity, :weight_unit, :plant_part_id, :slug, :si_weight)
end
end

View File

@@ -21,13 +21,14 @@ class Ability
cannot :read, Account
cannot :read, AccountType
# and nobody should be able to view these expect admins and crop wranglers
cannot :read, Crop, :approval_status => ["rejected", "pending"]
# # ... unless the current member is also the requester
# can :read, Crop, :requester_id => member.id
# can :read, Crop, :approval_status => "approved"
# nobody should be able to view unapproved crops unless they
# are wranglers or admins
cannot :read, Crop
can :read, Crop, :approval_status => "approved"
if member
# members can see even rejected or pending crops if they requested it
can :read, Crop, :requester_id => member.id
# managing your own user settings
can :update, Member, :id => member.id

View File

@@ -1,4 +1,5 @@
class AlternateName < ActiveRecord::Base
after_commit { |an| an.crop.__elasticsearch__.index_document if an.crop }
belongs_to :crop
belongs_to :creator, :class_name => 'Member'
end

View File

@@ -2,12 +2,12 @@ class Crop < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:slugged, :finders]
has_many :scientific_names
has_many :scientific_names, after_add: :update_index, after_remove: :update_index
accepts_nested_attributes_for :scientific_names,
:allow_destroy => true,
:reject_if => :all_blank
has_many :alternate_names
has_many :alternate_names, after_add: :update_index, after_remove: :update_index
has_many :plantings
has_many :photos, :through => :plantings
has_many :seeds
@@ -43,6 +43,68 @@ class Crop < ActiveRecord::Base
## This validation addresses a race condition
validate :approval_status_cannot_be_changed_again
####################################
# Elastic search configuration
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
# In order to avoid clashing between different environments,
# use Rails.env as a part of index name (eg. development_growstuff)
index_name [Rails.env, "growstuff"].join('_')
settings index: { number_of_shards: 1 },
analysis: {
tokenizer: {
gs_edgeNGram_tokenizer: {
type: "edgeNGram", # edgeNGram: NGram match from the start of a token
min_gram: 3,
max_gram: 10,
# token_chars: Elasticsearch will split on characters
# that dont belong to any of these classes
token_chars: [ "letter", "digit" ]
}
},
analyzer: {
gs_edgeNGram_analyzer: {
tokenizer: "gs_edgeNGram_tokenizer",
filter: ["lowercase"]
}
},
} do
mappings dynamic: 'false' do
indexes :id, type: 'long'
indexes :name, type: 'string', analyzer: 'gs_edgeNGram_analyzer'
indexes :scientific_names do
indexes :scientific_name,
type: 'string',
analyzer: 'gs_edgeNGram_analyzer',
# Disabling field-length norm (norm). If the norm option is turned on(by default),
# higher weigh would be given for shorter fields, which in our case is irrelevant.
norms: { enabled: false }
end
indexes :alternate_names do
indexes :name, type: 'string', analyzer: 'gs_edgeNGram_analyzer'
end
end
end
def as_indexed_json(options={})
self.as_json(
only: [:id, :name],
include: {
scientific_names: { only: :scientific_name },
alternate_names: { only: :name }
})
end
# update the Elasticsearch index (only if we're using it in this
# environment)
def update_index(name_obj)
if ENV["GROWSTUFF_ELASTICSEARCH"] == "true"
__elasticsearch__.index_document
end
end
# End Elasticsearch section
def to_s
return name
end
@@ -232,10 +294,25 @@ class Crop < ActiveRecord::Base
end
# Crop.search(string)
# searches for crops whose names match the string given
# just uses SQL LIKE for now, but can be made fancier later
def self.search(query)
where("name ILIKE ?", "%#{query}%")
if ENV['GROWSTUFF_ELASTICSEARCH'] == "true"
search_str = query.nil? ? "" : query.downcase
response = __elasticsearch__.search( {
# Finds documents which match any field, but uses the _score from
# the best field insead of adding up _score from each field.
query: {
multi_match: {
query: "#{search_str}",
fields: ["name", "scientific_names.scientific_name", "alternate_names.name"]
}
},
size: 50
}
)
return response.records.to_a
else
where("name ILIKE ?", "%#{query}%")
end
end
# Custom validations

View File

@@ -59,6 +59,17 @@ class Harvest < ActiveRecord::Base
after_validation :cleanup_quantities
before_save :set_si_weight
# we're storing the harvest weight in kilograms in the db too
# to make data manipulation easier
def set_si_weight
if self.weight_unit != nil
weight_string = "#{self.weight_quantity} #{self.weight_unit}"
self.si_weight = Unit(weight_string).convert_to("kg").to_s("%0.3f").delete(" kg").to_f
end
end
def cleanup_quantities
if quantity == 0
self.quantity = nil

View File

@@ -1,4 +1,5 @@
class ScientificName < ActiveRecord::Base
after_commit { |sn| sn.crop.__elasticsearch__.index_document if sn.crop }
belongs_to :crop
belongs_to :creator, :class_name => 'Member'
end

View File

@@ -1,6 +1,6 @@
- content_for :title, "Crops matching #{@search}"
- unless (@exact_match || @partial_matches.length > 0)
- if @all_matches.empty?
%h2 No results found
%p
Sorry, we couldn't find any crops that matched your search for "#{@search}".
@@ -9,18 +9,10 @@
= link_to "browsing our crop database", crops_path
instead.
- if @exact_match
%div#exact_match
%h2 Exact match
- else
%div#all_matches
.row
.col-md-2.six-across
= render :partial => "thumbnail", :locals => { :crop => @exact_match }
- if ! @partial_matches.empty?
%div#partial_matches
%h2 Partial matches
.row
- @partial_matches.each do |c|
- @all_matches.each do |c|
.col-md-2.six-across
= render :partial => "thumbnail", :locals => { :crop => c }

View File

@@ -10,6 +10,7 @@ csv.headers :id,
:unit,
:weight_quantity,
:weight_unit,
:si_weight,
:date_harvested,
:description,
:date_added,
@@ -31,7 +32,7 @@ csv.headers :id,
csv.cell :plant_part_id, h.plant_part ? h.plant_part.id : ''
csv.cell :plant_part_name, h.plant_part ? h.plant_part.to_s : ''
csv.cells :quantity, :unit, :weight_quantity, :weight_unit
csv.cells :quantity, :unit, :weight_quantity, :weight_unit, :si_weight
csv.cell :date_harvested, h.created_at.to_s(:db)

View File

@@ -62,6 +62,13 @@ GROWSTUFF_PAYPAL_USERNAME: "dummy"
GROWSTUFF_PAYPAL_PASSWORD: "dummy"
GROWSTUFF_PAYPAL_SIGNATURE: "dummy"
# Elasticsearch is used for flexible search and it requires another component
# to be installed. To make it easy for people who don't need to test this feature
# it's been turned off for test and development environment as a default.
# If you want to test this functionality, install elasticsearch and
# set this flag to "true".
GROWSTUFF_ELASTICSEARCH: "false"
##############################################################################
# Other environments
# You can override the above for staging, production, etc.
@@ -78,6 +85,8 @@ test:
staging:
GROWSTUFF_SITE_NAME: Growstuff (staging)
GROWSTUFF_ELASTICSEARCH: "true"
production:
GROWSTUFF_SITE_NAME: Growstuff
GROWSTUFF_ELASTICSEARCH: "true"

View File

@@ -0,0 +1,5 @@
class AddSiWeightToHarvest < ActiveRecord::Migration
def change
add_column :harvests, :si_weight, :float
end
end

View File

@@ -20,16 +20,16 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.string "name", null: false
t.boolean "is_paid"
t.boolean "is_permanent_paid"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "accounts", force: true do |t|
t.integer "member_id", null: false
t.integer "account_type_id"
t.datetime "paid_until"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "alternate_names", force: true do |t|
@@ -46,8 +46,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.string "uid"
t.string "token"
t.string "secret"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
end
@@ -57,15 +57,15 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.integer "post_id", null: false
t.integer "author_id", null: false
t.text "body", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "crops", force: true do |t|
t.string "name", null: false
t.string "en_wikipedia_url"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
t.integer "parent_id"
t.integer "plantings_count", default: 0
@@ -99,8 +99,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.string "name", null: false
t.text "description", null: false
t.integer "owner_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
end
@@ -110,8 +110,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.string "name", null: false
t.integer "owner_id"
t.string "slug", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.text "description"
t.boolean "active", default: true
t.string "location"
@@ -121,7 +121,7 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.string "area_unit"
end
add_index "gardens", ["owner_id"], name: "index_gardens_on_user_id", using: :btree
add_index "gardens", ["owner_id"], name: "index_gardens_on_owner_id", using: :btree
add_index "gardens", ["slug"], name: "index_gardens_on_slug", unique: true, using: :btree
create_table "gardens_photos", id: false, force: true do |t|
@@ -138,12 +138,13 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.decimal "quantity"
t.string "unit"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
t.decimal "weight_quantity"
t.string "weight_unit"
t.integer "plant_part_id"
t.float "si_weight"
end
create_table "harvests_photos", id: false, force: true do |t|
@@ -171,8 +172,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.integer "failed_attempts", default: 0
t.string "unlock_token"
t.datetime "locked_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "login_name"
t.string "slug"
t.boolean "tos_agreement"
@@ -187,11 +188,11 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.boolean "send_planting_reminder", default: true
end
add_index "members", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
add_index "members", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "members", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_index "members", ["slug"], name: "index_users_on_slug", unique: true, using: :btree
add_index "members", ["unlock_token"], name: "index_users_on_unlock_token", unique: true, using: :btree
add_index "members", ["confirmation_token"], name: "index_members_on_confirmation_token", unique: true, using: :btree
add_index "members", ["email"], name: "index_members_on_email", unique: true, using: :btree
add_index "members", ["reset_password_token"], name: "index_members_on_reset_password_token", unique: true, using: :btree
add_index "members", ["slug"], name: "index_members_on_slug", unique: true, using: :btree
add_index "members", ["unlock_token"], name: "index_members_on_unlock_token", unique: true, using: :btree
create_table "members_roles", id: false, force: true do |t|
t.integer "member_id"
@@ -205,8 +206,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.text "body"
t.boolean "read", default: false
t.integer "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "order_items", force: true do |t|
@@ -214,13 +215,13 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.integer "product_id"
t.integer "price"
t.integer "quantity"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "orders", force: true do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "completed_at"
t.integer "member_id"
t.string "paypal_express_token"
@@ -237,8 +238,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.integer "owner_id", null: false
t.string "thumbnail_url", null: false
t.string "fullsize_url", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "title", null: false
t.string "license_name", null: false
t.string "license_url"
@@ -253,8 +254,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
create_table "plant_parts", force: true do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
end
@@ -264,8 +265,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.date "planted_at"
t.integer "quantity"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
t.string "sunniness"
t.string "planted_from"
@@ -280,21 +281,22 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.integer "author_id", null: false
t.string "subject", null: false
t.text "body", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
t.integer "forum_id"
t.integer "parent_id"
end
add_index "posts", ["created_at", "author_id"], name: "index_updates_on_created_at_and_user_id", using: :btree
add_index "posts", ["slug"], name: "index_updates_on_slug", unique: true, using: :btree
add_index "posts", ["created_at", "author_id"], name: "index_posts_on_created_at_and_author_id", using: :btree
add_index "posts", ["slug"], name: "index_posts_on_slug", unique: true, using: :btree
create_table "products", force: true do |t|
t.string "name", null: false
t.text "description", null: false
t.integer "min_price", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "account_type_id"
t.integer "paid_months"
t.integer "recommended_price"
@@ -303,8 +305,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
create_table "roles", force: true do |t|
t.string "name", null: false
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "slug"
end
@@ -313,8 +315,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
create_table "scientific_names", force: true do |t|
t.string "scientific_name", null: false
t.integer "crop_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "creator_id"
end
@@ -324,8 +326,8 @@ ActiveRecord::Schema.define(version: 20150201064502) do
t.text "description"
t.integer "quantity"
t.date "plant_before"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "tradable_to", default: "nowhere"
t.string "slug"
end

View File

@@ -314,6 +314,20 @@ namespace :growstuff do
end
end
end
desc "January 2015: fill in si_weight column"
task :populate_si_weight => :environment do
Harvest.find_each do |h|
h.set_si_weight
h.save
end
end
desc "January 2015: build Elasticsearch index"
task :elasticsearch_create_index => :environment do
Crop.__elasticsearch__.create_index! force: true
Crop.import
end
end # end oneoff section
end

View File

@@ -18,3 +18,9 @@ rake growstuff:import_crops file=db/seeds/crops-15-squashes.csv
echo "2014-12-01 - load alternate names for crops"
rake growstuff:oneoff:add_alternate_names
echo "2015-01-28 - populate the harvest si_weight field"
rake growstuff:oneoff:populate_si_weight
echo "2015-01-30 - build Elasticsearch index"
rake growstuff:oneoff:elasticsearch_create_index

View File

@@ -11,7 +11,7 @@ feature "Requesting a new crop" do
scenario "Submit request" do
visit new_crop_path
fill_in "Name", with: "Couch potato"
fill_in "Comments", with: "Couch are real for real."
fill_in "Comments", with: "Couch potatoes are real for real."
click_button "Save"
expect(page).to have_content "Crop was successfully requested."
end

View File

@@ -7,12 +7,13 @@ feature "Harvesting a crop", :js => true do
background do
login_as(member)
visit new_harvest_path
sync_elasticsearch([maize])
end
it_behaves_like "crop suggest", "harvest", "crop"
scenario "Creating a new harvest", :js => true do
fill_autocomplete "crop", :with => "m"
fill_autocomplete "crop", :with => "mai"
select_from_autocomplete "maize"
within "form#new_harvest" do
fill_in "When?", :with => "2014-06-15"

View File

@@ -9,12 +9,13 @@ feature "Planting a crop", :js => true do
background do
login_as(member)
visit new_planting_path
sync_elasticsearch([maize])
end
it_behaves_like "crop suggest", "planting"
scenario "Creating a new planting" do
fill_autocomplete "crop", :with => "m"
fill_autocomplete "crop", :with => "mai"
select_from_autocomplete "maize"
within "form#new_planting" do
fill_in "When", :with => "2014-06-15"
@@ -41,7 +42,7 @@ feature "Planting a crop", :js => true do
end
scenario "Marking a planting as finished" do
fill_autocomplete "crop", :with => "m"
fill_autocomplete "crop", :with => "mai"
select_from_autocomplete "maize"
within "form#new_planting" do
fill_in "When?", :with => "2014-07-01"
@@ -77,7 +78,7 @@ feature "Planting a crop", :js => true do
end
scenario "Marking a planting as finished without a date" do
fill_autocomplete "crop", :with => "m"
fill_autocomplete "crop", :with => "mai"
select_from_autocomplete "maize"
within "form#new_planting" do
check "Mark as finished"

View File

@@ -7,12 +7,13 @@ feature "Seeds", :js => true do
background do
login_as(member)
visit new_seed_path
sync_elasticsearch([maize])
end
it_behaves_like "crop suggest", "seed", "crop"
scenario "Adding a new seed", :js => true do
fill_autocomplete "crop", :with => "m"
fill_autocomplete "crop", :with => "mai"
select_from_autocomplete "maize"
within "form#new_seed" do
fill_in "Quantity:", :with => 42

View File

@@ -1,29 +1,39 @@
require 'rails_helper'
shared_examples "crop suggest" do |resource|
let!(:popcorn) { FactoryGirl.create(:popcorn) }
let!(:pea) { FactoryGirl.create(:crop, :name => 'pea') }
let!(:pear) { FactoryGirl.create(:pear) }
let!(:tomato) { FactoryGirl.create(:tomato) }
let!(:roma) { FactoryGirl.create(:roma) }
background do
sync_elasticsearch([pea, pear, maize, tomato])
end
scenario "See text in crop auto suggest field" do
expect(page).to have_selector("input[placeholder='e.g. lettuce']")
end
scenario "Typing in the crop name displays suggestions" do
within "form#new_#{resource}" do
fill_autocomplete "crop", :with => "p"
fill_autocomplete "crop", :with => "pe"
end
expect(page).to_not have_content("pear")
expect(page).to_not have_content("pea")
within "form#new_#{resource}" do
fill_autocomplete "crop", :with => "pea"
end
expect(page).to have_content("pear")
expect(page).to have_content("popcorn")
expect(page).to have_content("pea")
within "form#new_#{resource}" do
fill_autocomplete "crop", :with => "pear"
end
expect(page).to have_content("pear")
expect(page).to_not have_content("popcorn")
select_from_autocomplete("pear")
@@ -32,16 +42,16 @@ shared_examples "crop suggest" do |resource|
scenario "Typing and pausing does not affect input" do
within "form#new_#{resource}" do
fill_autocomplete "crop", :with => "p"
fill_autocomplete "crop", :with => "pea"
end
expect(page).to have_content("pear")
expect(find_field("crop").value).to eq("p")
expect(find_field("crop").value).to eq("pea")
end
scenario "Searching for a crop casts a wide net on results" do
within "form#new_#{resource}" do
fill_autocomplete "crop", :with => "to"
fill_autocomplete "crop", :with => "tom"
end
expect(page).to have_content("tomato")

View File

@@ -90,9 +90,9 @@ describe Notifier do
end
it 'includes links to plant, harvest and stash seeds for the new crop' do
expect(mail.body.encoded).to match new_planting_url
expect(mail.body.encoded).to match new_harvest_url
expect(mail.body.encoded).to match new_seed_url
expect(mail.body.encoded).to match "#{new_planting_url}\\?crop_id=#{crop.id}"
expect(mail.body.encoded).to match "#{new_harvest_url}\\?crop_id=#{crop.id}"
expect(mail.body.encoded).to match "#{new_seed_url}\\?crop_id=#{crop.id}"
end
end

View File

@@ -328,6 +328,7 @@ describe Crop do
context "search" do
before :each do
@mushroom = FactoryGirl.create(:crop, :name => 'mushroom')
sync_elasticsearch([@mushroom])
end
it "finds exact matches" do
Crop.search('mushroom').should eq [@mushroom]

View File

@@ -118,6 +118,26 @@ describe Harvest do
end
end
context "standardized weights" do
it 'converts from pounds' do
@harvest = FactoryGirl.create(:harvest, :weight_quantity => 2, :weight_unit => "lb")
@harvest.should be_valid
@harvest.reload.si_weight.should eq 0.907
end
it 'converts from ounces' do
@harvest = FactoryGirl.create(:harvest, :weight_quantity => 16, :weight_unit => "oz")
@harvest.should be_valid
@harvest.reload.si_weight.should eq 0.454
end
it 'leaves kg alone' do
@harvest = FactoryGirl.create(:harvest, :weight_quantity => 2, :weight_unit => "kg")
@harvest.should be_valid
@harvest.reload.si_weight.should eq 2.0
end
end
context 'ordering' do
it 'lists most recent harvests first' do
@h1 = FactoryGirl.create(:harvest, :created_at => 1.day.ago)

View File

@@ -0,0 +1,18 @@
module ElasticsearchHelpers
def sync_elasticsearch(crops)
if ENV['GROWSTUFF_ELASTICSEARCH'] == "true"
crops.each {|crop| crop.__elasticsearch__.index_document}
Crop.__elasticsearch__.refresh_index!
end
end
end
RSpec.configure do |config|
config.include ElasticsearchHelpers
config.before(:each) do
if ENV['GROWSTUFF_ELASTICSEARCH'] == "true"
Crop.__elasticsearch__.create_index! force: true
end
end
end

View File

@@ -11,19 +11,18 @@ describe "crops/search" do
@tomato = FactoryGirl.create(:tomato)
@roma = FactoryGirl.create(:crop, :name => 'Roma tomato', :parent => @tomato)
assign(:search, 'tomato')
assign(:exact_match, @tomato)
assign(:partial_matches, [@roma])
assign(:all_matches, [@tomato, @roma])
render
end
it "shows exact matches" do
assert_select "div#exact_match" do
assert_select "div#all_matches" do
assert_select "a[href=#{crop_path(@tomato)}]"
end
end
it "shows partial matches" do
assert_select "div#partial_matches" do
assert_select "div#all_matches" do
assert_select "a[href=#{crop_path(@roma)}]"
end
end
@@ -31,8 +30,7 @@ describe "crops/search" do
context "no results" do
before :each do
assign(:exact_match, nil)
assign(:partial_matches, [])
assign(:all_matches, [])
assign(:search, 'tomato')
render
end