mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-02-02 13:41:00 -05:00
catch up with upstream dev
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
8
Gemfile
8
Gemfile
@@ -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
|
||||
|
||||
22
Gemfile.lock
22
Gemfile.lock
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 don’t 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
5
db/migrate/20150129034206_add_si_weight_to_harvest.rb
Normal file
5
db/migrate/20150129034206_add_si_weight_to_harvest.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AddSiWeightToHarvest < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :harvests, :si_weight, :float
|
||||
end
|
||||
end
|
||||
98
db/schema.rb
98
db/schema.rb
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
18
spec/support/elasticsearch_helpers.rb
Normal file
18
spec/support/elasticsearch_helpers.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user