Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
redox committed Sep 17, 2013
0 parents commit 5844892
Show file tree
Hide file tree
Showing 20 changed files with 685 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .document
@@ -0,0 +1,5 @@
README.rdoc
lib/**/*.rb
bin/*
features/**/*.feature
LICENSE
24 changes: 24 additions & 0 deletions .gitignore
@@ -0,0 +1,24 @@
## MAC OS
.DS_Store

## TEXTMATE
*.tmproj
tmtags

## EMACS
*~
\#*
.\#*

## VIM
*.swp

## PROJECT::GENERAL
coverage
rdoc
pkg
.rvmrc

## PROJECT::SPECIFIC
Gemfile.lock
data.sqlite3
1 change: 1 addition & 0 deletions .rspec
@@ -0,0 +1 @@
--color
20 changes: 20 additions & 0 deletions Gemfile
@@ -0,0 +1,20 @@
source "http://rubygems.org"

gem 'json', '>= 1.5.1'
gem 'algoliasearch'

group :test do
gem 'rspec', '>= 2.5.0'
gem 'activerecord', '>= 3.0.7'
gem 'sqlite3'
gem 'autotest'
gem 'autotest-fsevent'
gem 'redgreen'
gem 'autotest-growl'
end

group :development do
gem 'jeweler'
gem 'will_paginate', '>= 2.3.15'
gem 'kaminari'
end
22 changes: 22 additions & 0 deletions LICENSE
@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2013 Algolia
http://www.algolia.com/

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
1 change: 1 addition & 0 deletions README.rdoc
@@ -0,0 +1 @@
WIP
51 changes: 51 additions & 0 deletions Rakefile
@@ -0,0 +1,51 @@
require 'rubygems'
require 'rake'

begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "algoliasearch-rails"
gem.summary = %Q{AlgoliaSearch integration to your favorite ORM}
gem.description = %Q{AlgoliaSearch integration to your favorite ORM}
gem.homepage = "http://github.com/algolia/algoliasearch-client-rails"
gem.email = "contact@algolia.com"
gem.authors = ["Algolia"]
gem.files.exclude 'spec/integration_spec.rb'
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
end

require "rspec/core/rake_task"
# RSpec 2.0
RSpec::Core::RakeTask.new(:spec) do |spec|
spec.pattern = 'spec/{algoliasearch,utilities}_spec.rb'
spec.rspec_opts = ['--backtrace']
end
task :default => :spec

desc "Generate code coverage"
RSpec::Core::RakeTask.new(:coverage) do |t|
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
t.rcov = true
t.rcov_opts = ['--exclude', 'spec']
end

desc "Run Integration Specs"
RSpec::Core::RakeTask.new(:integration) do |t|
t.pattern = "spec/integration_spec.rb" # don't need this, it's default.
t.rcov = true
t.rcov_opts = ['--exclude', 'spec']
end

require 'rdoc/task'
Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""

rdoc.rdoc_dir = 'rdoc'
rdoc.title = "AlgoliaSearch Rails #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
1 change: 1 addition & 0 deletions VERSION
@@ -0,0 +1 @@
1.0.0
68 changes: 68 additions & 0 deletions algoliasearch-rails.gemspec
@@ -0,0 +1,68 @@
# Generated by jeweler
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
# -*- encoding: utf-8 -*-

Gem::Specification.new do |s|
s.name = "algoliasearch-rails"
s.version = "1.0.0"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Algolia"]
s.date = "2013-09-17"
s.description = "AlgoliaSearch integration to your favorite ORM"
s.email = "contact@algolia.com"
s.extra_rdoc_files = [
"LICENSE",
"README.rdoc"
]
s.files = [
".document",
".rspec",
"Gemfile",
"LICENSE",
"README.rdoc",
"Rakefile",
"VERSION",
"algoliasearch-rails.gemspec",
"lib/algoliasearch-rails.rb",
"lib/algoliasearch/configuration.rb",
"lib/algoliasearch/pagination.rb",
"lib/algoliasearch/pagination/kaminari.rb",
"lib/algoliasearch/pagination/will_paginate.rb",
"lib/algoliasearch/railtie.rb",
"lib/algoliasearch/tasks/algoliasearch.rake",
"lib/algoliasearch/utilities.rb",
"spec/spec_helper.rb",
"spec/utilities_spec.rb"
]
s.homepage = "http://github.com/algolia/algoliasearch-client-rails"
s.require_paths = ["lib"]
s.rubygems_version = "1.8.25"
s.summary = "AlgoliaSearch integration to your favorite ORM"

if s.respond_to? :specification_version then
s.specification_version = 3

if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<json>, [">= 1.5.1"])
s.add_runtime_dependency(%q<algoliasearch>, [">= 0"])
s.add_development_dependency(%q<jeweler>, [">= 0"])
s.add_development_dependency(%q<will_paginate>, [">= 2.3.15"])
s.add_development_dependency(%q<kaminari>, [">= 0"])
else
s.add_dependency(%q<json>, [">= 1.5.1"])
s.add_dependency(%q<algoliasearch>, [">= 0"])
s.add_dependency(%q<jeweler>, [">= 0"])
s.add_dependency(%q<will_paginate>, [">= 2.3.15"])
s.add_dependency(%q<kaminari>, [">= 0"])
end
else
s.add_dependency(%q<json>, [">= 1.5.1"])
s.add_dependency(%q<algoliasearch>, [">= 0"])
s.add_dependency(%q<jeweler>, [">= 0"])
s.add_dependency(%q<will_paginate>, [">= 2.3.15"])
s.add_dependency(%q<kaminari>, [">= 0"])
end
end

147 changes: 147 additions & 0 deletions lib/algoliasearch-rails.rb
@@ -0,0 +1,147 @@
begin
require "rubygems"
require "bundler"

Bundler.setup :default
rescue => e
puts "AlgoliaSearch: #{e.message}"
end
require 'algoliasearch'

require 'algoliasearch/utilities'

if defined? Rails
begin
require 'algoliasearch/railtie'
rescue LoadError
end
end

module AlgoliaSearch

class NotConfigured < StandardError; end
class BadConfiguration < StandardError; end
class NoBlockGiven < StandardError; end

autoload :Configuration, 'algoliasearch/configuration'
extend Configuration

autoload :Pagination, 'algoliasearch/pagination'

class << self
attr_reader :included_in

def included(klass)
@included_in ||= []
@included_in << klass
@included_in.uniq!

klass.send :include, InstanceMethods
klass.extend ClassMethods
end

end

class IndexOptions
OPTIONS = [:attributesToIndex, :minWordSizeForApprox1,
:minWordSizeForApprox2, :hitsPerPage, :attributesToRetrieve,
:attributesToHighlight, :attributesToSnippet, :attributesToIndex,
:ranking, :customRanking, :queryType]

attr_accessor *OPTIONS

def initialize(block)
instance_exec(&block) if block
end

def attribute(*names)
@attributesToIndex ||= []
@attributesToIndex += names.map { |name| name.to_s }
end

def to_settings
settings = {}
OPTIONS.each do |k|
v = send(k)
settings[k] = v if !v.nil?
end
settings
end
end

# these are the class methods added when AlgoliaSearch is included
# They're kept to a minimum to prevent namespace pollution
module ClassMethods
def algoliasearch(options = {}, &block)
@index_options = IndexOptions.new(block_given? ? Proc.new : nil)

unless options[:auto_index] == false
before_save :mark_for_auto_indexing if respond_to?(:before_save)
after_save :perform_index_tasks if respond_to?(:after_save)
end
unless options[:auto_remove] == false
after_destroy { |searchable| searchable.remove_from_index! } if respond_to?(:after_destroy)
end

@options = { type: model_name, per_page: @index_options.hitsPerPage || 10, page: 1 }.merge(options)
init
end

def reindex!(batch_size = 1000)
find_in_batches(batch_size: batch_size) do |group|
@index.add_objects!(group)
end
end

def index!(object)
@index.add_object!(object)
end

def remove_from_index!(object)
@index.delete_object(object.id)
end

def clear_index!
@index.delete
@index = nil
init
end

def search(q)
json = @index.search(q)
results = json['hits'].map do |hit|
Object.const_get(@options[:type]).find(hit['id'])
end
AlgoliaSearch::Pagination.create(results, json['nbHits'].to_i, @options)
end

def init
@index ||= Algolia::Index.new(model_name)
@index.set_settings(@index_options.to_settings)
end

end

# these are the instance methods included
module InstanceMethods
def index!
self.class.index! self
end

def remove_from_index!
self.class.remove_from_index! self
end

private

def mark_for_auto_indexing
@auto_indexing = true
end

def perform_index_tasks
return if !@auto_indexing
index!
remove_instance_variable :@auto_indexing
end
end
end
15 changes: 15 additions & 0 deletions lib/algoliasearch/configuration.rb
@@ -0,0 +1,15 @@
module AlgoliaSearch
module Configuration
def configuration
@@configuration || raise(NotConfigured, "Please configure AlgoliaSearch. Set AlgoliaSearch.configuration = {application_id: 'YOUR_APPLICATION_ID', api_key: 'YOUR_API_KEY'}")
end

def configuration=(new_configuration)
# the default pagination backend is WillPaginate
@@configuration = new_configuration.tap do |_config|
_config.replace({ :pagination_backend => :will_paginate }.merge(_config)) if _config.is_a?(Hash)
end
Algolia.init application_id: @@configuration[:application_id], api_key: @@configuration[:api_key]
end
end
end
18 changes: 18 additions & 0 deletions lib/algoliasearch/pagination.rb
@@ -0,0 +1,18 @@
module AlgoliaSearch
module Pagination

autoload :WillPaginate, 'algoliasearch/pagination/will_paginate'
autoload :Kaminari, 'algoliasearch/pagination/kaminari'

def self.create(results, total_hits, options = {})
begin
backend = AlgoliaSearch.configuration[:pagination_backend].to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } # classify pagination backend name
page = Object.const_get(:AlgoliaSearch).const_get(:Pagination).const_get(backend).create(results, total_hits, options)
page
rescue NameError
raise(BadConfiguration, "Unknown pagination backend")
end
end

end
end

0 comments on commit 5844892

Please sign in to comment.