Rfizzy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ <!DOCTYPE html> <html> <head> <title>installation_and_setup.rb</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <div id="jump_to"> Jump To &hellip; <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="FullTextSearch.html"> FullTextSearch.rb </a> <a class="source" href="Tagging.html"> Tagging.rb </a> <a class="source" href="installation_and_setup.html"> installation_and_setup.rb </a> </div> </div> </div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> installation_and_setup.rb </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-1">&#182;</a> </div> <p>Rfizzy is a ORM agnostic library where full text searching, tagging and social graph can be implemented in very few lines of code and extrmly low learning curve. Basically plug'n'play. </p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-2">&#182;</a> </div> <hr /> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-3">&#182;</a> </div> <h2>Installation</h2>
2
+
3
+ <h3>Dependency</h3> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-4">&#182;</a> </div> <p>In your gemfile.</p> </td> <td class="code"> <div class="highlight"><pre><span class="n">gem</span> <span class="s2">&quot;Rfizzy&quot;</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-5">&#182;</a> </div> <pre><code> bundle install
4
+ </code></pre> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-6">&#182;</a> </div> <h3>Initializer</h3>
5
+
6
+ <h4>inside config/initializers/load_rfizzy.rb</h4> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-7">&#182;</a> </div> <p>Create an Rfizzy with your namespace.
7
+ Default namespace is "Rfizzy".</p> </td> <td class="code"> <div class="highlight"><pre><span class="no">PostSearcher</span> <span class="o">=</span> <span class="no">Rfizzy</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:namespace</span> <span class="o">=&gt;</span> <span class="s2">&quot;PostSearcher&quot;</span><span class="p">,</span> <span class="ss">:redis</span> <span class="o">=&gt;</span> <span class="n">R</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-8">&#182;</a> </div> <p>Make sure to pass a reference to the Redis connection.</p> </td> <td class="code"> <div class="highlight"><pre><span class="no">FullOfShizzly</span> <span class="o">=</span> <span class="no">Rfizzy</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:redis</span> <span class="o">=&gt;</span> <span class="n">R</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-9">&#182;</a> </div> <p>This also works (Redis will default to localhost).</p> </td> <td class="code"> <div class="highlight"><pre><span class="no">KissMyTizzly</span> <span class="o">=</span> <span class="no">Rfizzy</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Redis</span><span class="o">.</span><span class="n">new</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-10">&#182;</a> </div> <h4>RedisMissingException will be raised if no Redis is passed - that means if the @redis attribute reader is nil.</h4> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-11">&#182;</a> </div> <hr /> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-12">&#182;</a> </div> <h2>But wait! ... there's more</h2>
8
+
9
+ <h3>What can I do with this?</h3>
10
+
11
+ <ul>
12
+ <li><p><a href="https://github.com/fabrik42/acts_as_api/wiki/">Tagging - find posts through tagging, find tags through posts</a></p></li>
13
+ <li><p><a href="https://github.com/fabrik42/acts_as_api/wiki/Calling-a-method-of-the-model">Full text search -through association or attributes</a></p></li>
14
+ <li><p>Social Graph, friends, followers, followees</p></li>
15
+ </ul>
16
+
17
+ <p>You can find more advanced examples by using the "Jumper" placed at the upper right corner</p> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-13">&#182;</a> </div> <hr /> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="pilwrap"> <a class="pilcrow" href="#section-14">&#182;</a> </div> <h2>Links</h2>
18
+
19
+ <ul>
20
+ <li><a href="https://github.com/seivan/Rfizzy">Check out the source code on Github</a></li>
21
+ <li><a href="https://github.com/seivan/Rfizzy/issues">Found a bug or do you have a feature request?</a></li>
22
+ <li><a href="http://travis-ci.org/seivan/Rfizzy">Continues build and testing status</a></li>
23
+ </ul> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> </tbody> </table> </div> </body> </html>
@@ -0,0 +1,95 @@
1
+ ### Full Text Searching
2
+ #
3
+
4
+ #We are using an ActiveRecord model here.
5
+ class Tweet < ActiveRecord::Base
6
+
7
+ # With it's complimentary callbacks
8
+ after_create :create_search_index
9
+ before_destroy :destroy_search_index
10
+
11
+ # But you can just as well use any other orm that gives it's documents/tables unique identifiers (id's)
12
+
13
+ private
14
+ def create_search_index
15
+ # So we set the namespace we want the searching to be on by setting it's name :tweet_text_content
16
+ # We pass the tweets id to it so we can find it
17
+ #And we pass the tweets text_content, so we can match our search
18
+ FullTextSearch.create_index :attribute_namespace => :tweet_text_content,
19
+ :document_id => id
20
+
21
+ end
22
+
23
+ def destroy_search_index
24
+ #Same applies here to as to create, except we are removing the records indices
25
+ #No need to pass the words here, just what records index to remove
26
+ FullTextSearch.destroy_index :attribute_namespace => :tweet_text_content,
27
+ :document_id => id
28
+
29
+ end
30
+ end
31
+
32
+
33
+
34
+ #Now you can search through your indices easily
35
+ #By passing what namespace you want to search on with the words you are searching with
36
+ set_of_ids = FullTextSearch.search_index :attribute_namespace => :tweet_text_content,
37
+ :search => "jquery mobile"
38
+
39
+ #And no matter if you're using Mongoid, ActiveRecord or DataMapper, you can now query the ID's.
40
+ Article.where(:id => set_of_ids)
41
+
42
+ # ***
43
+
44
+ ### Association
45
+
46
+ #Now lets assume we want to limit to who can search what by setting an association
47
+ def create_search_index
48
+ #Notice the assoction on the tweets user.id
49
+ FullTextSearch.create_index :attribute_namespace => :tweet_text_content,
50
+ :document_id => id,
51
+ :words => text_content,
52
+ :association => user.id
53
+
54
+ end
55
+
56
+ def destroy_search_index
57
+ #Same concept applies here as well
58
+ FullTextSearch.destroy_index :attribute_namespace => :tweet_text_content,
59
+ :document_id => id,
60
+ :association => user.id,
61
+
62
+ end
63
+
64
+ #And when the current_user searches he will only find on his own associated tweets
65
+ set_of_ids = FullTextSearch.search_index :attribute_namespace => :tweet_text_content,
66
+ :search => "jQuery map",
67
+ :association => current_user.id
68
+ # ***
69
+
70
+ ### But wait! ... there's more
71
+ #
72
+ #### What can I do with this?
73
+ #
74
+ #
75
+ # * [Tagging - find posts through tagging, find tags through posts][tagging]
76
+ #
77
+ # * [Full text search -through association or attributes][search]
78
+ #
79
+ # * Social Graph, friends, followers, followees
80
+ #
81
+ # You can find more advanced examples by using the "Jumper" placed at the upper right corner
82
+ #
83
+ # [tagging]: https://github.com/fabrik42/acts_as_api/wiki/
84
+ # [search]: https://github.com/fabrik42/acts_as_api/wiki/Calling-a-method-of-the-model
85
+
86
+ # ***
87
+
88
+ ### Links
89
+ # * [Check out the source code on Github][source]
90
+ # * [Found a bug or do you have a feature request?][issue]
91
+ # * [Continues build and testing status][build]
92
+ #
93
+ # [source]: https://github.com/seivan/Rfizzy
94
+ # [issue]: https://github.com/seivan/Rfizzy/issues
95
+ # [build]: http://travis-ci.org/seivan/Rfizzy
@@ -0,0 +1,94 @@
1
+ ### Tagging
2
+ #
3
+
4
+ #We are using an ActiveRecord model here.
5
+ class Article < ActiveRecord::Base
6
+
7
+ # With it's complimentary callbacks
8
+ after_create :create_search_index
9
+ before_destroy :destroy_search_index
10
+
11
+ # But you can just as well use any other orm that gives it's documents/tables unique identifiers (id's)
12
+
13
+ private
14
+ def create_search_index
15
+ # So we set the namespace we want the searching to be on by setting it's name :article_tags
16
+ # We pass the article's id to it so we can find it
17
+ #And we pass the articles tags as an array
18
+ #Naturally the tags can just be a virtual attribute as an array of tags when posting the article
19
+ TaggyMcFaggy.create_index :attribute_namespace => :article_tags,
20
+ :document_id => id,
21
+ :words => tags
22
+
23
+ end
24
+
25
+ def destroy_search_index
26
+ #Same concept applies here, except we don't care about the tags themselves.
27
+ TaggyMcFaggy.destroy_index :attribute_namespace => :article_text,
28
+ :document_id => id
29
+
30
+ end
31
+ end
32
+
33
+
34
+
35
+ #Pass in an array of tags, or a single tag as a text
36
+ set_of_ids = TaggyMcFaggy.search_index :attribute_namespace => :article_text,
37
+ :search => "food"
38
+
39
+ #And no matter if you're using Mongoid, ActiveRecord or DataMapper, you can now query the ID's.
40
+ Article.where(:id => set_of_ids)
41
+
42
+
43
+ #Now lets assume we want to limit to who can search what by setting an association
44
+
45
+ def create_search_index
46
+ #Notice the assoction on article.user.id
47
+ TaggyMcFaggy.create_index :attribute_namespace => :article_text,
48
+ :document_id => id,
49
+ :words => text_content,
50
+ :association => user.id
51
+
52
+ end
53
+
54
+ def destroy_search_index
55
+ #Same concept applies here, except we really do not care about the tags when deleting
56
+ TaggyMcFaggy.destroy_index :attribute_namespace => :article_text,
57
+ :document_id => id,
58
+ :association => user.id,
59
+
60
+ end
61
+
62
+ #And when the current_user searches he will only find on his own associated tweets
63
+ set_of_ids = TaggyMcFaggy.search_index :attribute_namespace => :article_text,
64
+ :association => current_user.id,
65
+ :search => ["Food", "gaming", "Balls"]
66
+
67
+ # ***
68
+
69
+ ### But wait! ... there's more
70
+ #
71
+ #### What can I do with this?
72
+ #
73
+ #
74
+ # * [Tagging - find posts through tagging, find tags through posts][tagging]
75
+ #
76
+ # * [Full text search -through association or attributes][search]
77
+ #
78
+ # * Social Graph, friends, followers, followees
79
+ #
80
+ # You can find more advanced examples by using the "Jumper" placed at the upper right corner
81
+ #
82
+ # [tagging]: https://github.com/fabrik42/acts_as_api/wiki/
83
+ # [search]: https://github.com/fabrik42/acts_as_api/wiki/Calling-a-method-of-the-model
84
+
85
+ # ***
86
+
87
+ ### Links
88
+ # * [Check out the source code on Github][source]
89
+ # * [Found a bug or do you have a feature request?][issue]
90
+ # * [Continues build and testing status][build]
91
+ #
92
+ # [source]: https://github.com/seivan/Rfizzy
93
+ # [issue]: https://github.com/seivan/Rfizzy/issues
94
+ # [build]: http://travis-ci.org/seivan/Rfizzy
@@ -0,0 +1,55 @@
1
+ #Rfizzy is a ORM agnostic library where full text searching, tagging and social graph can be implemented in very few lines of code and extrmly low learning curve. Basically plug'n'play.
2
+
3
+ # ***
4
+
5
+ ###Installation
6
+ #### Dependency
7
+
8
+ #In your gemfile.
9
+ gem "Rfizzy"
10
+ # bundle install
11
+
12
+ #### Initializer
13
+ ##### inside config/initializers/load_rfizzy.rb
14
+
15
+ #Create an Rfizzy with your namespace.
16
+ # Default namespace is "Rfizzy".
17
+ PostSearcher = Rfizzy.new(:namespace => "PostSearcher", :redis => R)
18
+
19
+ #Make sure to pass a reference to the Redis connection.
20
+ FullOfShizzly = Rfizzy.new(:redis => R)
21
+
22
+
23
+ #This also works (Redis will default to localhost).
24
+ KissMyTizzly = Rfizzy.new(Redis.new)
25
+ ##### RedisMissingException will be raised if no Redis is passed - that means if the @redis attribute reader is nil.
26
+
27
+
28
+ # ***
29
+
30
+ ### But wait! ... there's more
31
+ #
32
+ #### What can I do with this?
33
+ #
34
+ #
35
+ # * [Tagging - find posts through tagging, find tags through posts][tagging]
36
+ #
37
+ # * [Full text search -through association or attributes][search]
38
+ #
39
+ # * Social Graph, friends, followers, followees
40
+ #
41
+ # You can find more advanced examples by using the "Jumper" placed at the upper right corner
42
+ #
43
+ # [tagging]: https://github.com/fabrik42/acts_as_api/wiki/
44
+ # [search]: https://github.com/fabrik42/acts_as_api/wiki/Calling-a-method-of-the-model
45
+
46
+ # ***
47
+
48
+ ### Links
49
+ # * [Check out the source code on Github][source]
50
+ # * [Found a bug or do you have a feature request?][issue]
51
+ # * [Continues build and testing status][build]
52
+ #
53
+ # [source]: https://github.com/seivan/Rfizzy
54
+ # [issue]: https://github.com/seivan/Rfizzy/issues
55
+ # [build]: http://travis-ci.org/seivan/Rfizzy
@@ -0,0 +1,83 @@
1
+
2
+ class Rfizzy
3
+ attr_reader :namespace, :redis
4
+
5
+ def initialize(params)
6
+ if params.instance_of? Hash
7
+ possible_namespace = params[:namespace].match(/[\w]+/).to_s unless blank? params[:namespace]
8
+ if blank? possible_namespace
9
+ @namespace = "Rfizzy:"
10
+ else
11
+ @namespace = possible_namespace
12
+ end
13
+ raise RedisMissingException, "You need to pass a redis driver {:redis => your_driver}" if params[:redis] == nil
14
+ @redis = params[:redis]
15
+ else
16
+ raise RedisMissingException, "You need to pass a redis driver {:redis => your_driver}" if params == nil || !params.instance_of?(Redis)
17
+ @redis = params
18
+ @namespace = "Rfizzy:"
19
+ end
20
+ end
21
+
22
+ def create_index(params)
23
+ words = params[:words]
24
+ words = words.split(" ") if words.instance_of? String
25
+ @redis.multi do |red|
26
+ words.each do |word|
27
+ red.sadd "#{@namespace}:document:#{params[:association]}:#{params[:attribute_namespace]}:#{params[:document_id]}", word
28
+ red.sadd "#{@namespace}:word:#{params[:association]}:#{params[:attribute_namespace]}:#{word}", params[:document_id]
29
+ end
30
+ end
31
+ end
32
+
33
+ def search_index(params)
34
+ interstore_cache = Time.now.to_f.to_s
35
+ results = []
36
+ search_keys_array = search_keys(params)
37
+ @redis.multi do |red|
38
+ red.sinterstore interstore_cache, *search_keys_array
39
+ end
40
+ results = @redis.smembers interstore_cache
41
+ @redis.multi do |red|
42
+ red.del interstore_cache
43
+ end
44
+
45
+ # puts @redis.sinterstore interstore_cache, *search_keys_array
46
+ # puts search_keys_array.inspect
47
+ # results = @redis.smembers interstore_cache
48
+ results
49
+ end
50
+
51
+ def destroy_index(params)
52
+ document_id = params[:document_id]
53
+ words_array = @redis.smembers "#{@namespace}:document:#{params[:association]}:#{params[:attribute_namespace]}:#{document_id}"
54
+ params.merge!({:search => words_array})
55
+ keys_to_delete = search_keys(params)
56
+ @redis.multi do |red|
57
+ keys_to_delete.each do |key|
58
+ red.srem key, document_id
59
+ end
60
+ red.del "#{@namespace}:document:#{params[:association]}:#{params[:attribute_namespace]}:document_id"
61
+ end
62
+ end
63
+
64
+ private
65
+ def search_keys(params)
66
+ association = params[:association]
67
+ attribute_namespace = params[:attribute_namespace]
68
+ search_text = params[:search]
69
+ search_text = search_text.split(" ") if search_text.instance_of? String
70
+ search_keys = search_text.map do |word|
71
+ "#{@namespace}:word:#{association}:#{attribute_namespace}:#{word}"
72
+ end
73
+ search_keys
74
+ end
75
+
76
+ def blank?(obj)
77
+ obj.strip! if obj.instance_of? String
78
+ obj.respond_to?(:empty?) ? obj.empty? : !obj
79
+ end
80
+
81
+
82
+ end
83
+
Binary file
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+
3
+ describe Rfizzy, "initialize" do
4
+
5
+ context "should raise error if" do
6
+ it "initialize with no redis passed to it" do
7
+ lambda { Rfizzy.new() }.should raise_error
8
+ end
9
+ end
10
+ context "initialize with arguments" do
11
+ subject {Rfizzy.new(:namespace => namespace, :redis => R) }
12
+ let(:namespace) { "FullTextSearch" }
13
+ it { should be_an_instance_of Rfizzy }
14
+ it { subject.namespace.should == namespace }
15
+ end
16
+
17
+ context "initialize with blank namespace" do
18
+ subject {Rfizzy.new(:namespace => namespace, :redis => R) }
19
+ let(:namespace) { " " }
20
+ it { subject.namespace.should == "Rfizzy:" }
21
+ end
22
+
23
+ context "initialize with no namespace" do
24
+ context "with hash param for redis" do
25
+ subject { Rfizzy.new(:redis => R) }
26
+ it { subject.namespace.should == "Rfizzy:" }
27
+ end
28
+ context "with just a redis object as param" do
29
+ before(:each) do
30
+ lambda { Rfizzy.new(R) }.should_not raise_error
31
+ end
32
+ subject {Rfizzy.new(R)}
33
+ it { subject.namespace.should == "Rfizzy:" }
34
+ end
35
+
36
+ end
37
+ end
Binary file
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+
3
+ describe Rfizzy, "search create_index" do
4
+ let(:namespace) { SearchFactory.instance.namespace }
5
+ let(:tweet) { SearchFactory.instance.tweet }
6
+ let(:document_key_name) { SearchFactory.instance.document_key_name }
7
+
8
+ let(:word_key_name) {SearchFactory.instance.word_key_name}
9
+ let(:word_list) { SearchFactory.instance.word_list }
10
+ let(:word_size) { SearchFactory.instance.word_size }
11
+
12
+ before(:each) do
13
+ @search = Rfizzy.new({:redis => R, :namespace => namespace })
14
+ @search.create_index(tweet)
15
+ end
16
+
17
+ context "create an index with" do
18
+ context "a document that" do
19
+ it "should exist " do
20
+ R.exists(document_key_name).should be_true
21
+ end
22
+ it "should be of the type set" do
23
+ R.type(document_key_name).should == "set"
24
+ end
25
+ it "should contain words" do
26
+ R.smembers(document_key_name).should have(word_size).things
27
+ end
28
+ end
29
+
30
+ context "a set for each word that" do
31
+ it "should exist" do
32
+ word_list.each do |word|
33
+ R.exists("#{word_key_name}#{word}").should be_true
34
+ end
35
+ end
36
+ it "should refer to the document_id" do
37
+ word_list.each do |word|
38
+ R.sismember("#{word_key_name}#{word}", tweet[:document_id])
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ require "spec_helper"
2
+ describe Rfizzy, "search destroy_index" do
3
+ let(:namespace) { SearchFactory.instance.namespace }
4
+ let(:tweet) { SearchFactory.instance.tweet }
5
+ let(:document_key_name) { SearchFactory.instance.document_key_name }
6
+ let(:document_id) { SearchFactory.instance.document_id }
7
+ let(:word_key_name) {SearchFactory.instance.word_key_name}
8
+ let(:word_list) { SearchFactory.instance.word_list }
9
+ let(:word_size) { SearchFactory.instance.word_size }
10
+ let(:search_parameters) do
11
+ {:association => tweet[:association],
12
+ :attribute_namespace => tweet[:attribute_namespace],
13
+ :search =>"should"}
14
+ end
15
+
16
+ before(:each) do
17
+ @search = Rfizzy.new({:redis => R, :namespace => namespace })
18
+ @search.create_index(tweet)
19
+ end
20
+
21
+ context "destroy an index with" do
22
+ # context "a document that exist" do
23
+ # subject { @search.search_index(search_parameters) }
24
+ # it { should_not be_empty }
25
+ # it { should include(document_id) }
26
+ # end
27
+ it "should no longer return results" do
28
+ @search.destroy_index(tweet)
29
+ results = @search.search_index(search_parameters)
30
+ results.should_not include(document_id)
31
+ end
32
+ end
33
+ end