mongoid-tags 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ # Because this is a gem, ignore Gemfile.lock:
2
+
3
+ Gemfile.lock
4
+
5
+ # And because this is Ruby, ignore the following
6
+ # (source: https://github.com/github/gitignore/blob/master/Ruby.gitignore):
7
+
8
+ *.gem
9
+ *.rbc
10
+ .bundle
11
+ .config
12
+ coverage
13
+ InstalledFiles
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
21
+
22
+ # YARD artifacts
23
+ .yardoc
24
+ _yardoc
25
+ doc/
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.0.1 (December 22, 2012)
2
+
3
+ * First version.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'minitest'
6
+ gem 'rake'
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # mongoid-tags
2
+
3
+ Mongoid::Tags adds a simple tagging system to your Mongoid documents,
4
+ and allows you to query them using a boolean search syntax.
5
+
6
+
7
+ ## Installation
8
+
9
+ In your Gemfile:
10
+
11
+ ```ruby
12
+ gem 'mongoid-tags'
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```ruby
18
+ class Document
19
+ include Mongoid::Document
20
+ include Mongoid::Tags
21
+ end
22
+ ```
23
+
24
+ ```ruby
25
+ # Documents tagged foo || bar
26
+ Document.tagged('foo bar')
27
+
28
+ # Documents tagged foo && bar
29
+ Document.tagged('+foo bar')
30
+
31
+ # Documents tagged foo, but !bar
32
+ Document.tagged('foo -bar')
33
+ ```
34
+
35
+ Be sure to checkout spec/integration_spec.rb for more examples. By the way, `tagged` returns a `Mongoid::Criteria` object so you can chain it to your existing criteria, e.g: `Document.where(published: true).tagged('foo').desc(:created_at)`
36
+
37
+ ## Contributing
38
+
39
+ 1. Fork it
40
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
41
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
42
+ 4. Push to the branch (`git push origin my-new-feature`)
43
+ 5. Create new Pull Request
44
+
45
+ ## Copyright
46
+
47
+ (The MIT license)
48
+
49
+ Copyright (c) 2012 Mario Uher
50
+
51
+ Permission is hereby granted, free of charge, to any person obtaining
52
+ a copy of this software and associated documentation files (the
53
+ "Software"), to deal in the Software without restriction, including
54
+ without limitation the rights to use, copy, modify, merge, publish,
55
+ distribute, sublicense, and/or sell copies of the Software, and to
56
+ permit persons to whom the Software is furnished to do so, subject to
57
+ the following conditions:
58
+
59
+ The above copyright notice and this permission notice shall be
60
+ included in all copies or substantial portions of the Software.
61
+
62
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
63
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
64
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
65
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
66
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
67
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
68
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.test_files = Dir.glob('spec/**/*_spec.rb')
6
+ end
7
+
8
+ task(default: :test)
@@ -0,0 +1,5 @@
1
+ module Mongoid
2
+ module Tags
3
+ VERSION = '0.1.1'
4
+ end
5
+ end
@@ -0,0 +1,69 @@
1
+ require 'active_support'
2
+ require 'mongoid'
3
+ require 'treetop'
4
+
5
+ module Mongoid
6
+ module Tags
7
+ extend ActiveSupport::Concern
8
+
9
+ included do |base|
10
+ field :tags, type: Array, default: []
11
+ index tags: 1
12
+ end
13
+
14
+ module ClassMethods
15
+ def selector(query)
16
+ parser.parse(query).tap do |result|
17
+ raise Error, parser.failure_reason unless result
18
+ end.to_criteria
19
+ end
20
+
21
+ def tagged(query)
22
+ where(tags: selector(query))
23
+ end
24
+
25
+ private
26
+ def parser
27
+ @parser ||= Treetop.load(File.expand_path('../tags.tt', __FILE__)).new
28
+ end
29
+ end
30
+
31
+ class Query < Treetop::Runtime::SyntaxNode
32
+ def to_criteria(tags = nil)
33
+ {}.tap do |criteria|
34
+ (tags.presence || elements).each do |tag|
35
+ (criteria[tag.operator.selector] ||= []) << tag.to_criteria if tag.is_a? Tag
36
+
37
+ criteria.merge!(to_criteria(tag.elements)) { |key, first, second| first + second } if tag.elements.present?
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ class Tag < Treetop::Runtime::SyntaxNode
44
+ def to_criteria
45
+ tag.text_value
46
+ end
47
+ end
48
+
49
+ class OrOperator < Treetop::Runtime::SyntaxNode
50
+ def selector
51
+ '$in'
52
+ end
53
+ end
54
+
55
+ class AndOperator < Treetop::Runtime::SyntaxNode
56
+ def selector
57
+ '$all'
58
+ end
59
+ end
60
+
61
+ class NotOperator < Treetop::Runtime::SyntaxNode
62
+ def selector
63
+ '$nin'
64
+ end
65
+ end
66
+
67
+ class Error < StandardError; end
68
+ end
69
+ end
@@ -0,0 +1,33 @@
1
+ module Mongoid
2
+ module Tags
3
+ grammar Tags
4
+ rule query
5
+ tag (space tag)* <Query>
6
+ end
7
+
8
+ rule tag
9
+ operator tag:([a-zA-Z0-9] [a-zA-Z0-9_]*) <Tag>
10
+ end
11
+
12
+ rule operator
13
+ and_operator / not_operator / or_operator
14
+ end
15
+
16
+ rule or_operator
17
+ '' <OrOperator>
18
+ end
19
+
20
+ rule and_operator
21
+ '+' <AndOperator>
22
+ end
23
+
24
+ rule not_operator
25
+ '-' <NotOperator>
26
+ end
27
+
28
+ rule space
29
+ [\s]+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ require './lib/mongoid/tags/version'
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'mongoid-tags'
5
+ gem.version = Mongoid::Tags::VERSION
6
+ gem.authors = 'Mario Uher'
7
+ gem.email = 'uher.mario@gmail.com'
8
+ gem.homepage = 'https://github.com/haihappen/mongoid-tags'
9
+ gem.summary = 'Simple tagging system with boolean search.'
10
+ gem.description = 'Mongoid::Tags adds a simple tagging system to your Mongoid documents, and allows you to query them using a boolean search syntax.'
11
+
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.require_path = 'lib'
14
+
15
+ gem.add_dependency 'activesupport'
16
+ gem.add_dependency 'mongoid'
17
+ gem.add_dependency 'treetop'
18
+ end
@@ -0,0 +1,121 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe 'Mongoid::Tags integration' do
4
+ before do
5
+ # Create all possible combinations
6
+ 3.times do |i|
7
+ %w[foo bar baz].combination(i + 1).each do |tags|
8
+ Document.create(tags: tags)
9
+ end
10
+ end
11
+ end
12
+
13
+
14
+ let(:query) { self.class.name.demodulize } # Use description as query ;)
15
+ subject { Document.tagged(query) }
16
+
17
+
18
+ describe 'foo' do
19
+ it 'returns documents including foo' do
20
+ subject.all? { |d| d.tags.include?('foo') }.must_equal true
21
+ end
22
+ end
23
+
24
+
25
+ describe '+foo' do
26
+ it 'returns documents including at least foo' do
27
+ subject.all? { |d| d.tags.include?('foo') }.must_equal true
28
+ end
29
+ end
30
+
31
+
32
+ describe '-foo' do
33
+ it 'returns documents not including foo' do
34
+ subject.none? { |d| d.tags.include?('foo') }.must_equal true
35
+ end
36
+ end
37
+
38
+
39
+ describe 'foo bar' do
40
+ it 'returns documents including foo or bar' do
41
+ subject.all? { |d| d.tags.include?('foo') || d.tags.include?('bar') }.must_equal true
42
+ end
43
+ end
44
+
45
+
46
+ describe '+foo bar' do
47
+ it 'returns documents including foo and bar' do
48
+ subject.all? { |d| d.tags.include?('foo') }.must_equal true
49
+ subject.all? { |d| d.tags.include?('bar') }.must_equal true
50
+ end
51
+ end
52
+
53
+
54
+ describe '-foo bar' do
55
+ it 'returns documents not including foo, but bar' do
56
+ subject.none? { |d| d.tags.include?('foo') }.must_equal true
57
+ subject.all? { |d| d.tags.include?('bar') }.must_equal true
58
+ end
59
+ end
60
+
61
+
62
+ describe '-foo -bar' do
63
+ it 'returns documents not including foo and bar' do
64
+ subject.none? { |d| d.tags.include?('foo') }.must_equal true
65
+ subject.none? { |d| d.tags.include?('bar') }.must_equal true
66
+ end
67
+ end
68
+
69
+
70
+ describe 'foo bar baz' do
71
+ it 'returns documents including foo or bar' do
72
+ subject.all? { |d| d.tags.include?('foo') || d.tags.include?('bar') || d.tags.include?('baz') }.must_equal true
73
+ end
74
+ end
75
+
76
+
77
+ describe '+foo bar baz' do
78
+ it 'returns documents including at least foo and bar or baz' do
79
+ subject.all? { |d| d.tags.include?('foo') }.must_equal true
80
+ subject.all? { |d| d.tags.include?('bar') || d.tags.include?('baz') }.must_equal true
81
+ end
82
+ end
83
+
84
+
85
+ describe '+foo +bar baz' do
86
+ it 'returns documents including at least foo, bar and baz' do
87
+ subject.all? { |d| d.tags.include?('foo') }.must_equal true
88
+ subject.all? { |d| d.tags.include?('bar') }.must_equal true
89
+ subject.all? { |d| d.tags.include?('baz') }.must_equal true
90
+ end
91
+ end
92
+
93
+
94
+ describe '-foo bar baz' do
95
+ it 'returns documents not including foo, but bar or baz' do
96
+ subject.none? { |d| d.tags.include?('foo') }.must_equal true
97
+ subject.all? { |d| d.tags.include?('bar') || d.tags.include?('baz') }.must_equal true
98
+ end
99
+ end
100
+
101
+
102
+ describe '-foo -bar baz' do
103
+ it 'returns documents not including foo and bar, but baz' do
104
+ subject.none? { |d| d.tags.include?('foo') }.must_equal true
105
+ subject.none? { |d| d.tags.include?('bar') }.must_equal true
106
+ subject.all? { |d| d.tags.include?('baz') }.must_equal true
107
+ end
108
+ end
109
+
110
+
111
+ describe '-foo -bar -baz' do
112
+ it 'returns documents not including foo, bar and baz' do
113
+ subject.none? { |d| d.tags.include?('foo') }.must_equal true
114
+ subject.none? { |d| d.tags.include?('bar') }.must_equal true
115
+ subject.none? { |d| d.tags.include?('baz') }.must_equal true
116
+ end
117
+ end
118
+
119
+
120
+ after { Document.destroy_all }
121
+ end
@@ -0,0 +1,8 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require 'minitest/spec'
4
+
5
+ require './lib/mongoid/tags'
6
+
7
+ # Load support *.rb files in ./support
8
+ Dir[File.expand_path('../support/*.rb', __FILE__)].each { |file| require file }
@@ -0,0 +1 @@
1
+ Mongoid.load!(File.expand_path('../mongoid.yml', __FILE__), 'test')
@@ -0,0 +1,4 @@
1
+ class Document
2
+ include Mongoid::Document
3
+ include Mongoid::Tags
4
+ end
@@ -0,0 +1,8 @@
1
+ test:
2
+ sessions:
3
+ default:
4
+ database: mongoid_tags_test
5
+ hosts:
6
+ - localhost:27017
7
+ options:
8
+ consistency: :strong
data/spec/tags_spec.rb ADDED
@@ -0,0 +1,76 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe Mongoid::Tags do
4
+ describe :selector do
5
+ let(:query) { @__name__.match(/test_\d+_(.*)/).to_a.last } # Use description as query ;)
6
+ subject { Document.selector(query) }
7
+
8
+
9
+ it 'foo' do
10
+ subject.must_equal '$in' => %w[foo]
11
+ end
12
+
13
+
14
+ it '+foo' do
15
+ subject.must_equal '$all' => %w[foo]
16
+ end
17
+
18
+
19
+ it '- foo' do
20
+ proc { subject }.must_raise Mongoid::Tags::Error
21
+ end
22
+
23
+
24
+ it '-foo' do
25
+ subject.must_equal '$nin' => %w[foo]
26
+ end
27
+
28
+
29
+ it '- foo' do
30
+ proc { subject }.must_raise Mongoid::Tags::Error
31
+ end
32
+
33
+
34
+ it '/foo' do
35
+ proc { subject }.must_raise Mongoid::Tags::Error
36
+ end
37
+
38
+
39
+ it 'foo bar' do
40
+ subject.must_equal '$in' => %w[foo bar]
41
+ end
42
+
43
+
44
+ it 'foo +bar' do
45
+ subject.must_equal '$in' => %w[foo], '$all' => %w[bar]
46
+ end
47
+
48
+
49
+ it 'foo + bar' do
50
+ proc { subject }.must_raise Mongoid::Tags::Error
51
+ end
52
+
53
+
54
+ it 'foo +bar +baz' do
55
+ subject.must_equal '$in' => %w[foo], '$all' => %w[bar baz]
56
+ end
57
+
58
+
59
+ it 'foo +bar baz' do
60
+ subject.must_equal '$in' => %w[foo baz], '$all' => %w[bar]
61
+ end
62
+
63
+
64
+ it 'foo +bar -baz' do
65
+ subject.must_equal '$in' => %w[foo], '$all' => %w[bar], '$nin' => %w[baz]
66
+ end
67
+
68
+
69
+ it 'foo foo foo' do
70
+ # Removing duplicates does not gain any
71
+ # performance improvements.
72
+ skip
73
+ subject.must_equal '$in' => %w[foo]
74
+ end
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid-tags
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.1
6
+ platform: ruby
7
+ authors:
8
+ - Mario Uher
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ none: false
21
+ name: activesupport
22
+ type: :runtime
23
+ prerelease: false
24
+ requirement: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ! '>='
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ none: false
30
+ - !ruby/object:Gem::Dependency
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ none: false
37
+ name: mongoid
38
+ type: :runtime
39
+ prerelease: false
40
+ requirement: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ none: false
46
+ - !ruby/object:Gem::Dependency
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ none: false
53
+ name: treetop
54
+ type: :runtime
55
+ prerelease: false
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ none: false
62
+ description: Mongoid::Tags adds a simple tagging system to your Mongoid documents,
63
+ and allows you to query them using a boolean search syntax.
64
+ email: uher.mario@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - CHANGELOG.md
71
+ - Gemfile
72
+ - README.md
73
+ - Rakefile
74
+ - lib/mongoid/tags.rb
75
+ - lib/mongoid/tags.tt
76
+ - lib/mongoid/tags/version.rb
77
+ - mongoid-tags.gemspec
78
+ - spec/integration_spec.rb
79
+ - spec/spec_helper.rb
80
+ - spec/support/connection.rb
81
+ - spec/support/document.rb
82
+ - spec/support/mongoid.yml
83
+ - spec/tags_spec.rb
84
+ homepage: https://github.com/haihappen/mongoid-tags
85
+ licenses: []
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ none: false
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ none: false
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.23
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Simple tagging system with boolean search.
108
+ test_files: []