ts_vector_tags 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ts_vector_tags.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # TsVectorTags
2
+
3
+ A simple tagging library that uses the (fairly exotic) `ts_vector` type in PostgreSQL on top of ActiveRecord.
4
+
5
+ ## Requirements
6
+
7
+ * ActiveRecord
8
+ * a field `tags_vector` of type `tsvector` on the underlying table
9
+
10
+ ## Usage
11
+
12
+ Tags can be set using either an array, or a comma separated list.
13
+
14
+ class Post < ActiveRecord::Base
15
+ include TsVectorTags
16
+
17
+ # ...
18
+ end
19
+
20
+ post = Post.new
21
+
22
+ post.tags = "bing, bong"
23
+ post.tags
24
+ => ['bing', 'bong']
25
+
26
+ post.tags = ['bing', 'bong']
27
+ post.tags
28
+ => ['bing', 'bong']
29
+
30
+ The including class has a scope:
31
+
32
+ Post.with_tags('Paris, Texas')
33
+ Post.with_tags('Paris', 'Texas')
34
+
35
+ Tags are normalized:
36
+
37
+ post.tags = [' wtf#$%^ &*??!']
38
+ post.tags
39
+ => ['wtf']
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,42 @@
1
+ require "ts_vector_tags/version"
2
+
3
+ module TsVectorTags
4
+ class MissingAttributeError < Exception; end
5
+
6
+ module Standardizer
7
+ class << self
8
+ def tagify(tags)
9
+ tags = tags.split(/\s*,\s*/) if tags.is_a?(String)
10
+ tags.map!{ |tag| self.normalize(tag) }
11
+ end
12
+
13
+ def normalize(tag)
14
+ tag.downcase.gsub(/[^[:alnum:]]/, "")
15
+ end
16
+ end
17
+ end
18
+
19
+ def self.included(base)
20
+ unless base.instance_methods.include?(:tags_vector) && base.instance_methods.include?(:tags_vector=)
21
+ msg = "The TsVectorTags mixin assumes that the underlying PostgreSQL table has a field `tags_vector` of type tsvector"
22
+ raise TsVectorTags::MissingAttributeError.new(msg)
23
+ end
24
+
25
+ base.class_eval do
26
+ scope :with_tags, lambda { |tags|
27
+ where("tags_vector @@ to_tsquery('simple', ?) ", TsVectorTags::Standardizer.tagify(tags).join(' & '))
28
+ }
29
+ end
30
+ end
31
+
32
+ def tags=(value)
33
+ return self.tags_vector = "" if value.nil?
34
+ self.tags_vector = TsVectorTags::Standardizer.tagify(value).map{ |tag| "'#{tag}'" }.join(' ')
35
+ end
36
+
37
+ def tags
38
+ return [] unless self.tags_vector
39
+ self.tags_vector.scan(/'(.+?)'/).flatten
40
+ end
41
+
42
+ end
@@ -0,0 +1,3 @@
1
+ module TsVectorTags
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1 @@
1
+ require 'ts_vector_tags'
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe TsVectorTags::Standardizer do
4
+ it "normalizes" do
5
+ TsVectorTags::Standardizer.normalize('abc-!@ #$ %^&*()123').should eq('abc123')
6
+ end
7
+
8
+ it "splits and normalizes a string" do
9
+ TsVectorTags::Standardizer.tagify('abc-!@ #$ , %^&*()123').should eq(['abc', '123'])
10
+ end
11
+
12
+ it "normalizes tags in an arrayarray" do
13
+ TsVectorTags::Standardizer.tagify(['abc-!@ #$ ', ' %^&*()123']).should eq(['abc', '123'])
14
+ end
15
+ end
16
+
17
+ class Thing
18
+ def self.scope(name, options = {}); end
19
+
20
+ attr_accessor :tags_vector
21
+ include TsVectorTags
22
+
23
+ end
24
+
25
+ describe TsVectorTags do
26
+ describe "accessors" do
27
+ let(:thing) { Thing.new }
28
+
29
+ describe "deleting tags" do
30
+ before :each do
31
+ thing.tags_vector = "'bing' 'bong'"
32
+ end
33
+
34
+ it "uses nil" do
35
+ thing.tags = nil
36
+ thing.tags.should eq([])
37
+ end
38
+
39
+ it "removes tags with an empty array" do
40
+ thing.tags = []
41
+ thing.tags.should eq([])
42
+ end
43
+
44
+ it "removes tags with an empty string" do
45
+ thing.tags = ""
46
+ thing.tags.should eq([])
47
+ end
48
+ end
49
+
50
+ it "assigns comma-separated strings" do
51
+ thing.tags = "bing, bong"
52
+ thing.tags_vector.should eq("'bing' 'bong'")
53
+ end
54
+
55
+ it "assigns arrays" do
56
+ thing.tags = ["bing", "bong"]
57
+ thing.tags_vector.should eq("'bing' 'bong'")
58
+ end
59
+
60
+ it "reads tag vectors" do
61
+ thing.tags_vector = "'bing' 'bong'"
62
+ thing.tags.should eq(['bing', 'bong'])
63
+ end
64
+ end
65
+
66
+ context "with the required attributes" do
67
+ it "does not raise an exception" do
68
+ lambda {
69
+ class GoodThing
70
+ def self.scope(name, options = {}); end
71
+ attr_accessor :tags_vector
72
+ include TsVectorTags
73
+ end
74
+ }.should_not raise_error
75
+ end
76
+ end
77
+
78
+ context "without a tags_vector attribute" do
79
+ it "raises a helpful exception when no tags_vector writer is present" do
80
+ lambda {
81
+ class OneBadThing
82
+ def self.scope(name, options = {}); end
83
+
84
+ attr_reader :tags_vector
85
+ include TsVectorTags
86
+ end
87
+ }.should raise_error(TsVectorTags::MissingAttributeError)
88
+ end
89
+
90
+ it "raises a helpful exception when no tags_vector reader is pressent" do
91
+ lambda {
92
+ class AnotherBadThing
93
+ def self.scope(name, options = {}); end
94
+
95
+ attr_writer :tags_vector
96
+ include TsVectorTags
97
+ end
98
+ }.should raise_error(TsVectorTags::MissingAttributeError)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ts_vector_tags/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ts_vector_tags"
7
+ s.version = TsVectorTags::VERSION
8
+ s.authors = ["Simen Svale Skogsrud", "Katrina Owen"]
9
+ s.email = ["simen@bengler.no", "katrina@bengler.no"]
10
+ s.homepage = ""
11
+ s.summary = %q{Super simple tag mixin for postgresql and activerecord}
12
+ s.description = %q{Extremely simple, if somewhat exotic, mixin that uses the tsvector feature in postgresql to add tags to an ActiveRecord model.}
13
+
14
+ s.rubyforge_project = "ts_vector_tags"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ts_vector_tags
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Simen Svale Skogsrud
9
+ - Katrina Owen
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-11-18 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: &2161651360 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *2161651360
26
+ description: Extremely simple, if somewhat exotic, mixin that uses the tsvector feature
27
+ in postgresql to add tags to an ActiveRecord model.
28
+ email:
29
+ - simen@bengler.no
30
+ - katrina@bengler.no
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - .gitignore
36
+ - .rspec
37
+ - Gemfile
38
+ - README.md
39
+ - Rakefile
40
+ - lib/ts_vector_tags.rb
41
+ - lib/ts_vector_tags/version.rb
42
+ - spec/spec_helper.rb
43
+ - spec/ts_vector_tags_spec.rb
44
+ - ts_vector_tags.gemspec
45
+ homepage: ''
46
+ licenses: []
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project: ts_vector_tags
65
+ rubygems_version: 1.8.10
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Super simple tag mixin for postgresql and activerecord
69
+ test_files: []