diff --git a/Gemfile b/Gemfile index 00485c4f..596ebb42 100644 --- a/Gemfile +++ b/Gemfile @@ -43,13 +43,20 @@ gem 'neopets', :git => 'git://github.com/matchu/neopets.git' gem "mini_magick", "~> 3.4" -gem "fog", "~> 1.1.2" +gem "fog", "~> 1.8.0" gem "carrierwave", "~> 0.5.8" gem "parallel", "~> 0.5.17" gem "http_accept_language", :git => "git://github.com/iain/http_accept_language.git" +gem "globalize3" + +# My flex branch fixes a minor pagination bug. Once it's merged into the +# original gem, we can switch back. +gem "flex", :require => "flex/rails", :git => "git@github.com:matchu/flex.git" +gem "patron", "~> 0.4.18" + group :development do gem "bullet", "~> 4.1.5" end diff --git a/Gemfile.lock b/Gemfile.lock index 4d8c952b..600f57e0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,8 @@ GIT remote: git://github.com/eventmachine/eventmachine.git - revision: 69151c3ebb3e4ecf2bb9b6e2fab2022dc34f8541 + revision: d7c8a14dc494193a775add4b16c1e303cab5b285 specs: - eventmachine (1.0.0.beta.4) + eventmachine (1.0.0) GIT remote: git://github.com/iain/http_accept_language.git @@ -12,9 +12,9 @@ GIT GIT remote: git://github.com/igrigorik/em-http-request.git - revision: ce50f322ce08d43a4a747cf333ea576765d764c4 + revision: 322f2273fa7ea07c1eeb92755bf67f1f05058e54 specs: - em-http-request (1.0.1) + em-http-request (1.0.3) addressable (>= 2.2.3) cookiejar em-socksify @@ -30,21 +30,21 @@ GIT GIT remote: git://github.com/igrigorik/em-synchrony.git - revision: c7209a58f9eb92e1dc81fb141297f9f257c2fdcb + revision: fe592a4b9b5345bca329477cb8f2f8d186b6fc7f specs: - em-synchrony (1.0.0) + em-synchrony (1.0.2) eventmachine (>= 1.0.0.beta.1) GIT remote: git://github.com/matchu/neopets.git - revision: fe694681a302243d2937e811355473f358035403 + revision: d33aaf63d4617d9236ef0d99452b3bdc577cbc8e specs: - neopets (0.0.2) + neopets (0.1.0) nokogiri (~> 1.5.2) GIT remote: git://github.com/oldmoe/mysqlplus.git - revision: 3dbaa7c00ff0bb75ad9538cdef176c72de35d231 + revision: f07936d2eb9b0893994ed99fe82267b5e7770d06 specs: mysqlplus (0.1.1) @@ -54,6 +54,15 @@ GIT specs: RocketAMF (1.0.0) +GIT + remote: git@github.com:matchu/flex.git + revision: d62f508f795ecdbb383f406865daa72368b43ba5 + specs: + flex (0.4.1) + multi_json (~> 1.3.4) + progressbar (~> 0.11.0) + prompter (~> 0.1.5) + GEM remote: http://rubygems.org/ specs: @@ -88,17 +97,17 @@ GEM activemodel (= 3.0.19) activesupport (= 3.0.19) activesupport (3.0.19) - addressable (2.2.6) + addressable (2.3.2) arel (2.0.10) bcrypt-ruby (2.1.4) builder (2.1.2) - bullet (4.1.5) + bullet (4.1.6) uniform_notifier (~> 1.0.0) carrierwave (0.5.8) activesupport (~> 3.0) character-encodings (0.4.1) chronic (0.6.7) - closure-compiler (1.1.4) + closure-compiler (1.1.8) compass (0.10.6) haml (>= 3.0.4) cookiejar (0.3.0) @@ -106,27 +115,32 @@ GEM bcrypt-ruby (~> 2.1.2) warden (~> 1.0.2) diff-lcs (1.1.3) - em-socksify (0.1.0) - eventmachine + dye (0.1.4) + em-socksify (0.2.1) + eventmachine (>= 1.0.0.beta.4) erubis (2.6.6) abstract (>= 1.0.0) - excon (0.9.6) - factory_girl (2.3.2) - activesupport - factory_girl_rails (1.4.0) - factory_girl (~> 2.3.0) + excon (0.16.10) + factory_girl (2.6.4) + activesupport (>= 2.3.9) + factory_girl_rails (1.7.0) + factory_girl (~> 2.6.0) railties (>= 3.0.0) - fog (1.1.2) + fog (1.8.0) builder - excon (~> 0.9.0) + excon (~> 0.14) formatador (~> 0.2.0) mime-types - multi_json (~> 1.0.3) + multi_json (~> 1.0) net-scp (~> 1.0.4) net-ssh (>= 2.1.3) nokogiri (~> 1.5.0) ruby-hmac - formatador (0.2.1) + formatador (0.2.4) + globalize3 (0.3.0) + activemodel (>= 3.0.0) + activerecord (>= 3.0.0) + paper_trail (~> 2) haml (3.0.25) hoptoad_notifier (2.4.11) activesupport @@ -146,20 +160,28 @@ GEM mime-types (1.19) mini_magick (3.4) subexec (~> 0.2.1) - msgpack (0.4.6) - multi_json (1.0.4) - mysql2 (0.2.6) + msgpack (0.4.7) + multi_json (1.3.7) + mysql2 (0.2.18) net-scp (1.0.4) net-ssh (>= 1.99.1) - net-ssh (2.3.0) - newrelic_rpm (3.5.3.25) - nokogiri (1.5.3) + net-ssh (2.6.3) + newrelic_rpm (3.5.5.38) + nokogiri (1.5.6) open4 (1.3.0) openneo-auth-signatory (0.1.0) ruby-hmac - parallel (0.5.17) + paper_trail (2.7.0) + activerecord (~> 3.0) + railties (~> 3.0) + parallel (0.5.21) + patron (0.4.18) polyglot (0.3.3) - rack (1.2.6) + progressbar (0.11.0) + prompter (0.1.5) + dye (>= 0.1.1) + yard (>= 0.6.3) + rack (1.2.7) rack-fiber_pool (0.9.2) rack-mount (0.6.14) rack (>= 1.0.0) @@ -183,9 +205,9 @@ GEM rdiscount (1.6.8) rdoc (3.12) json (~> 1.4) - redis (2.2.2) - redis-namespace (1.1.0) - redis (< 3.0.0) + redis (3.0.2) + redis-namespace (1.2.1) + redis (~> 3.0.0) resque (1.15.0) json (~> 1.4.6) redis-namespace (>= 0.10.0) @@ -214,14 +236,14 @@ GEM rspec-rails (2.0.1) rspec (~> 2.0.0) ruby-hmac (0.4.0) - rufus-scheduler (2.0.16) + rufus-scheduler (2.0.17) tzinfo (>= 0.3.23) sanitize (2.0.3) nokogiri (>= 1.4.4, < 1.6) sinatra (1.2.8) rack (~> 1.1) tilt (>= 1.2.2, < 2.0) - subexec (0.2.1) + subexec (0.2.2) swf_converter (0.0.3) thor (0.14.6) tilt (1.3.3) @@ -230,14 +252,15 @@ GEM polyglot (>= 0.3.1) tzinfo (0.3.35) uniform_notifier (1.0.2) - vegas (0.1.8) + vegas (0.1.11) rack (>= 1.0.0) warden (1.0.6) rack (>= 1.0) whenever (0.7.3) activesupport (>= 2.3.4) chronic (~> 0.6.3) - will_paginate (3.0.2) + will_paginate (3.0.4) + yard (0.8.3) yui-compressor (0.9.6) POpen4 (>= 0.1.4) @@ -257,7 +280,9 @@ DEPENDENCIES em-synchrony! eventmachine! factory_girl_rails (~> 1.0) - fog (~> 1.1.2) + flex! + fog (~> 1.8.0) + globalize3 haml (~> 3.0.18) hoptoad_notifier http_accept_language! @@ -272,6 +297,7 @@ DEPENDENCIES nokogiri (~> 1.5.2) openneo-auth-signatory (~> 0.1.0) parallel (~> 0.5.17) + patron (~> 0.4.18) rack-fiber_pool rails (= 3.0.19) rdiscount (~> 1.6.5) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index aa8edccd..76d8467d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -57,7 +57,7 @@ class ApplicationController < ActionController::Base end def valid_locale?(locale) - locale && I18n.available_locales.include?(locale.to_sym) + locale && I18n.usable_locales.include?(locale.to_sym) end end diff --git a/app/controllers/closet_hangers_controller.rb b/app/controllers/closet_hangers_controller.rb index b8849ac3..0720a105 100644 --- a/app/controllers/closet_hangers_controller.rb +++ b/app/controllers/closet_hangers_controller.rb @@ -188,15 +188,17 @@ class ClosetHangersController < ApplicationController def find_closet_lists_by_owned(closet_lists) return {} if closet_lists == [] - closet_lists.alphabetical.includes(:hangers => :item). + closet_lists.alphabetical.includes(:hangers => {:item => :translations}). group_by(&:hangers_owned) end def find_unlisted_closet_hangers_by_owned(visible_groups) unless visible_groups.empty? @user.closet_hangers.unlisted. - owned_before_wanted.alphabetical_by_item_name.includes(:item). - where(:owned => [visible_groups]).group_by(&:owned) + owned_before_wanted.alphabetical_by_item_name. + includes(:item => :translations). + where(:owned => [visible_groups]). + group_by(&:owned) else {} end diff --git a/app/controllers/item_zone_sets_controller.rb b/app/controllers/item_zone_sets_controller.rb index cbfcdabb..92282c02 100644 --- a/app/controllers/item_zone_sets_controller.rb +++ b/app/controllers/item_zone_sets_controller.rb @@ -1,7 +1,5 @@ class ItemZoneSetsController < ApplicationController - caches_page :index - def index - render :json => Zone::ItemZoneSets.keys.sort.as_json + render :json => Zone.all_plain_labels end end diff --git a/app/controllers/items_controller.rb b/app/controllers/items_controller.rb index b753dfd6..c54105d8 100644 --- a/app/controllers/items_controller.rb +++ b/app/controllers/items_controller.rb @@ -1,5 +1,6 @@ class ItemsController < ApplicationController before_filter :set_query + rescue_from Item::Search::Error, :with => :search_error def index if params.has_key?(:q) @@ -8,25 +9,21 @@ class ItemsController < ApplicationController per_page = params[:per_page].to_i per_page = 50 if per_page && per_page > 50 else - per_page = nil + per_page = 30 end - @items = Item.search(@query, current_user).alphabetize.paginate :page => params[:page], :per_page => per_page + # Note that we sort by name by hand, since we might have to use + # fallbacks after the fact + @items = Item::Search::Query.from_text(@query, current_user). + paginate(:page => params[:page], :per_page => per_page) assign_closeted! respond_to do |format| format.html { render } format.json { render :json => {:items => @items, :total_pages => @items.total_pages} } format.js { render :json => {:items => @items, :total_pages => @items.total_pages}, :callback => params[:callback] } end - rescue Item::SearchError - @items = [] - respond_to do |format| - format.html { flash.now[:alert] = $!.message } - format.json { render :json => {:error => $!.message} } - format.js { render :json => {:error => $!.message}, :callback => params[:callback] } - end end elsif params.has_key?(:ids) && params[:ids].is_a?(Array) - @items = Item.find(params[:ids]) + @items = Item.includes(:translations).find(params[:ids]) assign_closeted! respond_to do |format| format.json { render :json => @items } @@ -35,7 +32,7 @@ class ItemsController < ApplicationController respond_to do |format| format.html { unless localized_fragment_exist?('items#index newest_items') - @newest_items = Item.newest.limit(18) + @newest_items = Item.newest.includes(:translations).limit(18) end } format.js { render :json => {:error => '$q required'}} @@ -48,6 +45,15 @@ class ItemsController < ApplicationController respond_to do |format| format.html do + unless localized_fragment_exist?("items/#{@item.id} info") + @occupied_zones = @item.occupied_zones( + :scope => Zone.includes_translations.alphabetical + ) + @restricted_zones = @item.restricted_zones( + :scope => Zone.includes_translations.alphabetical + ) + end + unless localized_fragment_exist?("items/#{@item.id} contributors") @contributors_with_counts = @item.contributors_with_counts end @@ -96,7 +102,8 @@ class ItemsController < ApplicationController raise ActiveRecord::RecordNotFound, 'Pet type not found' end - @items = @pet_type.needed_items.alphabetize + @items = @pet_type.needed_items.includes(:translations). + alphabetize_by_translations assign_closeted! respond_to do |format| @@ -110,6 +117,16 @@ class ItemsController < ApplicationController def assign_closeted! current_user.assign_closeted_to_items!(@items) if user_signed_in? end + + def search_error(e) + @items = [] + respond_to do |format| + format.html { flash.now[:alert] = e.message; render } + format.json { render :json => {:error => e.message} } + format.js { render :json => {:error => e.message}, + :callback => params[:callback] } + end + end def set_query @query = params[:q] diff --git a/app/controllers/outfits_controller.rb b/app/controllers/outfits_controller.rb index e20b8a70..1f0e2bf6 100644 --- a/app/controllers/outfits_controller.rb +++ b/app/controllers/outfits_controller.rb @@ -42,12 +42,13 @@ class OutfitsController < ApplicationController def new unless localized_fragment_exist?(:action_suffix => 'start_from_scratch_form_content') - @colors = Color.all_ordered_by_name - @species = Species.all_ordered_by_name + @colors = Color.alphabetical + @species = Species.alphabetical end unless localized_fragment_exist?('outfits#new newest_items') - @newest_items = Item.newest.select([:id, :name, :thumbnail_url]).limit(9) + @newest_items = Item.newest.select([:id, :name, :thumbnail_url]). + includes(:translations).limit(9) end unless localized_fragment_exist?('outfits#new latest_contribution') @@ -65,8 +66,13 @@ class OutfitsController < ApplicationController end def start + # Start URLs are always in English, so let's make sure we search in + # English. + I18n.locale = I18n.default_locale + @species = Species.find_by_name params[:species_name] @color = Color.find_by_name params[:color_name] + if @species && @color redirect_to wardrobe_path(:species => @species.id, :color => @color.id) else diff --git a/app/controllers/pet_attributes_controller.rb b/app/controllers/pet_attributes_controller.rb index 731177f4..b7d6a641 100644 --- a/app/controllers/pet_attributes_controller.rb +++ b/app/controllers/pet_attributes_controller.rb @@ -1,10 +1,8 @@ class PetAttributesController < ApplicationController - caches_page :index - def index render :json => { - :color => Color.all_ordered_by_name, - :species => Species.all_ordered_by_name + :color => Color.alphabetical, + :species => Species.alphabetical } end end diff --git a/app/controllers/pets_controller.rb b/app/controllers/pets_controller.rb index 6e5fc0aa..62f6fb39 100644 --- a/app/controllers/pets_controller.rb +++ b/app/controllers/pets_controller.rb @@ -10,13 +10,16 @@ class PetsController < ApplicationController redirect_to roulette_path else raise Pet::PetNotFound unless params[:name] - @pet = Pet.load(params[:name]) + @pet = Pet.load(params[:name], :item_scope => Item.includes(:translations)) if user_signed_in? points = current_user.contribute! @pet else @pet.save points = true end + + @pet.translate_items + respond_to do |format| format.html do path = destination + @pet.wardrobe_query diff --git a/app/controllers/sitemap_controller.rb b/app/controllers/sitemap_controller.rb index 3b5c270c..d65708a4 100644 --- a/app/controllers/sitemap_controller.rb +++ b/app/controllers/sitemap_controller.rb @@ -3,7 +3,7 @@ class SitemapController < ApplicationController def index respond_to do |format| - format.xml { @items = Item.sitemap } + format.xml { @items = Item.includes(:translations).sitemap } end end end diff --git a/app/controllers/swf_assets_controller.rb b/app/controllers/swf_assets_controller.rb index c7b00657..0af82690 100644 --- a/app/controllers/swf_assets_controller.rb +++ b/app/controllers/swf_assets_controller.rb @@ -2,7 +2,7 @@ class SwfAssetsController < ApplicationController def index if params[:item_id] item = Item.find(params[:item_id]) - @swf_assets = item.swf_assets + @swf_assets = item.swf_assets.includes_depth if params[:body_id] @swf_assets = @swf_assets.fitting_body_id(params[:body_id]) else @@ -15,33 +15,32 @@ class SwfAssetsController < ApplicationController end elsif params[:pet_type_id] && params[:item_ids] pet_type = PetType.find(params[:pet_type_id], :select => [:body_id, :species_id]) - items = Item.find(params[:item_ids], :select => [:id, :species_support_ids]) - compatible_items = items.select { |i| i.support_species?(pet_type.species) } - compatible_item_ids = compatible_items.map(&:id) - @swf_assets = SwfAsset.object_assets. + @swf_assets = SwfAsset.object_assets.includes_depth. fitting_body_id(pet_type.body_id). - for_item_ids(compatible_item_ids). + for_item_ids(params[:item_ids]). with_parent_ids json = @swf_assets.map { |a| a.as_json(:parent_id => a.parent_id.to_i, :for => 'wardrobe') } elsif params[:pet_state_id] - @swf_assets = PetState.find(params[:pet_state_id]).swf_assets.all + @swf_assets = PetState.find(params[:pet_state_id]).swf_assets. + includes_depth.all pet_state_id = params[:pet_state_id].to_i json = @swf_assets.map { |a| a.as_json(:parent_id => pet_state_id, :for => 'wardrobe') } elsif params[:pet_type_id] - @swf_assets = PetType.find(params[:pet_type_id]).pet_states.emotion_order.first.swf_assets + @swf_assets = PetType.find(params[:pet_type_id]).pet_states.emotion_order + .first.swf_assets.includes_depth elsif params[:ids] @swf_assets = [] if params[:ids][:biology] - @swf_assets += SwfAsset.biology_assets.where(:remote_id => params[:ids][:biology]).all + @swf_assets += SwfAsset.includes_depth.biology_assets.where(:remote_id => params[:ids][:biology]).all end if params[:ids][:object] - @swf_assets += SwfAsset.object_assets.where(:remote_id => params[:ids][:object]).all + @swf_assets += SwfAsset.includes_depth.object_assets.where(:remote_id => params[:ids][:object]).all end elsif params[:body_id] && params[:item_ids] # DEPRECATED in favor of pet_type_id and item_ids swf_assets = SwfAsset.arel_table - @swf_assets = SwfAsset.object_assets. + @swf_assets = SwfAsset.includes_depth.object_assets. select('swf_assets.*, parents_swf_assets.parent_id'). fitting_body_id(params[:body_id]). for_item_ids(params[:item_ids]) diff --git a/app/flex/flex_search.rb b/app/flex/flex_search.rb new file mode 100644 index 00000000..23f88cff --- /dev/null +++ b/app/flex/flex_search.rb @@ -0,0 +1,18 @@ +# inspect the methods loaded in this module and their usage +# in the rails console by just typing: +# >> puts FlexSearch.flex.info +# you can eventually restrict the info to a single method by pasing its name: +# >> puts FlexSearch.flex.info :search +# see the detailed doc for this feature at https://github.com/ddnexus/flex/wiki/Selfdoc + +module FlexSearch + + extend self + + include Flex::Loader + flex.load_search_source + + # you may need to add more method here, usually wrapper methods + # that use one of the autogenerated methods from the loaded templates + +end diff --git a/app/flex/flex_search.yml b/app/flex/flex_search.yml new file mode 100644 index 00000000..15168564 --- /dev/null +++ b/app/flex/flex_search.yml @@ -0,0 +1,83 @@ +# Add here your search queries +# see the detailed Source documentation at https://github.com/ddnexus/flex/wiki/Sources + + +# ANCHORS litheral key: it will not be used as template +# you can store here fragments of queries to reuse in the templates below +ANCHORS: + - &name_partial + multi_match: + query: <> + fields: <> + type: phrase + - &species_support_id_partial + term: + species_support_id: <> + - &occupied_zone_id_partial + terms: + occupied_zone_id: <> + - &restricted_zone_id_partial + terms: + restricted_zone_id: <> + - &user_closet_hangers_ownership_partial + has_child: + type: closet_hanger + query: + bool: + must: + - term: + user_id: <> + - term: + owned: <> + +_names: + *name_partial + +_negative_names: + *name_partial + +_species_support_ids: + *species_support_id_partial + +_negative_species_support_ids: + *species_support_id_partial + +_occupied_zone_ids: + *occupied_zone_id_partial + +_negative_occupied_zone_ids: + *occupied_zone_id_partial + +_restricted_zone_ids: + *restricted_zone_id_partial + +_negative_restricted_zone_ids: + *restricted_zone_id_partial + +_user_closet_hanger_ownerships: + *user_closet_hangers_ownership_partial + +_negative_user_closet_hanger_ownerships: + *user_closet_hangers_ownership_partial + +item_search: + - query: + bool: + must: + - term: + is_nc: <> + - term: + is_pb: <> + - <<_names= ~>> + - <<_species_support_ids= ~>> + - <<_occupied_zone_ids= ~>> + - <<_restricted_zone_ids= ~>> + - <<_user_closet_hanger_ownerships= ~>> + must_not: + - <<_negative_names= ~>> + - <<_negative_species_support_ids= ~>> + - <<_negative_occupied_zone_ids= ~>> + - <<_negative_restricted_zone_ids= ~>> + - <<_negative_user_closet_hanger_ownerships= ~>> + sort: + - name.<>.untouched diff --git a/app/flex/flex_search_extender.rb b/app/flex/flex_search_extender.rb new file mode 100644 index 00000000..89cf1c85 --- /dev/null +++ b/app/flex/flex_search_extender.rb @@ -0,0 +1,36 @@ +# see the detailed Extenders documentation at https://github.com/ddnexus/flex/wiki/Extenders + +module FlexSearchExtender + + # set this method to restrict this extender to certain types of results + # see the other Flex extenders for reference (https://github.com/ddnexus/flex/tree/master/lib/flex/result) + def self.should_extend?(response) + true + end + + def scoped_loaded_collection(options) + options[:scopes] ||= {} + @loaded_collection ||= begin + records = [] + # returns a structure like {Comment=>[{"_id"=>"123", ...}, {...}], BlogPost=>[...]} + h = Flex::Utils.group_array_by(collection) do |d| + d.mapped_class(should_raise=true) + end + h.each do |klass, docs| + scope = options[:scopes][klass.name] || klass.scoped + records |= scope.find(docs.map(&:_id)) + end + class_ids = collection.map { |d| [d.mapped_class.to_s, d._id] } + # Reorder records to preserve order from search results + records = class_ids.map do |class_str, id| + records.detect do |record| + record.class.to_s == class_str && record.id.to_s == id.to_s + end + end + records.extend Flex::Result::Collection + records.setup(self['hits']['total'], variables) + records + end + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 45bc2402..8275720f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -43,7 +43,10 @@ module ApplicationHelper end def canonical_path(resource) + original_locale = I18n.locale + I18n.locale = I18n.default_locale content_for :meta, tag(:link, :rel => 'canonical', :href => url_for(resource)) + I18n.locale = original_locale end def contact_email @@ -100,9 +103,18 @@ module ApplicationHelper end def locale_options - I18n.available_locales.map do |available_locale| + current_locale_is_public = false + options = I18n.public_locales.map do |available_locale| + current_locale_is_public = true if I18n.locale == available_locale [translate('locale_name', :locale => available_locale), available_locale] end + + unless current_locale_is_public + name = translate('locale_name', :locale => I18n.locale) + ' (alpha)' + options << [name, I18n.locale] + end + + options end def localized_cache(key={}, &block) diff --git a/app/helpers/items_helper.rb b/app/helpers/items_helper.rb index 47662b54..e7343564 100644 --- a/app/helpers/items_helper.rb +++ b/app/helpers/items_helper.rb @@ -20,7 +20,7 @@ module ItemsHelper end def standard_species_search_links - build_on_pet_types(Species.all) do |pet_type| + build_on_pet_types(Species.alphabetical) do |pet_type| image = pet_type_image(pet_type, :happy, :zoom) query = "species:#{pet_type.species.name}" link_to(image, items_path(:q => query)) @@ -73,7 +73,7 @@ module ItemsHelper end def list_zones(zones, method=:label) - zones.sort { |x,y| x.label <=> y.label }.map(&method).join(', ') + zones.map(&method).join(', ') end def nc_icon @@ -125,7 +125,8 @@ module ItemsHelper def build_on_pet_types(species, special_color=nil, &block) species_ids = species.map(&:id) pet_types = special_color ? - PetType.where(:color_id => special_color.id, :species_id => species_ids).order(:species_id) : + PetType.where(:color_id => special_color.id, :species_id => species_ids). + order(:species_id).includes_child_translations : PetType.random_basic_per_species(species.map(&:id)) pet_types.map(&block).join.html_safe end diff --git a/app/models/closet_hanger.rb b/app/models/closet_hanger.rb index 99fe30f8..ad3dde76 100644 --- a/app/models/closet_hanger.rb +++ b/app/models/closet_hanger.rb @@ -1,4 +1,6 @@ class ClosetHanger < ActiveRecord::Base + include Flex::Model + belongs_to :item belongs_to :list, :class_name => 'ClosetList' belongs_to :user @@ -29,6 +31,17 @@ class ClosetHanger < ActiveRecord::Base end before_validation :merge_quantities, :set_owned_by_list + + flex.parent :item, 'item' => 'closet_hanger' + flex.sync self + + def flex_source + { + :user_id => user_id, + :item_id => item_id, + :owned => owned? + }.to_json + end def verb(subject=:someone) self.class.verb(subject, owned?) @@ -56,23 +69,28 @@ class ClosetHanger < ActiveRecord::Base end hanger = self.where(conditions).first - unless hanger - hanger = self.new - hanger.user_id = conditions[:user_id] - hanger.item_id = conditions[:item_id] - # One of the following will be nil, and that's okay. If owned is nil, - # we'll cover for it before validation, as always. - hanger.owned = conditions[:owned] - hanger.list_id = conditions[:list_id] - end - unless quantity == 0 - Rails.logger.debug("Logging to #{hanger.id} quantity #{quantity}") + if quantity > 0 + # If quantity is non-zero, create/update the corresponding hanger. + + unless hanger + hanger = self.new + hanger.user_id = conditions[:user_id] + hanger.item_id = conditions[:item_id] + # One of the following will be nil, and that's okay. If owned is nil, + # we'll cover for it before validation, as always. + hanger.owned = conditions[:owned] + hanger.list_id = conditions[:list_id] + end + hanger.quantity = quantity hanger.save! - else - hanger.destroy if hanger + elsif hanger + # If quantity is zero and there's a hanger, destroy it. + hanger.destroy end + + # If quantity is zero and there's no hanger, good. Do nothing. end protected diff --git a/app/models/color.rb b/app/models/color.rb index 624466a3..792dc21b 100644 --- a/app/models/color.rb +++ b/app/models/color.rb @@ -1,15 +1,16 @@ -class Color < PetAttribute - fetch_objects! +class Color < ActiveRecord::Base + translates :name - Basic = %w(blue green red yellow).map { |name| find_by_name(name) } - BasicIds = Basic.map(&:id) + scope :alphabetical, lambda { includes(:translations).order(Color::Translation.arel_table[:name]) } + scope :basic, where(:basic => true) + scope :standard, where(:standard => true) + scope :nonstandard, where(:standard => false) - def self.basic_ids - BasicIds + def as_json(options={}) + {:id => id, :name => human_name} end - def self.nonstandard_ids - @nonstandard_ids ||= File.read(Rails.root.join('config', 'nonstandard_colors.txt')). - chomp.split("\n").map { |name| Color.find_by_name(name).id } + def human_name + name.split(' ').map { |word| word.capitalize }.join(' ') end end diff --git a/app/models/item.rb b/app/models/item.rb index 11a5c314..404b1028 100644 --- a/app/models/item.rb +++ b/app/models/item.rb @@ -1,7 +1,12 @@ class Item < ActiveRecord::Base + include Flex::Model include PrettyParam + + set_inheritance_column 'inheritance_type' # PHP Impress used "type" to describe category SwfAssetType = 'object' + + translates :name, :description, :rarity has_many :closet_hangers has_one :contribution, :as => :contributed @@ -15,18 +20,16 @@ class Item < ActiveRecord::Base SPECIAL_COLOR_DESCRIPTION_REGEX = /This item is only wearable by Neopets painted ([a-zA-Z]+)\.|WARNING: This [a-zA-Z]+ can be worn by ([a-zA-Z]+) [a-zA-Z]+ ONLY!/ - SPECIAL_PAINTBRUSH_COLORS_PATH = Rails.root.join('config', 'colors_with_unique_bodies.txt') - SPECIAL_PAINTBRUSH_COLORS = File.read(SPECIAL_PAINTBRUSH_COLORS_PATH).split("\n").map { |name| Color.find_by_name(name) } - - set_table_name 'objects' # Neo & PHP Impress call them objects, but the class name is a conflict (duh!) - set_inheritance_column 'inheritance_type' # PHP Impress used "type" to describe category - cattr_reader :per_page @@per_page = 30 - scope :alphabetize, order('name ASC') + scope :alphabetize, order(arel_table[:name]) + scope :alphabetize_by_translations, lambda { + it = Item::Translation.arel_table + order(it[:name]) + } - scope :join_swf_assets, joins(:swf_assets).group('objects.id') + scope :join_swf_assets, joins(:swf_assets).group(arel_table[:id]) scope :newest, order(arel_table[:created_at].desc) if arel_table[:created_at] @@ -35,9 +38,31 @@ class Item < ActiveRecord::Base scope :sold_in_mall, where(:sold_in_mall => true) scope :not_sold_in_mall, where(:sold_in_mall => false) - scope :sitemap, select([:id, :name]).order(:id).limit(49999) + scope :sitemap, select([arel_table[:id], arel_table[:name]]). + order(arel_table[:id]).limit(49999) scope :with_closet_hangers, joins(:closet_hangers) + + flex.sync self + + def flex_source + indexed_attributes = { + :is_nc => self.nc?, + :is_pb => self.pb?, + :species_support_id => self.species_support_ids, + :occupied_zone_id => self.occupied_zone_ids, + :restricted_zone_id => self.restricted_zone_ids, + :name => {} + } + + I18n.usable_locales_with_neopets_language_code.each do |locale| + I18n.with_locale(locale) do + indexed_attributes[:name][locale] = self.name + end + end + + indexed_attributes.to_json + end def closeted? @owned || @wanted @@ -72,6 +97,10 @@ class Item < ActiveRecord::Base def nc? NCRarities.include?(rarity_index) end + + def pb? + (self.description == PAINTBRUSH_SET_DESCRIPTION) + end def owned? @owned @@ -81,17 +110,27 @@ class Item < ActiveRecord::Base @wanted end - def restricted_zones - unless @restricted_zones - @restricted_zones = [] + def restricted_zones(options={}) + options[:scope] ||= Zone.scoped + options[:scope].find(restricted_zone_ids) + end + + def restricted_zone_ids + unless @restricted_zone_ids + @restricted_zone_ids = [] zones_restrict.split(//).each_with_index do |switch, id| - @restricted_zones << Zone.find(id.to_i + 1) if switch == '1' + @restricted_zone_ids << (id.to_i + 1) if switch == '1' end end - @restricted_zones + @restricted_zone_ids + end + + def occupied_zone_ids + occupied_zones.map(&:id) end - def occupied_zones + def occupied_zones(options={}) + options[:scope] ||= Zone.scoped all_body_ids = [] zone_body_ids = {} selected_assets = swf_assets.select('body_id, zone_id').each do |swf_asset| @@ -100,12 +139,11 @@ class Item < ActiveRecord::Base body_ids << swf_asset.body_id unless body_ids.include?(swf_asset.body_id) all_body_ids << swf_asset.body_id unless all_body_ids.include?(swf_asset.body_id) end - zones = [] + zones = options[:scope].find(zone_body_ids.keys) + zones_by_id = zones.inject({}) { |h, z| h[z.id] = z; h } total_body_ids = all_body_ids.size zone_body_ids.each do |zone_id, body_ids| - zone = Zone.find(zone_id) - zone.sometimes = true if body_ids.size < total_body_ids - zones << zone + zones_by_id[zone_id].sometimes = true if body_ids.size < total_body_ids end zones end @@ -120,17 +158,21 @@ class Item < ActiveRecord::Base protected def determine_special_color - if description.include?(PAINTBRUSH_SET_DESCRIPTION) - downcased_name = name.downcase - SPECIAL_PAINTBRUSH_COLORS.each do |color| - return color if downcased_name.include?(color.name) + I18n.with_locale(I18n.default_locale) do + # Rather than go find the special description in all locales, let's just + # run this logic in English. + if description.include?(PAINTBRUSH_SET_DESCRIPTION) + downcased_name = name.downcase + Color.nonstandard.each do |color| + return color if downcased_name.include?(color.name) + end end - end - match = description.match(SPECIAL_COLOR_DESCRIPTION_REGEX) - if match - color = match[1] || match[2] - return Color.find_by_name(color.downcase) + match = description.match(SPECIAL_COLOR_DESCRIPTION_REGEX) + if match + color = match[1] || match[2] + return Color.find_by_name(color.downcase) + end end end public @@ -146,45 +188,18 @@ class Item < ActiveRecord::Base end def supported_species - @supported_species ||= species_support_ids.blank? ? Species.all : species_support_ids.sort.map { |id| Species.find(id) } + body_ids = swf_assets.select([:body_id]).map(&:body_id) + return Species.all if body_ids.include?(0) + + pet_types = PetType.where(:body_id => body_ids).select([:species_id]) + species_ids = pet_types.map(&:species_id).uniq + Species.find(species_ids) end def support_species?(species) species_support_ids.blank? || species_support_ids.include?(species.id) end - def self.search(query, user=nil) - raise SearchError, "Please provide a search query" unless query - query = query.strip - raise SearchError, "Search queries should be at least 3 characters" if query.length < 3 - query_conditions = [Condition.new] - in_phrase = false - query.each_char do |c| - if c == ' ' && !in_phrase - query_conditions << Condition.new - elsif c == '"' - in_phrase = !in_phrase - elsif c == ':' && !in_phrase - query_conditions.last.to_filter! - elsif c == '-' && !in_phrase && query_conditions.last.empty? - query_conditions.last.negate! - else - query_conditions.last << c - end - end - limited_filters_used = [] - query_conditions.inject(self.scoped) do |scope, condition| - if condition.filter? && LimitedSearchFilters.include?(condition.filter) - if limited_filters_used.include?(condition.filter) - raise SearchError, "The #{condition.filter} filter is complex; please only use one per search. Thanks!" - else - limited_filters_used << condition.filter - end - end - condition.narrow(scope, user) - end - end - def as_json(options = {}) { :description => description, @@ -230,6 +245,12 @@ class Item < ActiveRecord::Base end end end + + def body_specific? + # If there are species support IDs (it's not empty), the item is + # body-specific. If it's empty, it fits everyone the same. + !species_support_ids.empty? + end def origin_registry_info=(info) # bear in mind that numbers from registries are floats @@ -252,6 +273,12 @@ class Item < ActiveRecord::Base def parent_swf_asset_relationships_to_update=(rels) @parent_swf_asset_relationships_to_update = rels end + + def needed_translations + translatable_locales = Set.new(I18n.locales_with_neopets_language_code) + translated_locales = Set.new(translations.map(&:locale)) + translatable_locales - translated_locales + end def self.all_by_ids_or_children(ids, swf_assets) swf_asset_ids = [] @@ -284,7 +311,7 @@ class Item < ActiveRecord::Base end end - def self.collection_from_pet_type_and_registries(pet_type, info_registry, asset_registry) + def self.collection_from_pet_type_and_registries(pet_type, info_registry, asset_registry, scope=Item.scoped) # bear in mind that registries are arrays with many nil elements, # due to how the parser works @@ -299,7 +326,7 @@ class Item < ActiveRecord::Base # Collect existing relationships existing_relationships_by_item_id_and_swf_asset_id = {} - existing_items = Item.find_all_by_id(item_ids, :include => :parent_swf_asset_relationships) + existing_items = scope.find_all_by_id(item_ids, :include => :parent_swf_asset_relationships) existing_items.each do |item| items[item.id] = item relationships_by_swf_asset_id = {} @@ -656,214 +683,4 @@ class Item < ActiveRecord::Base class SpiderHTTPError < SpiderError;end class SpiderJSONError < SpiderError;end end - - private - - SearchFilterScopes = [] - LimitedSearchFilters = [] - - def self.search_filter(name, options={}, &block) - assume_complement = options.delete(:assume_complement) || true - name = name.to_s - SearchFilterScopes << name - LimitedSearchFilters << name if options[:limit] - - (class << self; self; end).instance_eval do - if options[:full] - define_method "search_filter_#{name}", &options[:full] - else - if assume_complement - define_method "search_filter_not_#{name}", &Item.search_filter_block(options, false, &block) - end - define_method "search_filter_#{name}", &Item.search_filter_block(options, true, &block) - end - end - end - - def self.single_search_filter(name, options={}, &block) - options[:assume_complement] = false - search_filter name, options, &block - end - - def self.search_filter_block(options, positive, &block) - Proc.new { |str, user, scope| - condition = block.arity == 1 ? block.call(str) : block.call(str, user) - unless positive - condition = condition.to_sql if condition.respond_to?(:to_sql) - condition = "!(#{condition})" - end - scope = scope.send(options[:scope]) if options[:scope] - scope.where(condition) - } - end - - search_filter :name do |name| - arel_table[:name].matches("%#{name}%") - end - - search_filter :description do |description| - arel_table[:description].matches("%#{description}%") - end - - def self.adjective_filters - @adjective_filters ||= { - 'nc' => arel_table[:rarity_index].in(NCRarities), - 'pb' => arel_table[:description].eq(PAINTBRUSH_SET_DESCRIPTION) - } - end - - search_filter :is do |adjective| - filter = adjective_filters[adjective] - unless filter - raise SearchError, - "We don't know how an item can be \"#{adjective}\". " + - "Did you mean is:nc or is:pb?" - end - filter - end - - USER_ADJECTIVES = { - 'own' => true, - 'owns' => true, - 'owned' => true, - 'want' => false, - 'wants' => false, - 'wanted' => false, - 'all' => nil, - 'items' => nil - } - def self.parse_user_adjective(adjective, user) - unless USER_ADJECTIVES.has_key?(adjective) - raise SearchError, "We don't understand user:#{adjective}. " + - "Find items you own with user:owns, items you want with user:wants, or " + - "both with user:all" - end - - unless user - raise SearchError, "It looks like you're not logged in, so you don't own any items." - end - - USER_ADJECTIVES[adjective] - end - - search_filter :user do |adjective, user| - # Though joins may seem more efficient here for the positive case, we need - # to be able to handle cases like "user:owns user:wants", which breaks on - # the JOIN approach. Just have to look up the IDs in advance. - - owned_value = parse_user_adjective(adjective, user) - hangers = ClosetHanger.arel_table - items = user.closeted_items - items = items.where(ClosetHanger.arel_table[:owned].eq(owned_value)) unless owned_value.nil? - item_ids = items.map(&:id) - # Though it's best to do arel_table[:id].in(item_ids), it breaks in this - # version of Arel, and other conditions will overwrite this one. Since IDs - # are guaranteed to be integers, let's just build our own string condition - # and be done with it. - - if item_ids.empty? - raise SearchError, "You don't #{ClosetHanger.verb :you, owned_value} " + - "any items yet. Head to Your Items to add some!" - end - - arel_table[:id].in(item_ids) - end - - search_filter :only do |species_name| - begin - id = Species.require_by_name(species_name).id - rescue Species::NotFound => e - raise SearchError, e.message - end - arel_table[:species_support_ids].eq(id.to_s) - end - - search_filter :species do |species_name| - begin - id = Species.require_by_name(species_name).id - rescue Species::NotFound => e - raise SearchError, e.message - end - ids = arel_table[:species_support_ids] - ids.eq('').or(ids.matches_any([ - id, - "#{id},%", - "%,#{id},%", - "%,#{id}" - ])) - end - - single_search_filter :type, {:limit => true, :scope => :join_swf_assets} do |zone_set_name| - zone_set = Zone.find_set(zone_set_name) - raise SearchError, "Type \"#{zone_set_name}\" does not exist" unless zone_set - SwfAsset.arel_table[:zone_id].in(zone_set.map(&:id)) - end - - single_search_filter :not_type, :full => lambda { |zone_set_name, user, scope| - zone_set = Zone::ItemZoneSets[zone_set_name] - raise SearchError, "Type \"#{zone_set_name}\" does not exist" unless zone_set - psa = ParentSwfAssetRelationship.arel_table.alias - sa = SwfAsset.arel_table.alias - # Join to SWF assets, including the zone condition in the join so that - # SWFs that don't match end up being NULL rows. Then we take the max SWF - # asset ID, which is NULL if and only if there are no rows that matched - # the zone requirement. If that max was NULL, return the object. - item_ids = select(arel_table[:id]).joins( - "LEFT JOIN #{ParentSwfAssetRelationship.table_name} #{psa.name} ON " + - psa[:parent_type].eq(self.name). - and(psa[:parent_id].eq(arel_table[:id])). - to_sql - ). - joins( - "LEFT JOIN #{SwfAsset.table_name} #{sa.name} ON " + - sa[:type].eq(SwfAssetType). - and(sa[:id].eq(psa[:swf_asset_id])). - and(sa[:zone_id].in(zone_set.map(&:id))). - to_sql - ). - group("#{table_name}.id"). - having("MAX(#{sa.name}.id) IS NULL"). # SwfAsset.arel_table[:id].maximum has no #eq - map(&:id) - scope.where(arel_table[:id].in(item_ids)) - } - - class Condition < String - attr_accessor :filter - - def initialize - @positive = true - end - - def filter? - !@filter.nil? - end - - def to_filter! - @filter = self.clone - self.replace '' - end - - def negate! - @positive = !@positive - end - - def narrow(scope, user) - if SearchFilterScopes.include?(filter) - polarized_filter = @positive ? filter : "not_#{filter}" - Item.send("search_filter_#{polarized_filter}", self, user, scope) - else - raise SearchError, "Filter #{filter} does not exist" - end - end - - def filter - @filter || 'name' - end - - def inspect - @filter ? "#{@filter}:#{super}" : super - end - end - - class SearchError < ArgumentError;end end diff --git a/app/models/item/search.rb b/app/models/item/search.rb new file mode 100644 index 00000000..079392c5 --- /dev/null +++ b/app/models/item/search.rb @@ -0,0 +1,8 @@ +class Item + module Search + def self.error(key, *args) + message = I18n.translate("items.search.errors.#{key}", *args) + raise Item::Search::Error, message + end + end +end diff --git a/app/models/item/search/contradiction.rb b/app/models/item/search/contradiction.rb new file mode 100644 index 00000000..c6ef01d4 --- /dev/null +++ b/app/models/item/search/contradiction.rb @@ -0,0 +1,7 @@ +class Item + module Search + class Contradiction < Error + + end + end +end diff --git a/app/models/item/search/error.rb b/app/models/item/search/error.rb new file mode 100644 index 00000000..45d19059 --- /dev/null +++ b/app/models/item/search/error.rb @@ -0,0 +1,7 @@ +class Item + module Search + class Error < RuntimeError + + end + end +end diff --git a/app/models/item/search/field.rb b/app/models/item/search/field.rb new file mode 100644 index 00000000..82f296a3 --- /dev/null +++ b/app/models/item/search/field.rb @@ -0,0 +1,11 @@ +class Item + module Search + class Field + attr_reader :key + + def initialize(key) + @key = key + end + end + end +end diff --git a/app/models/item/search/fields/flag.rb b/app/models/item/search/fields/flag.rb new file mode 100644 index 00000000..d50f003a --- /dev/null +++ b/app/models/item/search/fields/flag.rb @@ -0,0 +1,20 @@ +class Item + module Search + module Fields + class Flag < Field + def <<(filter) + if @value.nil? + @value = filter.positive? + elsif @value != filter.positive? + raise Item::Search::Contradiction, + "flag #{key} both positive and negative" + end + end + + def to_flex_params + {key => @value} + end + end + end + end +end diff --git a/app/models/item/search/fields/set_field.rb b/app/models/item/search/fields/set_field.rb new file mode 100644 index 00000000..e089fa13 --- /dev/null +++ b/app/models/item/search/fields/set_field.rb @@ -0,0 +1,34 @@ +class Item + module Search + module Fields + class SetField < Field + def initialize(*args) + super(*args) + @values = {true => Set.new, false => Set.new} + end + + def <<(filter) + if @values[!filter.positive?].include?(filter.value) + raise Item::Search::Contradiction, + "positive #{key} and negative #{key} both contain #{filter.value}" + end + + @values[filter.positive?] << filter.value + end + + def to_flex_params + { + :"_#{key}s" => nil_if_empty(@values[true]), + :"_negative_#{key}s" => nil_if_empty(@values[false]) + } + end + + private + + def nil_if_empty(set) + set.map { |value| {key => value} } unless set.empty? + end + end + end + end +end diff --git a/app/models/item/search/fields/text_field.rb b/app/models/item/search/fields/text_field.rb new file mode 100644 index 00000000..9825b38d --- /dev/null +++ b/app/models/item/search/fields/text_field.rb @@ -0,0 +1,29 @@ +class Item + module Search + module Fields + class TextField < Field + def initialize(*args) + super(*args) + @values = {true => '', false => ''} + end + + def <<(filter) + @values[filter.positive?] << (filter.value + ' ') + end + + def to_flex_params + { + key => nil_if_empty(@values[true]), + :"negative_#{key}" => nil_if_empty(@values[false]) + } + end + + private + + def nil_if_empty(str) + str unless str.empty? + end + end + end + end +end diff --git a/app/models/item/search/filter.rb b/app/models/item/search/filter.rb new file mode 100644 index 00000000..0f54c5b0 --- /dev/null +++ b/app/models/item/search/filter.rb @@ -0,0 +1,17 @@ +class Item + module Search + class Filter + attr_reader :key, :value + + def initialize(key, value, is_positive) + @key = key + @value = value + @is_positive = is_positive + end + + def positive? + @is_positive + end + end + end +end diff --git a/app/models/item/search/query.rb b/app/models/item/search/query.rb new file mode 100644 index 00000000..b9ac8621 --- /dev/null +++ b/app/models/item/search/query.rb @@ -0,0 +1,197 @@ +class Item + module Search + class Query + FIELD_CLASSES = { + :is_nc => Fields::Flag, + :is_pb => Fields::Flag, + :species_support_id => Fields::SetField, + :occupied_zone_id => Fields::SetField, + :restricted_zone_id => Fields::SetField, + :name => Fields::SetField, + :user_closet_hanger_ownership => Fields::SetField + } + + def initialize(filters, user) + @filters = filters + @user = user + end + + def fields + initial_fields.tap do |fields| + @filters.each { |filter| fields[filter.key] << filter } + end + end + + def to_flex_params + fields.values.map(&:to_flex_params).inject(&:merge) + end + + def paginate(options={}) + begin + flex_params = self.to_flex_params + rescue Item::Search::Contradiction + # If we have a contradictory query, no need to raise a stink about + # it, but no need to actually run a search, either. + return [] + end + + final_flex_params = { + :page => (options[:page] || 1), + :size => (options[:per_page] || 30), + :type => 'item' + }.merge(flex_params) + + locales = I18n.fallbacks[I18n.locale] & + I18n.locales_with_neopets_language_code + final_flex_params[:locale] = locales.first + + # Extend the names/negative_names queries with the corresponding + # localalized field names. + if final_flex_params[:_names] || final_flex_params[:_negative_names] + locale_entries = locales.map do |locale| + boost = (locale == I18n.locale) ? 4 : 1 + "name.#{locale}^#{boost}" + end + + # We *could* have set _name_locales once as a partial, but Flex won't + # let us call partials from inside other partials. Whatever. Assign + # it to each name entry instead. I also feel bad doing this + # afterwards, since it's kinda the field's job to return proper flex + # params, but that's a refactor for another day. + [:_names, :_negative_names].each do |key| + if final_flex_params[key] + final_flex_params[key].each do |name_query| + name_query[:fields] = locale_entries + end + end + end + end + + # Okay, yeah, looks like this really does deserve a refactor, like + # _names and _negative_names do. (Or Flex could just make all variables + # accessible from partials... hint, hint) + [:_user_closet_hanger_ownerships, :_negative_user_closet_hanger_ownerships].each do |key| + if final_flex_params[key] + Item::Search.error 'not_logged_in' unless @user + + final_flex_params[key].each do |entry| + entry[:user_id] = @user.id + end + end + end + + result = FlexSearch.item_search(final_flex_params) + result.scoped_loaded_collection( + :scopes => {'Item' => Item.includes(:translations)} + ) + end + + # Load the text query labels from I18n, so that when we see, say, + # the filter "species:acara", we know it means species_support_id. + TEXT_KEYS_BY_LABEL = {} + OWNERSHIP_KEYWORDS = {} + I18n.available_locales.each do |locale| + TEXT_KEYS_BY_LABEL[locale] = {} + OWNERSHIP_KEYWORDS[locale] = {} + FIELD_CLASSES.keys.each do |key| + # A locale can specify multiple labels for a key by separating by + # commas: "occupies,zone,type" + labels = I18n.translate("items.search.labels.#{key}", + :locale => locale).split(',') + labels.each { |label| TEXT_KEYS_BY_LABEL[locale][label] = key } + + {:owns => true, :wants => false}.each do |key, value| + translated_key = I18n.translate("items.search.labels.user_#{key}", + :locale => locale) + OWNERSHIP_KEYWORDS[locale][translated_key] = value + end + end + end + + TEXT_QUERY_RESOURCE_FINDERS = { + :species => lambda { |name| + species = Species.find_by_name(name) + unless species + Item::Search.error 'not_found.species', :species_name => name + end + species.id + }, + :zone => lambda { |label| + zone_set = Zone.with_plain_label(label) + unless zone_set + Item::Search.error 'not_found.zone', :zone_name => name + end + zone_set.map(&:id) + }, + :ownership => lambda { |keyword| + OWNERSHIP_KEYWORDS[I18n.locale][keyword].tap do |value| + if value.nil? + Item::Search.error 'not_found.ownership', :keyword => keyword + end + end + } + } + + TEXT_QUERY_RESOURCE_TYPES_BY_KEY = { + :species_support_id => :species, + :occupied_zone_id => :zone, + :restricted_zone_id => :zone, + :user_closet_hanger_ownership => :ownership + } + + TEXT_FILTER_EXPR = /([+-]?)(?:([a-z]+):)?(?:"([^"]+)"|(\S+))/ + def self.from_text(text, user=nil) + filters = [] + + is_keyword = I18n.translate('items.search.flag_keywords.is') + text.scan(TEXT_FILTER_EXPR) do |sign, label, quoted_value, unquoted_value| + label ||= 'name' + raw_value = quoted_value || unquoted_value + is_positive = (sign != '-') + + if label == is_keyword + # is-filters are weird. "-is:nc" is transposed to something more + # like "-nc:", then it's translated into a negative "is_nc" + # flag. Fun fact: "nc:foobar" and "-nc:foobar" also work. A bonus, + # I guess. There's probably a good way to refactor this to avoid + # the unintended bonus syntax, but this is a darn good cheap + # technique for the time being. + label = raw_value + raw_value = nil + end + + key = TEXT_KEYS_BY_LABEL[I18n.locale][label] + if key.nil? + message = I18n.translate('items.search.errors.not_found.label', + :label => label) + raise Item::Search::Error, message + end + + if TEXT_QUERY_RESOURCE_TYPES_BY_KEY.has_key?(key) + resource_type = TEXT_QUERY_RESOURCE_TYPES_BY_KEY[key] + finder = TEXT_QUERY_RESOURCE_FINDERS[resource_type] + value = finder.call(raw_value) + else + value = raw_value + end + + filters << Filter.new(key, value, is_positive) + end + + self.new(filters, user) + end + + private + + # The fields start out empty, then have the filters inserted into 'em, + # so that the fields can validate and aggregate their requirements. + def initial_fields + {}.tap do |fields| + FIELD_CLASSES.map do |key, klass| + fields[key] = klass.new(key) + end + end + end + end + end +end diff --git a/app/models/outfit.rb b/app/models/outfit.rb index c2c2cbba..76ddf1ea 100644 --- a/app/models/outfit.rb +++ b/app/models/outfit.rb @@ -84,7 +84,7 @@ class Outfit < ActiveRecord::Base # ordered from bottom to top. Careful: this method is memoized, so if the # image layers change after its first call we'll get bad results. def image_layers - @image_layers ||= visible_assets_with_images.sort { |a, b| a.zone.depth <=> b.zone.depth } + @image_layers ||= visible_assets_with_images.sort { |a, b| a.depth <=> b.depth } end # Creates and writes the thumbnail images for this outfit iff the new image @@ -177,9 +177,10 @@ class Outfit < ActiveRecord::Base end def visible_assets - biology_assets = pet_state.swf_assets + biology_assets = pet_state.swf_assets.includes(:zone) object_assets = SwfAsset.object_assets. - fitting_body_id(pet_state.pet_type.body_id).for_item_ids(worn_item_ids) + fitting_body_id(pet_state.pet_type.body_id).for_item_ids(worn_item_ids). + includes(:zone) # Now for fun with bitmasks! Rather than building a bunch of integer arrays # here, we instead go low-level and use bit-level operations. Build the diff --git a/app/models/pet.rb b/app/models/pet.rb index 94c5d4c8..a0d5d311 100644 --- a/app/models/pet.rb +++ b/app/models/pet.rb @@ -2,8 +2,8 @@ require 'rocketamf/remote_gateway' class Pet < ActiveRecord::Base GATEWAY_URL = 'http://www.neopets.com/amfphp/gateway.php' - AMF_SERVICE_NAME = 'CustomPetService' - PET_VIEWER_METHOD = 'getViewerData' + PET_VIEWER = RocketAMF::RemoteGateway.new(GATEWAY_URL). + service('CustomPetService').action('getViewerData') PET_NOT_FOUND_REMOTE_ERROR = 'PHP: Unable to retrieve records from the database.' WARDROBE_PATH = '/wardrobe' @@ -16,11 +16,22 @@ class Pet < ActiveRecord::Base joins(:pet_type).where(PetType.arel_table[:id].in(color_ids)) } - def load! + def load!(options={}) + options[:item_scope] ||= Item.scoped + options[:locale] ||= I18n.default_locale + + original_locale = I18n.locale + I18n.locale = options[:locale] + require 'ostruct' begin - envelope = Pet.amf_service.request(PET_VIEWER_METHOD, name, nil). - fetch(:timeout => 2) + neopets_language_code = I18n.neopets_language_code_for(options[:locale]) + envelope = PET_VIEWER.request([name, 0]).post( + :timeout => 2, + :headers => { + 'Cookie' => "lang=#{neopets_language_code}" + } + ) rescue RocketAMF::RemoteGateway::AMFError => e if e.message == PET_NOT_FOUND_REMOTE_ERROR raise PetNotFound, "Pet #{name.inspect} does not exist" @@ -31,18 +42,27 @@ class Pet < ActiveRecord::Base end contents = OpenStruct.new(envelope.messages[0].data.body) pet_data = OpenStruct.new(contents.custom_pet) - self.pet_type = PetType.find_or_initialize_by_species_id_and_color_id( - pet_data.species_id.to_i, - pet_data.color_id.to_i - ) - self.pet_type.body_id = pet_data.body_id - self.pet_type.origin_pet = self - biology = pet_data.biology_by_zone - biology[0] = nil # remove effects if present - @pet_state = self.pet_type.add_pet_state_from_biology! biology - @pet_state.label_by_pet(self, pet_data.owner) - @items = Item.collection_from_pet_type_and_registries(self.pet_type, - contents.object_info_registry, contents.object_asset_registry) + + # in case this is running in a thread, explicitly grab an ActiveRecord + # connection, to avoid connection conflicts + Pet.connection_pool.with_connection do + self.pet_type = PetType.find_or_initialize_by_species_id_and_color_id( + pet_data.species_id.to_i, + pet_data.color_id.to_i + ) + self.pet_type.body_id = pet_data.body_id + self.pet_type.origin_pet = self + biology = pet_data.biology_by_zone + biology[0] = nil # remove effects if present + @pet_state = self.pet_type.add_pet_state_from_biology! biology + @pet_state.label_by_pet(self, pet_data.owner) + @items = Item.collection_from_pet_type_and_registries(self.pet_type, + contents.object_info_registry, contents.object_asset_registry, + options[:item_scope]) + end + + I18n.locale = original_locale + true end @@ -64,6 +84,44 @@ class Pet < ActiveRecord::Base end contributables end + + def item_translation_candidates + {}.tap do |candidates| + if @items + @items.each do |item| + item.needed_translations.each do |locale| + candidates[locale] ||= [] + candidates[locale] << item + end + end + end + end + end + + def translate_items + candidates = self.item_translation_candidates + + until candidates.empty? + last_pet_loaded = nil + reloaded_pets = Parallel.map(candidates.keys, :in_threads => 8) do |locale| + Rails.logger.info "Reloading #{name} in #{locale}" + reloaded_pet = Pet.load(name, :item_scope => Item.includes(:translations), + :locale => locale) + Pet.connection_pool.with_connection { reloaded_pet.save! } + last_pet_loaded = reloaded_pet + end + previous_candidates = candidates + candidates = last_pet_loaded.item_translation_candidates + + if previous_candidates == candidates + # This condition should never happen if Neopets responds with correct + # data, but, if Neopets somehow responds with incorrect data, this + # condition could throw us into an infinite loop if uncaught. Better + # safe than sorry when working with external services. + raise "No change when reloading #{name} for #{candidates}" + end + end + end before_validation do pet_type.save! @@ -80,25 +138,12 @@ class Pet < ActiveRecord::Base end end - def self.load(name) + def self.load(name, options={}) pet = Pet.find_or_initialize_by_name(name) - pet.load! + pet.load!(options) pet end - private - - def self.amf_service - @amf_service ||= gateway.service AMF_SERVICE_NAME - end - - def self.gateway - unless @gateway - @gateway = RocketAMF::RemoteGateway.new(GATEWAY_URL) - end - @gateway - end - class PetNotFound < Exception;end class DownloadError < Exception;end end diff --git a/app/models/pet_type.rb b/app/models/pet_type.rb index e0b8e156..66f679f1 100644 --- a/app/models/pet_type.rb +++ b/app/models/pet_type.rb @@ -3,6 +3,8 @@ class PetType < ActiveRecord::Base IMAGE_CP_LOCATION_REGEX = %r{^/cp/(.+?)/1/1\.png$}; IMAGE_CPN_ACCEPTABLE_NAME = /^[a-z0-9_]+$/ + belongs_to :species + belongs_to :color has_one :contribution, :as => :contributed has_many :pet_states has_many :pets @@ -13,17 +15,20 @@ class PetType < ActiveRecord::Base # Returns all pet types of a single standard color. The caller shouldn't care # which, though, in this implemention, it's always Blue. Don't depend on that. - scope :single_standard_color, where(:color_id => Color::BasicIds[0]) + scope :single_standard_color, lambda { where(:color_id => Color.standard.first) } - scope :nonstandard_colors, where(:color_id => Color.nonstandard_ids) + scope :nonstandard_colors, lambda { where(:color_id => Color.nonstandard) } + + scope :includes_child_translations, + lambda { includes({:color => :translations, :species => :translations}) } def self.standard_pet_types_by_species_id - @standard_pet_types_by_species_id ||= - PetType.where(:color_id => Color::BasicIds).group_by(&:species_id) + PetType.where(:color_id => Color.basic).includes_child_translations. + group_by(&:species_id) end def self.standard_body_ids - @standard_body_ids ||= [].tap do |body_ids| + [].tap do |body_ids| standard_pet_types_by_species_id.each do |species_id, pet_types| body_ids.concat(pet_types.map(&:body_id)) end @@ -32,8 +37,9 @@ class PetType < ActiveRecord::Base def self.random_basic_per_species(species_ids) random_pet_types = [] + standards = self.standard_pet_types_by_species_id species_ids.each do |species_id| - pet_types = standard_pet_types_by_species_id[species_id] + pet_types = standards[species_id] random_pet_types << pet_types[rand(pet_types.size)] if pet_types end random_pet_types @@ -51,40 +57,21 @@ class PetType < ActiveRecord::Base end end - def color_id=(new_color_id) - @color = nil - write_attribute('color_id', new_color_id) - end - - def color=(new_color) - @color = new_color - write_attribute('color_id', @color.id) - end - - def color - @color ||= Color.find(color_id) - end - - def species_id=(new_species_id) - @species = nil - write_attribute('species_id', new_species_id) - end - - def species=(new_species) - @species = new_species - write_attribute('species_id', @species.id) - end - - def species - @species ||= Species.find(species_id) - end - def image_hash self['image_hash'] || basic_image_hash end def basic_image_hash - BasicHashes[species.name][color.name] + I18n.with_locale(I18n.default_locale) do + # Probably should move the basic hashes into the database someday. + # Until then, access the hash using the English color/species names. + + unless BasicHashes[species.name] && BasicHashes[species.name][color.name] + raise "basic image hash for #{species.name}, #{color.name} not found" + end + + BasicHashes[species.name][color.name] + end end def human_name diff --git a/app/models/species.rb b/app/models/species.rb index e6606067..ac3b4f4e 100644 --- a/app/models/species.rb +++ b/app/models/species.rb @@ -1,11 +1,13 @@ -class Species < PetAttribute - fetch_objects! +class Species < ActiveRecord::Base + translates :name - def self.require_by_name(name) - species = Species.find_by_name(name) - raise NotFound, "Species \"#{name.humanize}\" does not exist" unless species - species + scope :alphabetical, lambda { includes(:translations).order(Species::Translation.arel_table[:name]) } + + def as_json(options={}) + {:id => id, :name => human_name} end - class NotFound < ArgumentError;end + def human_name + name.capitalize + end end diff --git a/app/models/static_resource.rb b/app/models/static_resource.rb index 0db48d2c..8d4c7053 100644 --- a/app/models/static_resource.rb +++ b/app/models/static_resource.rb @@ -5,11 +5,21 @@ class StaticResource @objects end - def self.find(id) - @objects[id-1] + def self.find(id_or_ids) + if id_or_ids.is_a?(Array) + id_or_ids.uniq.map { |id| find_one(id) } + else + find_one(id_or_ids) + end end def self.count @objects.size end + + private + + def self.find_one(id) + @objects[id - 1] + end end diff --git a/app/models/swf_asset.rb b/app/models/swf_asset.rb index fd8f7b00..8b8e1f9c 100644 --- a/app/models/swf_asset.rb +++ b/app/models/swf_asset.rb @@ -23,6 +23,10 @@ class SwfAsset < ActiveRecord::Base include SwfConverter converts_swfs :size => IMAGE_SIZES[:large], :output_sizes => IMAGE_SIZES.values + + belongs_to :zone + + scope :includes_depth, lambda { includes(:zone) } def local_swf_path LOCAL_ASSET_DIR.join(local_path_within_outfit_swfs) @@ -197,15 +201,7 @@ class SwfAsset < ActiveRecord::Base end def body_specific? - # If we already have assigned this a non-zero body id, or if the asset is - # in a body-specific zone, or if the item is explicitly labeled as - # body-specific (like Encased In Ice, which is body-specific but whose - # assets occupy Background Item), then this asset is body-specific. - (body_id? && body_id > 0) || self.zone.type_id < 3 || (@item && @item.explicitly_body_specific?) - end - - def zone - Zone.find(zone_id) + self.zone.type_id < 3 || (@item && @item.body_specific?) end def origin_pet_type=(pet_type) diff --git a/app/models/zone.rb b/app/models/zone.rb index fef3983e..6f4e442b 100644 --- a/app/models/zone.rb +++ b/app/models/zone.rb @@ -1,51 +1,28 @@ -class Zone < StaticResource - AttributeNames = ['id', 'label', 'depth', 'type_id'] - ItemZoneSets = {} +class Zone < ActiveRecord::Base + translates :label, :plain_label - attr_reader *AttributeNames # When selecting zones that an asset occupies, we allow the zone to set # whether or not the zone is "sometimes" occupied. This is false by default. attr_writer :sometimes - - def initialize(attributes) - AttributeNames.each do |name| - instance_variable_set "@#{name}", attributes[name] - end - end + + scope :alphabetical, lambda { + includes_translations.order(Zone::Translation.arel_table[:label]) + } + scope :includes_translations, lambda { includes(:translations) } + scope :with_plain_label, lambda { |label| + t = Zone::Translation.arel_table + includes(:translations).where(t[:plain_label].eq(Zone.plainify_label(label))) + } def uncertain_label @sometimes ? "#{label} sometimes" : label end - def self.find_set(name) - ItemZoneSets[plain(name)] + def self.all_plain_labels + Zone.select([:id]).includes(:translations).all.map(&:plain_label).uniq.sort end - def self.plain(name) - name.delete('\- /').downcase - end - - n = 0 - @objects = YAML.load_file(Rails.root.join('config', 'zones.yml')).map do |a| - a['id'] = (n += 1) - obj = new(a) - if obj.type_id == 2 || obj.type_id == 3 - plain_name = plain(obj.label) - - ItemZoneSets[plain_name] ||= [] - ItemZoneSets[plain_name] << obj - end - obj - end - n = nil - - # Add aliases to keys like "lowerforegrounditem" to "lowerforeground" - # ...unless there's already such a key, like "backgrounditem" to "background", - # in which case we don't, because that'd be silly. - ItemZoneSets.keys.each do |name| - if name.end_with?('item') - stripped_name = name[0..-5] - ItemZoneSets[stripped_name] ||= ItemZoneSets[name] - end + def self.plainify_label(label) + label.delete('\- /').downcase end end diff --git a/app/stylesheets/_layout.sass b/app/stylesheets/_layout.sass index 180a509f..33262088 100644 --- a/app/stylesheets/_layout.sass +++ b/app/stylesheets/_layout.sass @@ -1,4 +1,7 @@ @import "partials/icon" +@import url(http://fonts.googleapis.com/css?family=Droid+Sans:400,700) +@import url(http://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic) +@import url(http://fonts.googleapis.com/css?family=Calligraffitti) /* Reset @@ -249,30 +252,3 @@ dd font-style: italic src: local("Delicious"), font-url("Delicious-Italic.otf") - -@font-face - font-family: 'Droid Serif' - font-style: normal - font-weight: normal - src: local("Droid Serif"), url("http://themes.googleusercontent.com/font?kit=70P0G8gxVDIV6F9om0DsKg") format("truetype") - - -@font-face - font-family: 'Droid Serif' - font-style: normal - font-weight: bold - src: local("Droid Serif"), url("http://themes.googleusercontent.com/font?kit=QQt14e8dY39u-eYBZmppwf5Jgr8ufe5A6KahQF76Xmg") format("truetype") - - -@font-face - font-family: 'Droid Sans' - font-style: normal - font-weight: normal - src: local("Droid Sans"), url("http://themes.googleusercontent.com/font?kit=POVDFY-UUf0WFR9DIMCU8g") format("truetype") - -@font-face - font-family: 'Calligraffitti' - font-style: normal - font-weight: normal - src: local('Calligraffiti'), url('http://themes.googleusercontent.com/font?kit=vLVN2Y-z65rVu1R7lWdvyKIZAuDcNtpCWuPSaIR0Ie8') format('woff') - diff --git a/app/views/items/show.html.haml b/app/views/items/show.html.haml index 02344e95..51610a89 100644 --- a/app/views/items/show.html.haml +++ b/app/views/items/show.html.haml @@ -53,13 +53,13 @@ #item-zones %p %strong #{t '.zones.occupied_header'}: - = list_zones @item.occupied_zones, :uncertain_label + = list_zones @occupied_zones, :uncertain_label %p %strong #{t '.zones.restricted_header'}: - - if @item.restricted_zones.empty? + - if @restricted_zones.empty? = t '.zones.none' - else - = list_zones @item.restricted_zones + = list_zones @restricted_zones #trade-hangers - [true, false].each do |owned| diff --git a/config/flex.yml b/config/flex.yml new file mode 100644 index 00000000..4611faf2 --- /dev/null +++ b/config/flex.yml @@ -0,0 +1,36 @@ +# ANCHORS litheral key: it will not be used as template +# you can store here fragments of structures to reuse below +ANCHORS: + - + +# This is a dynamic index name The settings and mapping below will work with any index. +# The default index name generated by Flex is usually _, +# but you may have changed it in the initializers/flex.rb or you can hardcode it if you prefer. +<%= Flex::Configuration.variables[:index] %>: + + settings: + number_of_shards: 5 + number_of_replicas: 1 + + # add your custom mappings here + mappings: + item: + properties: + # Name is an object of locale fields, which are in turn multi_fields. + # First, an analyzed string for searching. Second, an untouched + # string for sorting. Elasticsearch requires that both be expliticly + # named in the mapping, but will handle the copy implicitly. + name: + type: object + properties: + <% I18n.usable_locales_with_neopets_language_code.each do |locale| %> + <%= locale %>: + type: multi_field + fields: + <%= locale %>: + type: string + index: analyzed + untouched: + type: string + index: not_analyzed + <% end %> diff --git a/config/initializers/flex.rb b/config/initializers/flex.rb new file mode 100644 index 00000000..993b9a0a --- /dev/null +++ b/config/initializers/flex.rb @@ -0,0 +1,44 @@ +# see the detailed Configuration documentation at https://github.com/ddnexus/flex/wiki/Configuration + +Flex::Configuration.configure do |config| + + # you MUST add your indexed model names here + config.flex_models = %w[ Item ClosetHanger ] + + # Add the your result extenders here + config.result_extenders |= [ FlexSearchExtender ] + + # Add the default variables here + # see also the details Variables documentation at https://github.com/ddnexus/flex/wiki/Variables + # config.variables.add :index => 'my_index', + # :type => 'project', + # :anything => 'anything + + # The custom url of your ElasticSearch server + # config.base_uri = 'http://localhost:9200' + + # Set it to true to log the debug infos (true by default in development mode) + # config.debug = false + + # Debug info are actually valid curl commands + # config.debug_to_curl = false + + # The custom logger you want Flex to use. Default Rails.logger + # config.logger = Logger.new(STDERR) + + # Custom config file path + # config.config_file = '/custom/path/to/flex.yml', + + # Custom flex dir path + # config.flex_dir = '/custom/path/to/flex', + + # The custom http_client you may want to implement + # config.http_client = 'Your::Client' + + # The options passed to the http_client. They are client specific. + # config.http_client_options = {:timeout => 5} + + # Experimental: checks the response and return a boolean (should raise?) + # config.raise_proc = proc{|response| response.status >= 400} + +end diff --git a/config/initializers/locale_meta.rb b/config/initializers/locale_meta.rb new file mode 100644 index 00000000..3bc96f0d --- /dev/null +++ b/config/initializers/locale_meta.rb @@ -0,0 +1,58 @@ +module LocaleMeta + PUBLIC_LOCALES = [] + USABLE_LOCALES = [] + NEOPETS_LANGUAGE_CODES_BY_LOCALE = {} + LOCALES_WITH_NEOPETS_LANGUAGE_CODE = [] + COMPATIBLE_LOCALES = {} +end + +config = YAML.load_file(Rails.root.join('config', 'locale_meta.yml')) + +config.each do |locale_str, locale_meta| + locale = locale_str.to_sym + + visibility = locale_meta['visibility'] + if visibility == 'public' + LocaleMeta::PUBLIC_LOCALES << locale + LocaleMeta::USABLE_LOCALES << locale + elsif visibility == 'private' + LocaleMeta::USABLE_LOCALES << locale + end + + if locale_meta.has_key?('neopets_language_code') + neopets_language_code = locale_meta['neopets_language_code'] + LocaleMeta::NEOPETS_LANGUAGE_CODES_BY_LOCALE[locale] = neopets_language_code + LocaleMeta::LOCALES_WITH_NEOPETS_LANGUAGE_CODE << locale + elsif locale_meta.has_key?('compatible_with') + compatible_locale = locale_meta['compatible_with'].to_sym + LocaleMeta::COMPATIBLE_LOCALES[locale] = compatible_locale + else + raise "locale #{locale} must either have a neopets_language_code or " + + "be compatible_with a locale that does" + end +end + +LocaleMeta::USABLE_LOCALES_WITH_NEOPETS_LANGUAGE_CODE = LocaleMeta::USABLE_LOCALES & + LocaleMeta::LOCALES_WITH_NEOPETS_LANGUAGE_CODE + +module I18n + def self.public_locales + LocaleMeta::PUBLIC_LOCALES + end + + def self.usable_locales + LocaleMeta::USABLE_LOCALES + end + + def self.locales_with_neopets_language_code + LocaleMeta::LOCALES_WITH_NEOPETS_LANGUAGE_CODE + end + + def self.usable_locales_with_neopets_language_code + LocaleMeta::USABLE_LOCALES_WITH_NEOPETS_LANGUAGE_CODE + end + + def self.neopets_language_code_for(locale) + LocaleMeta::NEOPETS_LANGUAGE_CODES_BY_LOCALE[locale] + end +end diff --git a/config/locale_meta.yml b/config/locale_meta.yml new file mode 100644 index 00000000..82889814 --- /dev/null +++ b/config/locale_meta.yml @@ -0,0 +1,47 @@ +en: + visibility: public + neopets_language_code: en + +en-MEEP: + visibility: public + compatible_with: en + +pt: + visibility: private + neopets_language_code: pt + +es: + visibility: none + neopets_language_code: es + +nl: + visibility: none + neopets_language_code: nl + +de: + visibility: none + neopets_language_code: de + +fr: + visibility: none + neopets_language_code: fr + +it: + visibility: none + neopets_language_code: it + +zh-CN: + visibility: none + neopets_language_code: ch + +zh-TW: + visibility: none + neopets_language_code: zh + +ja: + visibility: none + neopets_language_code: ja + +ko: + visibility: none + neopets_language_code: ko diff --git a/config/locales/en.yml b/config/locales/en.yml index 196c670a..c1b97352 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -325,6 +325,30 @@ en: contributors: header: Brought to you by footer: Thanks! + + search: + errors: + not_found: + label: Filter "%{label}" does not exist. Is it spelled correctly? + species: + Species "%{species_name}" does not exist. Is it spelled correctly? + zone: Zone "%{zone_name}" does not exist. Is it spelled correctly? + ownership: + I don't know what user:%{keyword} means. Is it spelled correctly? + not_logged_in: + The "user" filters are only available if you're logged in. + flag_keywords: + is: is + labels: + name: name + is_nc: nc + is_pb: pb + species_support_id: species + occupied_zone_id: occupies,zone,type + restricted_zone_id: restricts + user_closet_hanger_ownership: user + user_owns: owns + user_wants: wants neopets_pages: create: diff --git a/config/locales/pt.yml b/config/locales/pt.yml new file mode 100644 index 00000000..da15ca9d --- /dev/null +++ b/config/locales/pt.yml @@ -0,0 +1,5 @@ +pt: + locale_name: Portuguese + + # This is a placeholder Portuguese locale, so that we can test things + # involving its presence. diff --git a/db/migrate/20130111213346_translate_items.rb b/db/migrate/20130111213346_translate_items.rb new file mode 100644 index 00000000..86b911ad --- /dev/null +++ b/db/migrate/20130111213346_translate_items.rb @@ -0,0 +1,17 @@ +class TranslateItems < ActiveRecord::Migration + def self.up + rename_table :objects, :items + Item.create_translation_table!({ + :name => :string, + :description => :text, + :rarity => :string + }, { + :migrate_data => true + }) + end + + def self.down + Item.drop_translation_table! :migrate_data => true + rename_table :items, :objects + end +end diff --git a/db/migrate/20130121193957_create_species.rb b/db/migrate/20130121193957_create_species.rb new file mode 100644 index 00000000..7384bd18 --- /dev/null +++ b/db/migrate/20130121193957_create_species.rb @@ -0,0 +1,11 @@ +class CreateSpecies < ActiveRecord::Migration + def self.up + create_table :species + Species.create_translation_table! :name => :string + end + + def self.down + drop_table :species + Species.drop_translation_table! + end +end diff --git a/db/migrate/20130121205607_create_colors.rb b/db/migrate/20130121205607_create_colors.rb new file mode 100644 index 00000000..fcc3eaa8 --- /dev/null +++ b/db/migrate/20130121205607_create_colors.rb @@ -0,0 +1,14 @@ +class CreateColors < ActiveRecord::Migration + def self.up + create_table :colors do |t| + t.boolean :basic + t.boolean :standard + end + Color.create_translation_table! :name => :string + end + + def self.down + drop_table :colors + Color.drop_translation_table! + end +end diff --git a/db/migrate/20130121221226_create_zones.rb b/db/migrate/20130121221226_create_zones.rb new file mode 100644 index 00000000..4d7a7a79 --- /dev/null +++ b/db/migrate/20130121221226_create_zones.rb @@ -0,0 +1,14 @@ +class CreateZones < ActiveRecord::Migration + def self.up + create_table :zones do |t| + t.integer :depth + t.integer :type_id + end + Zone.create_translation_table! :label => :string, :plain_label => :string + end + + def self.down + drop_table :zones + Zone.drop_translation_table! + end +end diff --git a/db/schema.rb b/db/schema.rb index 97249527..6fe68f87 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -10,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121006010446) do +ActiveRecord::Schema.define(:version => 20130121221226) do create_table "auth_servers", :force => true do |t| t.string "short_name", :limit => 10, :null => false @@ -20,6 +21,13 @@ ActiveRecord::Schema.define(:version => 20121006010446) do t.string "secret", :limit => 64, :null => false end + create_table "campaigns", :force => true do |t| + t.integer "goal_cents", :null => false + t.integer "progress_cents", :null => false + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "closet_hangers", :force => true do |t| t.integer "item_id" t.integer "user_id" @@ -46,9 +54,20 @@ ActiveRecord::Schema.define(:version => 20121006010446) do add_index "closet_lists", ["user_id"], :name => "index_closet_lists_on_user_id" + create_table "color_translations", :force => true do |t| + t.integer "color_id" + t.string "locale" + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "color_translations", ["color_id"], :name => "index_color_translations_on_color_id" + add_index "color_translations", ["locale"], :name => "index_color_translations_on_locale" + create_table "colors", :force => true do |t| - t.string "name" - t.boolean "basic", :default => false, :null => false + t.boolean "basic" + t.boolean "standard" end create_table "contributions", :force => true do |t| @@ -61,6 +80,14 @@ ActiveRecord::Schema.define(:version => 20121006010446) do add_index "contributions", ["contributed_id", "contributed_type"], :name => "index_contributions_on_contributed_id_and_contributed_type" add_index "contributions", ["user_id"], :name => "index_contributions_on_user_id" + create_table "donations", :force => true do |t| + t.integer "amount_cents", :null => false + t.integer "campaign_id", :null => false + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "forums", :force => true do |t| t.string "name" t.text "description" @@ -80,16 +107,20 @@ ActiveRecord::Schema.define(:version => 20121006010446) do add_index "item_outfit_relationships", ["item_id"], :name => "index_item_outfit_relationships_on_item_id" add_index "item_outfit_relationships", ["outfit_id", "is_worn"], :name => "index_item_outfit_relationships_on_outfit_id_and_is_worn" - create_table "login_cookies", :force => true do |t| - t.integer "user_id", :null => false - t.integer "series", :null => false - t.integer "token", :null => false + create_table "item_translations", :force => true do |t| + t.integer "item_id" + t.string "locale" + t.string "name" + t.text "description" + t.string "rarity" + t.datetime "created_at" + t.datetime "updated_at" end - add_index "login_cookies", ["user_id", "series"], :name => "login_cookies_user_id_and_series" - add_index "login_cookies", ["user_id"], :name => "login_cookies_user_id" + add_index "item_translations", ["item_id"], :name => "index_item_translations_on_item_id" + add_index "item_translations", ["locale"], :name => "index_item_translations_on_locale" - create_table "objects", :force => true do |t| + create_table "items", :force => true do |t| t.text "zones_restrict", :null => false t.text "thumbnail_url", :limit => 16777215, :null => false t.string "name", :limit => 100, :null => false @@ -108,8 +139,25 @@ ActiveRecord::Schema.define(:version => 20121006010446) do t.boolean "explicitly_body_specific", :default => false, :null => false end - add_index "objects", ["last_spidered"], :name => "objects_last_spidered" - add_index "objects", ["name"], :name => "name" + add_index "items", ["last_spidered"], :name => "objects_last_spidered" + add_index "items", ["name"], :name => "name" + + create_table "login_cookies", :force => true do |t| + t.integer "user_id", :null => false + t.integer "series", :null => false + t.integer "token", :null => false + end + + add_index "login_cookies", ["user_id", "series"], :name => "login_cookies_user_id_and_series" + add_index "login_cookies", ["user_id"], :name => "login_cookies_user_id" + + create_table "outfit_features", :force => true do |t| + t.integer "outfit_id", :null => false + t.datetime "created_at" + t.datetime "updated_at" + t.date "frontpage_start_date" + t.date "frontpage_end_date" + end create_table "outfits", :force => true do |t| t.integer "pet_state_id" @@ -121,6 +169,7 @@ ActiveRecord::Schema.define(:version => 20121006010446) do t.string "image" t.string "image_layers_hash" t.boolean "image_enqueued", :default => false, :null => false + t.boolean "image_dirty", :default => false, :null => false end add_index "outfits", ["pet_state_id"], :name => "index_outfits_on_pet_state_id" @@ -179,6 +228,20 @@ ActiveRecord::Schema.define(:version => 20121006010446) do t.datetime "updated_at" end + create_table "species", :force => true do |t| + end + + create_table "species_translations", :force => true do |t| + t.integer "species_id" + t.string "locale" + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "species_translations", ["locale"], :name => "index_species_translations_on_locale" + add_index "species_translations", ["species_id"], :name => "index_species_translations_on_species_id" + create_table "swf_assets", :force => true do |t| t.string "type", :limit => 7, :null => false t.integer "remote_id", :limit => 3, :null => false @@ -223,11 +286,21 @@ ActiveRecord::Schema.define(:version => 20121006010446) do t.integer "wanted_closet_hangers_visibility", :default => 1, :null => false end + create_table "zone_translations", :force => true do |t| + t.integer "zone_id" + t.string "locale" + t.string "label" + t.string "plain_label" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "zone_translations", ["locale"], :name => "index_zone_translations_on_locale" + add_index "zone_translations", ["zone_id"], :name => "index_zone_translations_on_zone_id" + create_table "zones", :force => true do |t| - t.integer "depth", :limit => 1, :null => false - t.integer "type_id", :limit => 1, :null => false - t.string "type", :limit => 40, :null => false - t.string "label", :limit => 40, :null => false + t.integer "depth" + t.integer "type_id" end end diff --git a/db/seeds.rb b/db/seeds.rb index 664d8c74..c49ebbac 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,7 +1,208 @@ -# This file should contain all the record creation needed to seed the database with its default values. -# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). -# -# Examples: -# -# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) -# Mayor.create(:name => 'Daley', :city => cities.first) +Species.create(:id => 1, :name => "acara") +Species.create(:id => 2, :name => "aisha") +Species.create(:id => 3, :name => "blumaroo") +Species.create(:id => 4, :name => "bori") +Species.create(:id => 5, :name => "bruce") +Species.create(:id => 6, :name => "buzz") +Species.create(:id => 7, :name => "chia") +Species.create(:id => 8, :name => "chomby") +Species.create(:id => 9, :name => "cybunny") +Species.create(:id => 10, :name => "draik") +Species.create(:id => 11, :name => "elephante") +Species.create(:id => 12, :name => "eyrie") +Species.create(:id => 13, :name => "flotsam") +Species.create(:id => 14, :name => "gelert") +Species.create(:id => 15, :name => "gnorbu") +Species.create(:id => 16, :name => "grarrl") +Species.create(:id => 17, :name => "grundo") +Species.create(:id => 18, :name => "hissi") +Species.create(:id => 19, :name => "ixi") +Species.create(:id => 20, :name => "jetsam") +Species.create(:id => 21, :name => "jubjub") +Species.create(:id => 22, :name => "kacheek") +Species.create(:id => 23, :name => "kau") +Species.create(:id => 24, :name => "kiko") +Species.create(:id => 25, :name => "koi") +Species.create(:id => 26, :name => "korbat") +Species.create(:id => 27, :name => "kougra") +Species.create(:id => 28, :name => "krawk") +Species.create(:id => 29, :name => "kyrii") +Species.create(:id => 30, :name => "lenny") +Species.create(:id => 31, :name => "lupe") +Species.create(:id => 32, :name => "lutari") +Species.create(:id => 33, :name => "meerca") +Species.create(:id => 34, :name => "moehog") +Species.create(:id => 35, :name => "mynci") +Species.create(:id => 36, :name => "nimmo") +Species.create(:id => 37, :name => "ogrin") +Species.create(:id => 38, :name => "peophin") +Species.create(:id => 39, :name => "poogle") +Species.create(:id => 40, :name => "pteri") +Species.create(:id => 41, :name => "quiggle") +Species.create(:id => 42, :name => "ruki") +Species.create(:id => 43, :name => "scorchio") +Species.create(:id => 44, :name => "shoyru") +Species.create(:id => 45, :name => "skeith") +Species.create(:id => 46, :name => "techo") +Species.create(:id => 47, :name => "tonu") +Species.create(:id => 48, :name => "tuskaninny") +Species.create(:id => 49, :name => "uni") +Species.create(:id => 50, :name => "usul") +Species.create(:id => 51, :name => "wocky") +Species.create(:id => 52, :name => "xweetok") +Species.create(:id => 53, :name => "yurble") +Species.create(:id => 54, :name => "zafara") + +Color.create(:id => 1, :name => "alien", :basic => false, :standard => true) +Color.create(:id => 2, :name => "apple", :basic => false, :standard => false) +Color.create(:id => 3, :name => "asparagus", :basic => false, :standard => false) +Color.create(:id => 4, :name => "aubergine", :basic => false, :standard => false) +Color.create(:id => 5, :name => "avocado", :basic => false, :standard => false) +Color.create(:id => 6, :name => "baby", :basic => false, :standard => false) +Color.create(:id => 7, :name => "biscuit", :basic => false, :standard => true) +Color.create(:id => 8, :name => "blue", :basic => true, :standard => true) +Color.create(:id => 9, :name => "blueberry", :basic => false, :standard => false) +Color.create(:id => 10, :name => "brown", :basic => false, :standard => true) +Color.create(:id => 11, :name => "camouflage", :basic => false, :standard => true) +Color.create(:id => 12, :name => "carrot", :basic => false, :standard => false) +Color.create(:id => 13, :name => "checkered", :basic => false, :standard => true) +Color.create(:id => 14, :name => "chocolate", :basic => false, :standard => true) +Color.create(:id => 15, :name => "chokato", :basic => false, :standard => false) +Color.create(:id => 16, :name => "christmas", :basic => false, :standard => true) +Color.create(:id => 17, :name => "clay", :basic => false, :standard => true) +Color.create(:id => 18, :name => "cloud", :basic => false, :standard => true) +Color.create(:id => 19, :name => "coconut", :basic => false, :standard => true) +Color.create(:id => 20, :name => "custard", :basic => false, :standard => true) +Color.create(:id => 21, :name => "darigan", :basic => false, :standard => true) +Color.create(:id => 22, :name => "desert", :basic => false, :standard => true) +Color.create(:id => 23, :name => "disco", :basic => false, :standard => true) +Color.create(:id => 24, :name => "durian", :basic => false, :standard => false) +Color.create(:id => 25, :name => "electric", :basic => false, :standard => true) +Color.create(:id => 26, :name => "faerie", :basic => false, :standard => true) +Color.create(:id => 27, :name => "fire", :basic => false, :standard => true) +Color.create(:id => 28, :name => "garlic", :basic => false, :standard => true) +Color.create(:id => 29, :name => "ghost", :basic => false, :standard => true) +Color.create(:id => 30, :name => "glowing", :basic => false, :standard => true) +Color.create(:id => 31, :name => "gold", :basic => false, :standard => true) +Color.create(:id => 32, :name => "gooseberry", :basic => false, :standard => false) +Color.create(:id => 33, :name => "grape", :basic => false, :standard => false) +Color.create(:id => 34, :name => "green", :basic => true, :standard => true) +Color.create(:id => 35, :name => "grey", :basic => false, :standard => true) +Color.create(:id => 36, :name => "halloween", :basic => false, :standard => true) +Color.create(:id => 37, :name => "ice", :basic => false, :standard => true) +Color.create(:id => 38, :name => "invisible", :basic => false, :standard => true) +Color.create(:id => 39, :name => "island", :basic => false, :standard => true) +Color.create(:id => 40, :name => "jelly", :basic => false, :standard => true) +Color.create(:id => 41, :name => "lemon", :basic => false, :standard => false) +Color.create(:id => 42, :name => "lime", :basic => false, :standard => false) +Color.create(:id => 43, :name => "mallow", :basic => false, :standard => true) +Color.create(:id => 44, :name => "maraquan", :basic => false, :standard => false) +Color.create(:id => 45, :name => "msp", :basic => false, :standard => true) +Color.create(:id => 46, :name => "mutant", :basic => false, :standard => false) +Color.create(:id => 47, :name => "orange", :basic => false, :standard => false) +Color.create(:id => 48, :name => "pea", :basic => false, :standard => false) +Color.create(:id => 49, :name => "peach", :basic => false, :standard => false) +Color.create(:id => 50, :name => "pear", :basic => false, :standard => false) +Color.create(:id => 51, :name => "pepper", :basic => false, :standard => false) +Color.create(:id => 52, :name => "pineapple", :basic => false, :standard => false) +Color.create(:id => 53, :name => "pink", :basic => false, :standard => true) +Color.create(:id => 54, :name => "pirate", :basic => false, :standard => true) +Color.create(:id => 55, :name => "plum", :basic => false, :standard => false) +Color.create(:id => 56, :name => "plushie", :basic => false, :standard => true) +Color.create(:id => 57, :name => "purple", :basic => false, :standard => true) +Color.create(:id => 58, :name => "quigukiboy", :basic => false, :standard => true) +Color.create(:id => 59, :name => "quigukigirl", :basic => false, :standard => true) +Color.create(:id => 60, :name => "rainbow", :basic => false, :standard => true) +Color.create(:id => 61, :name => "red", :basic => true, :standard => true) +Color.create(:id => 62, :name => "robot", :basic => false, :standard => true) +Color.create(:id => 63, :name => "royalboy", :basic => false, :standard => true) +Color.create(:id => 64, :name => "royalgirl", :basic => false, :standard => true) +Color.create(:id => 65, :name => "shadow", :basic => false, :standard => true) +Color.create(:id => 66, :name => "silver", :basic => false, :standard => true) +Color.create(:id => 67, :name => "sketch", :basic => false, :standard => true) +Color.create(:id => 68, :name => "skunk", :basic => false, :standard => true) +Color.create(:id => 69, :name => "snot", :basic => false, :standard => true) +Color.create(:id => 70, :name => "snow", :basic => false, :standard => false) +Color.create(:id => 71, :name => "speckled", :basic => false, :standard => true) +Color.create(:id => 72, :name => "split", :basic => false, :standard => true) +Color.create(:id => 73, :name => "sponge", :basic => false, :standard => true) +Color.create(:id => 74, :name => "spotted", :basic => false, :standard => true) +Color.create(:id => 75, :name => "starry", :basic => false, :standard => true) +Color.create(:id => 76, :name => "strawberry", :basic => false, :standard => true) +Color.create(:id => 77, :name => "striped", :basic => false, :standard => true) +Color.create(:id => 78, :name => "thornberry", :basic => false, :standard => false) +Color.create(:id => 79, :name => "tomato", :basic => false, :standard => false) +Color.create(:id => 80, :name => "tyrannian", :basic => false, :standard => true) +Color.create(:id => 81, :name => "usukiboy", :basic => false, :standard => true) +Color.create(:id => 82, :name => "usukigirl", :basic => false, :standard => true) +Color.create(:id => 83, :name => "white", :basic => false, :standard => true) +Color.create(:id => 84, :name => "yellow", :basic => true, :standard => true) +Color.create(:id => 85, :name => "zombie", :basic => false, :standard => true) +Color.create(:id => 86, :name => "onion", :basic => false, :standard => false) +Color.create(:id => 87, :name => "magma", :basic => false, :standard => true) +Color.create(:id => 88, :name => "relic", :basic => false, :standard => true) +Color.create(:id => 89, :name => "woodland", :basic => false, :standard => true) +Color.create(:id => 90, :name => "transparent", :basic => false, :standard => true) +Color.create(:id => 91, :name => "maractite", :basic => false, :standard => true) +Color.create(:id => 92, :name => "8-bit", :basic => false, :standard => true) +Color.create(:id => 93, :name => "swamp gas", :basic => false, :standard => true) +Color.create(:id => 94, :name => "water", :basic => false, :standard => true) +Color.create(:id => 95, :name => "wraith", :basic => false, :standard => true) +Color.create(:id => 96, :name => "eventide", :basic => false, :standard => true) +Color.create(:id => 97, :name => "elderlyboy", :basic => false, :standard => true) +Color.create(:id => 98, :name => "elderlygirl", :basic => false, :standard => true) +Color.create(:id => 99, :name => "stealthy", :basic => false, :standard => true) +Color.create(:id => 100, :name => "dimensional", :basic => false, :standard => true) + +Zone.create(:id => 1, :label => "Music", :plain_label => "music", :depth => 1, :type_id => 4) +Zone.create(:id => 2, :label => "Sound Effects", :plain_label => "soundeffects", :depth => 2, :type_id => 4) +Zone.create(:id => 3, :label => "Background", :plain_label => "background", :depth => 3, :type_id => 3) +Zone.create(:id => 4, :label => "Biology Effects", :plain_label => "biologyeffects", :depth => 6, :type_id => 1) +Zone.create(:id => 5, :label => "Hind Biology", :plain_label => "hindbiology", :depth => 7, :type_id => 1) +Zone.create(:id => 6, :label => "Markings", :plain_label => "markings", :depth => 8, :type_id => 2) +Zone.create(:id => 7, :label => "Hind Disease", :plain_label => "hinddisease", :depth => 9, :type_id => 1) +Zone.create(:id => 8, :label => "Hind Cover", :plain_label => "hindcover", :depth => 10, :type_id => 2) +Zone.create(:id => 9, :label => "Hind Transient Biology", :plain_label => "hindtransientbiology", :depth => 11, :type_id => 1) +Zone.create(:id => 10, :label => "Hind Drippings", :plain_label => "hinddrippings", :depth => 12, :type_id => 1) +Zone.create(:id => 11, :label => "Backpack", :plain_label => "backpack", :depth => 13, :type_id => 2) +Zone.create(:id => 12, :label => "Wings Transient Biology", :plain_label => "wingstransientbiology", :depth => 14, :type_id => 1) +Zone.create(:id => 13, :label => "Wings", :plain_label => "wings", :depth => 15, :type_id => 2) +Zone.create(:id => 14, :label => "Hair Back", :plain_label => "hairback", :depth => 17, :type_id => 1) +Zone.create(:id => 15, :label => "Body", :plain_label => "body", :depth => 18, :type_id => 1) +Zone.create(:id => 16, :label => "Markings", :plain_label => "markings", :depth => 19, :type_id => 2) +Zone.create(:id => 17, :label => "Body Disease", :plain_label => "bodydisease", :depth => 20, :type_id => 1) +Zone.create(:id => 18, :label => "Feet Transient Biology", :plain_label => "feettransientbiology", :depth => 21, :type_id => 1) +Zone.create(:id => 19, :label => "Shoes", :plain_label => "shoes", :depth => 22, :type_id => 2) +Zone.create(:id => 20, :label => "Lower-body Transient Biology", :plain_label => "lowerbodytransientbiology", :depth => 23, :type_id => 1) +Zone.create(:id => 21, :label => "Trousers", :plain_label => "trousers", :depth => 24, :type_id => 2) +Zone.create(:id => 22, :label => "Upper-body Transient Biology", :plain_label => "upperbodytransientbiology", :depth => 25, :type_id => 1) +Zone.create(:id => 23, :label => "Shirt/Dress", :plain_label => "shirtdress", :depth => 26, :type_id => 2) +Zone.create(:id => 24, :label => "Necklace", :plain_label => "necklace", :depth => 28, :type_id => 2) +Zone.create(:id => 25, :label => "Gloves", :plain_label => "gloves", :depth => 29, :type_id => 2) +Zone.create(:id => 26, :label => "Jacket", :plain_label => "jacket", :depth => 30, :type_id => 2) +Zone.create(:id => 27, :label => "Collar", :plain_label => "collar", :depth => 31, :type_id => 2) +Zone.create(:id => 28, :label => "Body Drippings", :plain_label => "bodydrippings", :depth => 32, :type_id => 1) +Zone.create(:id => 29, :label => "Ruff", :plain_label => "ruff", :depth => 33, :type_id => 1) +Zone.create(:id => 30, :label => "Head", :plain_label => "head", :depth => 34, :type_id => 1) +Zone.create(:id => 31, :label => "Markings", :plain_label => "markings", :depth => 35, :type_id => 2) +Zone.create(:id => 32, :label => "Head Disease", :plain_label => "headdisease", :depth => 36, :type_id => 1) +Zone.create(:id => 33, :label => "Eyes", :plain_label => "eyes", :depth => 37, :type_id => 1) +Zone.create(:id => 34, :label => "Mouth", :plain_label => "mouth", :depth => 38, :type_id => 1) +Zone.create(:id => 35, :label => "Glasses", :plain_label => "glasses", :depth => 41, :type_id => 2) +Zone.create(:id => 36, :label => "Earrings", :plain_label => "earrings", :depth => 39, :type_id => 2) +Zone.create(:id => 37, :label => "Hair Front", :plain_label => "hairfront", :depth => 40, :type_id => 1) +Zone.create(:id => 38, :label => "Head Transient Biology", :plain_label => "headtransientbiology", :depth => 42, :type_id => 1) +Zone.create(:id => 39, :label => "Head Drippings", :plain_label => "headdrippings", :depth => 43, :type_id => 1) +Zone.create(:id => 40, :label => "Hat", :plain_label => "hat", :depth => 44, :type_id => 2) +Zone.create(:id => 41, :label => "Earrings", :plain_label => "earrings", :depth => 45, :type_id => 2) +Zone.create(:id => 42, :label => "Right-hand Item", :plain_label => "righthand", :depth => 46, :type_id => 2) +Zone.create(:id => 43, :label => "Left-hand Item", :plain_label => "lefthand", :depth => 47, :type_id => 2) +Zone.create(:id => 44, :label => "Higher Foreground Item", :plain_label => "higherforeground", :depth => 49, :type_id => 3) +Zone.create(:id => 45, :label => "Lower Foreground Item", :plain_label => "lowerforeground", :depth => 50, :type_id => 3) +Zone.create(:id => 46, :label => "Static", :plain_label => "static", :depth => 48, :type_id => 3) +Zone.create(:id => 47, :label => "Thought Bubble", :plain_label => "thoughtbubble", :depth => 51, :type_id => 3) +Zone.create(:id => 48, :label => "Background Item", :plain_label => "background", :depth => 4, :type_id => 3) +Zone.create(:id => 49, :label => "Right-hand Item", :plain_label => "righthand", :depth => 5, :type_id => 2) +Zone.create(:id => 50, :label => "Hat", :plain_label => "hat", :depth => 16, :type_id => 2) +Zone.create(:id => 51, :label => "Belt", :plain_label => "belt", :depth => 27, :type_id => 2) +Zone.create(:id => 52, :label => "Foreground", :plain_label => "foreground", :depth => 52, :type_id => 3) diff --git a/lib/rocketamf/remote_gateway.rb b/lib/rocketamf/remote_gateway.rb index 5007093b..cc48e25c 100644 --- a/lib/rocketamf/remote_gateway.rb +++ b/lib/rocketamf/remote_gateway.rb @@ -1,7 +1,6 @@ require 'net/http' require 'rocketamf' require File.join(File.dirname(__FILE__), 'remote_gateway', 'service') -require File.join(File.dirname(__FILE__), 'remote_gateway', 'request') module RocketAMF class RemoteGateway diff --git a/lib/rocketamf/remote_gateway/action.rb b/lib/rocketamf/remote_gateway/action.rb new file mode 100644 index 00000000..eefbcd57 --- /dev/null +++ b/lib/rocketamf/remote_gateway/action.rb @@ -0,0 +1,18 @@ +require File.join(File.dirname(__FILE__), 'request') + +module RocketAMF + class RemoteGateway + class Action + attr_reader :service, :name + + def initialize(service, name) + @service = service + @name = name + end + + def request(params) + Request.new(self, params) + end + end + end +end diff --git a/lib/rocketamf/remote_gateway/request.rb b/lib/rocketamf/remote_gateway/request.rb index 7b983720..a2b0bfcb 100644 --- a/lib/rocketamf/remote_gateway/request.rb +++ b/lib/rocketamf/remote_gateway/request.rb @@ -5,18 +5,21 @@ module RocketAMF class Request ERROR_CODE = 'AMFPHP_RUNTIME_ERROR' - def initialize(service, method, *params) - @service = service - @method = method + def initialize(action, params) + @action = action @params = params end - def fetch(options={}) - uri = @service.gateway.uri + def post(options={}) + uri = @action.service.gateway.uri data = envelope.serialize req = Net::HTTP::Post.new(uri.path) req.body = data + headers = options[:headers] || {} + headers.each do |key, value| + req[key] = value + end res = nil @@ -68,8 +71,8 @@ module RocketAMF def remoting_message message = Values::RemotingMessage.new - message.source = @service.name - message.operation = @method + message.source = @action.service.name + message.operation = @action.name message.body = @params message end diff --git a/lib/rocketamf/remote_gateway/service.rb b/lib/rocketamf/remote_gateway/service.rb index b510582e..6f19aa74 100644 --- a/lib/rocketamf/remote_gateway/service.rb +++ b/lib/rocketamf/remote_gateway/service.rb @@ -1,3 +1,5 @@ +require File.join(File.dirname(__FILE__), 'action') + module RocketAMF class RemoteGateway class Service @@ -8,8 +10,8 @@ module RocketAMF @name = name end - def request(method, *params) - Request.new(self, method, *params) + def action(name) + Action.new(self, name) end end end diff --git a/public/stylesheets/compiled/screen.css b/public/stylesheets/compiled/screen.css index 645a6ef8..9c241e81 100644 --- a/public/stylesheets/compiled/screen.css +++ b/public/stylesheets/compiled/screen.css @@ -1,6 +1,9 @@ @charset "UTF-8"; +@import url(http://fonts.googleapis.com/css?family=Droid+Sans:400,700); +@import url(http://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic); +@import url(http://fonts.googleapis.com/css?family=Calligraffitti); /* Reset */ -/* line 5, ../../../app/stylesheets/_layout.sass */ +/* line 8, ../../../app/stylesheets/_layout.sass */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, @@ -16,12 +19,12 @@ caption, tbody, tfoot, thead, tr, th, td { } /* Typography */ -/* line 20, ../../../app/stylesheets/_layout.sass */ +/* line 23, ../../../app/stylesheets/_layout.sass */ html, body { height: 100%; } -/* line 23, ../../../app/stylesheets/_layout.sass */ +/* line 26, ../../../app/stylesheets/_layout.sass */ body { background: white; color: #004400; @@ -30,60 +33,60 @@ body { line-height: 1.5; } -/* line 31, ../../../app/stylesheets/_layout.sass */ +/* line 34, ../../../app/stylesheets/_layout.sass */ a { color: #226622; } -/* line 34, ../../../app/stylesheets/_layout.sass */ +/* line 37, ../../../app/stylesheets/_layout.sass */ p { font-family: "Droid Serif", Georgia, "Times New Roman", Times, serif; } -/* line 37, ../../../app/stylesheets/_layout.sass */ +/* line 40, ../../../app/stylesheets/_layout.sass */ input, button, select { font-family: inherit; font-size: 100%; } -/* line 42, ../../../app/stylesheets/_layout.sass */ +/* line 45, ../../../app/stylesheets/_layout.sass */ p { margin-bottom: 1em; } -/* line 45, ../../../app/stylesheets/_layout.sass */ +/* line 48, ../../../app/stylesheets/_layout.sass */ h1, h2, h3 { font-family: Delicious, Helvetica, Arial, Verdana, sans-serif; } -/* line 48, ../../../app/stylesheets/_layout.sass */ +/* line 51, ../../../app/stylesheets/_layout.sass */ h1 { font-size: 3em; line-height: 1; margin-bottom: 0.5em; } -/* line 53, ../../../app/stylesheets/_layout.sass */ +/* line 56, ../../../app/stylesheets/_layout.sass */ h2 { font-size: 2em; margin-bottom: 0.75em; } -/* line 57, ../../../app/stylesheets/_layout.sass */ +/* line 60, ../../../app/stylesheets/_layout.sass */ h3 { font-size: 1.5em; line-height: 1; margin-bottom: 1em; } -/* line 62, ../../../app/stylesheets/_layout.sass */ +/* line 65, ../../../app/stylesheets/_layout.sass */ .inline-image, body.pets-bulk #bulk-pets-form ul img { margin-right: 1em; vertical-align: middle; } /* Main */ -/* line 70, ../../../app/stylesheets/_layout.sass */ +/* line 73, ../../../app/stylesheets/_layout.sass */ #container { margin: 1em auto; padding-top: 3em; @@ -91,12 +94,12 @@ h3 { width: 800px; } -/* line 76, ../../../app/stylesheets/_layout.sass */ +/* line 79, ../../../app/stylesheets/_layout.sass */ input, button, select, label { cursor: pointer; } -/* line 79, ../../../app/stylesheets/_layout.sass */ +/* line 82, ../../../app/stylesheets/_layout.sass */ input[type=text], body.pets-bulk #bulk-pets-form textarea, input[type=password], input[type=search], input[type=number], select, textarea { -moz-border-radius: 3px; -webkit-border-radius: 3px; @@ -105,17 +108,17 @@ input[type=text], body.pets-bulk #bulk-pets-form textarea, input[type=password], color: #448844; padding: 0.25em; } -/* line 85, ../../../app/stylesheets/_layout.sass */ +/* line 88, ../../../app/stylesheets/_layout.sass */ input[type=text]:focus, body.pets-bulk #bulk-pets-form textarea:focus, input[type=text]:active, body.pets-bulk #bulk-pets-form textarea:active, input[type=password]:focus, input[type=password]:active, input[type=search]:focus, input[type=search]:active, input[type=number]:focus, input[type=number]:active, select:focus, select:active, textarea:focus, textarea:active { color: inherit; } -/* line 88, ../../../app/stylesheets/_layout.sass */ +/* line 91, ../../../app/stylesheets/_layout.sass */ textarea { font: inherit; } -/* line 91, ../../../app/stylesheets/_layout.sass */ +/* line 94, ../../../app/stylesheets/_layout.sass */ a.button, input[type=submit], button { /* http://www.zurb.com/blog_uploads/0000/0617/buttons-03.html */ -moz-border-radius: 5px; @@ -146,7 +149,7 @@ a.button:hover, input[type=submit]:hover, button:hover { a.button:active, input[type=submit]:active, button:active { top: 1px; } -/* line 93, ../../../app/stylesheets/_layout.sass */ +/* line 96, ../../../app/stylesheets/_layout.sass */ a.button.loud, input[type=submit].loud, button.loud { background: #ff5c00 url('/images/alert-overlay.png?1344550430') repeat-x; font-size: 125%; @@ -157,21 +160,21 @@ a.button.loud:hover, input[type=submit].loud:hover, button.loud:hover { background-color: #ee4b00; } -/* line 96, ../../../app/stylesheets/_layout.sass */ +/* line 99, ../../../app/stylesheets/_layout.sass */ ul.buttons { margin-bottom: 1em; } -/* line 98, ../../../app/stylesheets/_layout.sass */ +/* line 101, ../../../app/stylesheets/_layout.sass */ ul.buttons li { list-style: none; margin: 0 0.5em; } -/* line 101, ../../../app/stylesheets/_layout.sass */ +/* line 104, ../../../app/stylesheets/_layout.sass */ ul.buttons li, ul.buttons li form { display: inline; } -/* line 104, ../../../app/stylesheets/_layout.sass */ +/* line 107, ../../../app/stylesheets/_layout.sass */ #footer { clear: both; font-size: 75%; @@ -179,69 +182,69 @@ ul.buttons li, ul.buttons li form { padding-top: 2em; text-align: center; } -/* line 110, ../../../app/stylesheets/_layout.sass */ +/* line 113, ../../../app/stylesheets/_layout.sass */ #footer ul, #footer div { display: inline; margin: 0 1em; } -/* line 113, ../../../app/stylesheets/_layout.sass */ +/* line 116, ../../../app/stylesheets/_layout.sass */ #footer li, #footer div ul { display: inline; margin: 0 0.5em; } -/* line 116, ../../../app/stylesheets/_layout.sass */ +/* line 119, ../../../app/stylesheets/_layout.sass */ #footer #locale-form { float: right; } -/* line 119, ../../../app/stylesheets/_layout.sass */ +/* line 122, ../../../app/stylesheets/_layout.sass */ .success, .alert, .warning { margin-bottom: 1em; padding: 0.25em 0.5em; text-align: center; } -/* line 124, ../../../app/stylesheets/_layout.sass */ +/* line 127, ../../../app/stylesheets/_layout.sass */ .success { background: #e6efc2; border: 1px solid #c6d880; color: #264409; } -/* line 127, ../../../app/stylesheets/_layout.sass */ +/* line 130, ../../../app/stylesheets/_layout.sass */ .alert { background: #fbe3e4; border: 1px solid #fbc2c4; color: #8a1f11; } -/* line 130, ../../../app/stylesheets/_layout.sass */ +/* line 133, ../../../app/stylesheets/_layout.sass */ .warning { background: #fff6bf; border: 1px solid #ffd324; color: #514721; } -/* line 133, ../../../app/stylesheets/_layout.sass */ +/* line 136, ../../../app/stylesheets/_layout.sass */ #userbar { font-family: Delicious, Helvetica, Arial, Verdana, sans-serif; position: absolute; right: 0; top: 0; } -/* line 138, ../../../app/stylesheets/_layout.sass */ +/* line 141, ../../../app/stylesheets/_layout.sass */ #userbar > * { display: inline; margin: 0 0.25em; } -/* line 142, ../../../app/stylesheets/_layout.sass */ +/* line 145, ../../../app/stylesheets/_layout.sass */ #userbar-image-mode { font-weight: bold; margin-right: 1em; text-decoration: none; } -/* line 146, ../../../app/stylesheets/_layout.sass */ +/* line 149, ../../../app/stylesheets/_layout.sass */ #userbar-image-mode img { bottom: -2px; height: 16px; @@ -249,25 +252,25 @@ ul.buttons li, ul.buttons li form { width: 16px; } -/* line 149, ../../../app/stylesheets/_layout.sass */ +/* line 152, ../../../app/stylesheets/_layout.sass */ #userbar-log-in { text-decoration: none; } -/* line 151, ../../../app/stylesheets/_layout.sass */ +/* line 154, ../../../app/stylesheets/_layout.sass */ #userbar-log-in img { margin-bottom: -4px; margin-right: 0.25em; } -/* line 155, ../../../app/stylesheets/_layout.sass */ +/* line 158, ../../../app/stylesheets/_layout.sass */ #userbar-log-in span { text-decoration: underline; } -/* line 157, ../../../app/stylesheets/_layout.sass */ +/* line 160, ../../../app/stylesheets/_layout.sass */ #userbar-log-in:hover span { text-decoration: none; } -/* line 160, ../../../app/stylesheets/_layout.sass */ +/* line 163, ../../../app/stylesheets/_layout.sass */ .object { display: -moz-inline-box; -moz-box-orient: vertical; @@ -282,32 +285,32 @@ ul.buttons li, ul.buttons li form { vertical-align: top; width: 100px; } -/* line 168, ../../../app/stylesheets/_layout.sass */ +/* line 171, ../../../app/stylesheets/_layout.sass */ .object a { text-decoration: none; } -/* line 170, ../../../app/stylesheets/_layout.sass */ +/* line 173, ../../../app/stylesheets/_layout.sass */ .object a img { -moz-opacity: 0.75; -webkit-opacity: 0.75; -o-opacity: 0.75; -khtml-opacity: 0.75; } -/* line 172, ../../../app/stylesheets/_layout.sass */ +/* line 175, ../../../app/stylesheets/_layout.sass */ .object img { display: block; height: 80px; margin: 0 auto; width: 80px; } -/* line 177, ../../../app/stylesheets/_layout.sass */ +/* line 180, ../../../app/stylesheets/_layout.sass */ .object:hover img, .object a:hover img { -moz-opacity: 1; -webkit-opacity: 1; -o-opacity: 1; -khtml-opacity: 1; } -/* line 183, ../../../app/stylesheets/_layout.sass */ +/* line 186, ../../../app/stylesheets/_layout.sass */ .object .nc-icon, .object .closeted-icons { -moz-opacity: 1; -webkit-opacity: 1; @@ -318,7 +321,7 @@ ul.buttons li, ul.buttons li form { position: absolute; top: 64px; } -/* line 189, ../../../app/stylesheets/_layout.sass */ +/* line 192, ../../../app/stylesheets/_layout.sass */ .object .nc-icon:hover, .object .closeted-icons:hover { -moz-opacity: 0.5; -webkit-opacity: 0.5; @@ -326,32 +329,32 @@ ul.buttons li, ul.buttons li form { -khtml-opacity: 0.5; background: transparent; } -/* line 193, ../../../app/stylesheets/_layout.sass */ +/* line 196, ../../../app/stylesheets/_layout.sass */ .object .nc-icon, .object .closeted-icons img { display: inline; height: 16px; width: 16px; } -/* line 198, ../../../app/stylesheets/_layout.sass */ +/* line 201, ../../../app/stylesheets/_layout.sass */ .object .nc-icon { right: 18px; } -/* line 202, ../../../app/stylesheets/_layout.sass */ +/* line 205, ../../../app/stylesheets/_layout.sass */ .object .closeted-icons { left: 18px; } -/* line 205, ../../../app/stylesheets/_layout.sass */ +/* line 208, ../../../app/stylesheets/_layout.sass */ dt { font-weight: bold; } -/* line 208, ../../../app/stylesheets/_layout.sass */ +/* line 211, ../../../app/stylesheets/_layout.sass */ dd { margin: 0 0 1.5em 1em; } -/* line 211, ../../../app/stylesheets/_layout.sass */ +/* line 214, ../../../app/stylesheets/_layout.sass */ #home-link { font-family: Delicious, Helvetica, Arial, Verdana, sans-serif; font-size: 175%; @@ -362,21 +365,21 @@ dd { position: absolute; top: 0; } -/* line 221, ../../../app/stylesheets/_layout.sass */ +/* line 224, ../../../app/stylesheets/_layout.sass */ #home-link:hover { background: #eeffee; text-decoration: none; } -/* line 224, ../../../app/stylesheets/_layout.sass */ +/* line 227, ../../../app/stylesheets/_layout.sass */ #home-link span:before { content: "<< "; } -/* line 228, ../../../app/stylesheets/_layout.sass */ +/* line 231, ../../../app/stylesheets/_layout.sass */ .pagination a, .pagination span { margin: 0 0.5em; } -/* line 230, ../../../app/stylesheets/_layout.sass */ +/* line 233, ../../../app/stylesheets/_layout.sass */ .pagination .current { font-weight: bold; } @@ -400,34 +403,6 @@ dd { src: local("Delicious"), url('/fonts/Delicious-Italic.otf'); } -@font-face { - font-family: "Droid Serif"; - font-style: normal; - font-weight: normal; - src: local("Droid Serif"), url("http://themes.googleusercontent.com/font?kit=70P0G8gxVDIV6F9om0DsKg") format("truetype"); -} - -@font-face { - font-family: "Droid Serif"; - font-style: normal; - font-weight: bold; - src: local("Droid Serif"), url("http://themes.googleusercontent.com/font?kit=QQt14e8dY39u-eYBZmppwf5Jgr8ufe5A6KahQF76Xmg") format("truetype"); -} - -@font-face { - font-family: "Droid Sans"; - font-style: normal; - font-weight: normal; - src: local("Droid Sans"), url("http://themes.googleusercontent.com/font?kit=POVDFY-UUf0WFR9DIMCU8g") format("truetype"); -} - -@font-face { - font-family: "Calligraffitti"; - font-style: normal; - font-weight: normal; - src: local("Calligraffiti"), url("http://themes.googleusercontent.com/font?kit=vLVN2Y-z65rVu1R7lWdvyKIZAuDcNtpCWuPSaIR0Ie8") format("woff"); -} - /* line 2, ../../../app/stylesheets/partials/_jquery.jgrowl.sass */ div.jGrowl { padding: 10px; diff --git a/vendor/cache/addressable-2.2.6.gem b/vendor/cache/addressable-2.2.6.gem deleted file mode 100644 index 14282d1d..00000000 Binary files a/vendor/cache/addressable-2.2.6.gem and /dev/null differ diff --git a/vendor/cache/addressable-2.3.2.gem b/vendor/cache/addressable-2.3.2.gem new file mode 100644 index 00000000..a17dfdc5 Binary files /dev/null and b/vendor/cache/addressable-2.3.2.gem differ diff --git a/vendor/cache/bullet-4.1.5.gem b/vendor/cache/bullet-4.1.5.gem deleted file mode 100644 index 2d675c0e..00000000 Binary files a/vendor/cache/bullet-4.1.5.gem and /dev/null differ diff --git a/vendor/cache/bullet-4.1.6.gem b/vendor/cache/bullet-4.1.6.gem new file mode 100644 index 00000000..87e42c76 Binary files /dev/null and b/vendor/cache/bullet-4.1.6.gem differ diff --git a/vendor/cache/closure-compiler-1.1.4.gem b/vendor/cache/closure-compiler-1.1.4.gem deleted file mode 100644 index 0a4c5da1..00000000 Binary files a/vendor/cache/closure-compiler-1.1.4.gem and /dev/null differ diff --git a/vendor/cache/closure-compiler-1.1.8.gem b/vendor/cache/closure-compiler-1.1.8.gem new file mode 100644 index 00000000..be948500 Binary files /dev/null and b/vendor/cache/closure-compiler-1.1.8.gem differ diff --git a/vendor/cache/dye-0.1.4.gem b/vendor/cache/dye-0.1.4.gem new file mode 100644 index 00000000..016907ff Binary files /dev/null and b/vendor/cache/dye-0.1.4.gem differ diff --git a/vendor/cache/em-socksify-0.1.0.gem b/vendor/cache/em-socksify-0.1.0.gem deleted file mode 100644 index 8708cd9b..00000000 Binary files a/vendor/cache/em-socksify-0.1.0.gem and /dev/null differ diff --git a/vendor/cache/em-socksify-0.2.1.gem b/vendor/cache/em-socksify-0.2.1.gem new file mode 100644 index 00000000..7e9a091a Binary files /dev/null and b/vendor/cache/em-socksify-0.2.1.gem differ diff --git a/vendor/cache/excon-0.16.10.gem b/vendor/cache/excon-0.16.10.gem new file mode 100644 index 00000000..a4570e6e Binary files /dev/null and b/vendor/cache/excon-0.16.10.gem differ diff --git a/vendor/cache/excon-0.9.6.gem b/vendor/cache/excon-0.9.6.gem deleted file mode 100644 index 721a7c3e..00000000 Binary files a/vendor/cache/excon-0.9.6.gem and /dev/null differ diff --git a/vendor/cache/factory_girl-2.3.2.gem b/vendor/cache/factory_girl-2.3.2.gem deleted file mode 100644 index fd4df411..00000000 Binary files a/vendor/cache/factory_girl-2.3.2.gem and /dev/null differ diff --git a/vendor/cache/factory_girl-2.6.4.gem b/vendor/cache/factory_girl-2.6.4.gem new file mode 100644 index 00000000..fb27381c Binary files /dev/null and b/vendor/cache/factory_girl-2.6.4.gem differ diff --git a/vendor/cache/factory_girl_rails-1.4.0.gem b/vendor/cache/factory_girl_rails-1.4.0.gem deleted file mode 100644 index da2d4626..00000000 Binary files a/vendor/cache/factory_girl_rails-1.4.0.gem and /dev/null differ diff --git a/vendor/cache/factory_girl_rails-1.7.0.gem b/vendor/cache/factory_girl_rails-1.7.0.gem new file mode 100644 index 00000000..4cb55787 Binary files /dev/null and b/vendor/cache/factory_girl_rails-1.7.0.gem differ diff --git a/vendor/cache/fog-1.1.2.gem b/vendor/cache/fog-1.1.2.gem deleted file mode 100644 index 494ec94c..00000000 Binary files a/vendor/cache/fog-1.1.2.gem and /dev/null differ diff --git a/vendor/cache/fog-1.8.0.gem b/vendor/cache/fog-1.8.0.gem new file mode 100644 index 00000000..45a2f7f6 Binary files /dev/null and b/vendor/cache/fog-1.8.0.gem differ diff --git a/vendor/cache/formatador-0.2.1.gem b/vendor/cache/formatador-0.2.1.gem deleted file mode 100644 index 54dc8467..00000000 Binary files a/vendor/cache/formatador-0.2.1.gem and /dev/null differ diff --git a/vendor/cache/formatador-0.2.4.gem b/vendor/cache/formatador-0.2.4.gem new file mode 100644 index 00000000..64925406 Binary files /dev/null and b/vendor/cache/formatador-0.2.4.gem differ diff --git a/vendor/cache/globalize3-0.3.0.gem b/vendor/cache/globalize3-0.3.0.gem new file mode 100644 index 00000000..ba5beb9a Binary files /dev/null and b/vendor/cache/globalize3-0.3.0.gem differ diff --git a/vendor/cache/msgpack-0.4.6.gem b/vendor/cache/msgpack-0.4.6.gem deleted file mode 100644 index 38dac8f3..00000000 Binary files a/vendor/cache/msgpack-0.4.6.gem and /dev/null differ diff --git a/vendor/cache/msgpack-0.4.7.gem b/vendor/cache/msgpack-0.4.7.gem new file mode 100644 index 00000000..c9e3f28c Binary files /dev/null and b/vendor/cache/msgpack-0.4.7.gem differ diff --git a/vendor/cache/multi_json-1.0.4.gem b/vendor/cache/multi_json-1.0.4.gem deleted file mode 100644 index 2c862408..00000000 Binary files a/vendor/cache/multi_json-1.0.4.gem and /dev/null differ diff --git a/vendor/cache/multi_json-1.3.7.gem b/vendor/cache/multi_json-1.3.7.gem new file mode 100644 index 00000000..70e5ea79 Binary files /dev/null and b/vendor/cache/multi_json-1.3.7.gem differ diff --git a/vendor/cache/mysql2-0.2.18.gem b/vendor/cache/mysql2-0.2.18.gem new file mode 100644 index 00000000..0b82bb2f Binary files /dev/null and b/vendor/cache/mysql2-0.2.18.gem differ diff --git a/vendor/cache/mysql2-0.2.6.gem b/vendor/cache/mysql2-0.2.6.gem deleted file mode 100644 index ca1c3752..00000000 Binary files a/vendor/cache/mysql2-0.2.6.gem and /dev/null differ diff --git a/vendor/cache/net-ssh-2.3.0.gem b/vendor/cache/net-ssh-2.3.0.gem deleted file mode 100644 index 11d064e5..00000000 Binary files a/vendor/cache/net-ssh-2.3.0.gem and /dev/null differ diff --git a/vendor/cache/net-ssh-2.6.3.gem b/vendor/cache/net-ssh-2.6.3.gem new file mode 100644 index 00000000..f5038059 Binary files /dev/null and b/vendor/cache/net-ssh-2.6.3.gem differ diff --git a/vendor/cache/newrelic_rpm-3.5.3.25.gem b/vendor/cache/newrelic_rpm-3.5.3.25.gem deleted file mode 100644 index 02efc73d..00000000 Binary files a/vendor/cache/newrelic_rpm-3.5.3.25.gem and /dev/null differ diff --git a/vendor/cache/newrelic_rpm-3.5.5.38.gem b/vendor/cache/newrelic_rpm-3.5.5.38.gem new file mode 100644 index 00000000..097491b4 Binary files /dev/null and b/vendor/cache/newrelic_rpm-3.5.5.38.gem differ diff --git a/vendor/cache/nokogiri-1.5.3.gem b/vendor/cache/nokogiri-1.5.3.gem deleted file mode 100644 index 3c79ea2f..00000000 Binary files a/vendor/cache/nokogiri-1.5.3.gem and /dev/null differ diff --git a/vendor/cache/nokogiri-1.5.6.gem b/vendor/cache/nokogiri-1.5.6.gem new file mode 100644 index 00000000..cb13200d Binary files /dev/null and b/vendor/cache/nokogiri-1.5.6.gem differ diff --git a/vendor/cache/paper_trail-2.7.0.gem b/vendor/cache/paper_trail-2.7.0.gem new file mode 100644 index 00000000..7eb2cf0e Binary files /dev/null and b/vendor/cache/paper_trail-2.7.0.gem differ diff --git a/vendor/cache/parallel-0.5.17.gem b/vendor/cache/parallel-0.5.17.gem deleted file mode 100644 index 6b2fec49..00000000 Binary files a/vendor/cache/parallel-0.5.17.gem and /dev/null differ diff --git a/vendor/cache/parallel-0.5.21.gem b/vendor/cache/parallel-0.5.21.gem new file mode 100644 index 00000000..6f1d74cd Binary files /dev/null and b/vendor/cache/parallel-0.5.21.gem differ diff --git a/vendor/cache/patron-0.4.18.gem b/vendor/cache/patron-0.4.18.gem new file mode 100644 index 00000000..0b40e944 Binary files /dev/null and b/vendor/cache/patron-0.4.18.gem differ diff --git a/vendor/cache/progressbar-0.11.0.gem b/vendor/cache/progressbar-0.11.0.gem new file mode 100644 index 00000000..97f176f8 Binary files /dev/null and b/vendor/cache/progressbar-0.11.0.gem differ diff --git a/vendor/cache/prompter-0.1.5.gem b/vendor/cache/prompter-0.1.5.gem new file mode 100644 index 00000000..82ff55f6 Binary files /dev/null and b/vendor/cache/prompter-0.1.5.gem differ diff --git a/vendor/cache/rack-1.2.6.gem b/vendor/cache/rack-1.2.6.gem deleted file mode 100644 index 402769eb..00000000 Binary files a/vendor/cache/rack-1.2.6.gem and /dev/null differ diff --git a/vendor/cache/rack-1.2.7.gem b/vendor/cache/rack-1.2.7.gem new file mode 100644 index 00000000..0c4b7c2b Binary files /dev/null and b/vendor/cache/rack-1.2.7.gem differ diff --git a/vendor/cache/redis-2.2.2.gem b/vendor/cache/redis-2.2.2.gem deleted file mode 100644 index 9f5e05e7..00000000 Binary files a/vendor/cache/redis-2.2.2.gem and /dev/null differ diff --git a/vendor/cache/redis-3.0.2.gem b/vendor/cache/redis-3.0.2.gem new file mode 100644 index 00000000..08f7fd63 Binary files /dev/null and b/vendor/cache/redis-3.0.2.gem differ diff --git a/vendor/cache/redis-namespace-1.1.0.gem b/vendor/cache/redis-namespace-1.1.0.gem deleted file mode 100644 index 5f290f28..00000000 Binary files a/vendor/cache/redis-namespace-1.1.0.gem and /dev/null differ diff --git a/vendor/cache/redis-namespace-1.2.1.gem b/vendor/cache/redis-namespace-1.2.1.gem new file mode 100644 index 00000000..8d68f361 Binary files /dev/null and b/vendor/cache/redis-namespace-1.2.1.gem differ diff --git a/vendor/cache/rufus-scheduler-2.0.16.gem b/vendor/cache/rufus-scheduler-2.0.16.gem deleted file mode 100644 index 3612230c..00000000 Binary files a/vendor/cache/rufus-scheduler-2.0.16.gem and /dev/null differ diff --git a/vendor/cache/rufus-scheduler-2.0.17.gem b/vendor/cache/rufus-scheduler-2.0.17.gem new file mode 100644 index 00000000..43cadea3 Binary files /dev/null and b/vendor/cache/rufus-scheduler-2.0.17.gem differ diff --git a/vendor/cache/subexec-0.2.1.gem b/vendor/cache/subexec-0.2.1.gem deleted file mode 100644 index a8fd7513..00000000 Binary files a/vendor/cache/subexec-0.2.1.gem and /dev/null differ diff --git a/vendor/cache/subexec-0.2.2.gem b/vendor/cache/subexec-0.2.2.gem new file mode 100644 index 00000000..6e6e9df4 Binary files /dev/null and b/vendor/cache/subexec-0.2.2.gem differ diff --git a/vendor/cache/vegas-0.1.11.gem b/vendor/cache/vegas-0.1.11.gem new file mode 100644 index 00000000..a1c77fcf Binary files /dev/null and b/vendor/cache/vegas-0.1.11.gem differ diff --git a/vendor/cache/vegas-0.1.8.gem b/vendor/cache/vegas-0.1.8.gem deleted file mode 100644 index ea0aeec1..00000000 Binary files a/vendor/cache/vegas-0.1.8.gem and /dev/null differ diff --git a/vendor/cache/will_paginate-3.0.2.gem b/vendor/cache/will_paginate-3.0.2.gem deleted file mode 100644 index b6bfa672..00000000 Binary files a/vendor/cache/will_paginate-3.0.2.gem and /dev/null differ diff --git a/vendor/cache/will_paginate-3.0.4.gem b/vendor/cache/will_paginate-3.0.4.gem new file mode 100644 index 00000000..ac842cd6 Binary files /dev/null and b/vendor/cache/will_paginate-3.0.4.gem differ diff --git a/vendor/cache/yard-0.8.3.gem b/vendor/cache/yard-0.8.3.gem new file mode 100644 index 00000000..b1572548 Binary files /dev/null and b/vendor/cache/yard-0.8.3.gem differ