ts_vector_tags 0.0.1
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.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/README.md +39 -0
- data/Rakefile +1 -0
- data/lib/ts_vector_tags.rb +42 -0
- data/lib/ts_vector_tags/version.rb +3 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/ts_vector_tags_spec.rb +101 -0
- data/ts_vector_tags.gemspec +22 -0
- metadata +69 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
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
|
data/spec/spec_helper.rb
ADDED
@@ -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: []
|