metka 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 960a0b262fa5e1dc7a4a08baf844f4f12063696133527f0ef201a4414ee498c0
4
+ data.tar.gz: 2792d8b84d0ebf6c9aa197c3bbbe8d6b257f522493a2b770887105cd0d6b539d
5
+ SHA512:
6
+ metadata.gz: 4b47eb9c9b7e64609b0933b02e5704aa8e5dd5cca5ca1a9c796625942fed6b67551574cb6fe3c45190954ce8bfb56ae40bb125659751d55b5ef8f780a35beb0d
7
+ data.tar.gz: fe1b4c69679173d7f8c639f3c060722c617f39f537dba1253ff1f9cd70e26a44028e8ae6676d440c2b9165691162f907a0cf4e546510c8e37194db13c59adb44
@@ -0,0 +1,11 @@
1
+ name: CI
2
+ # This workflow is triggered on pushes to the repository.
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ name: RSpec
8
+ # This job runs on Linux
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v1
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /spec/tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
13
+ spec/debug.log
14
+
15
+ spec/dummy/log/test.log
16
+ spec/dummy/log/development.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,28 @@
1
+ inherit_gem:
2
+ jetrockets-standard: config/gems.yml
3
+
4
+ AllCops:
5
+ Exclude:
6
+ - 'bin/*'
7
+ - 'tmp/**/*'
8
+ - 'docs/**/*'
9
+ - 'Gemfile'
10
+ - 'vendor/**/*'
11
+ - 'gemfiles/**/*'
12
+ - '**/*.md'
13
+ DisplayCopNames: true
14
+ TargetRubyVersion: 2.5
15
+
16
+ Style/TrailingCommaInArrayLiteral:
17
+ EnforcedStyleForMultiline: no_comma
18
+
19
+ Style/TrailingCommaInHashLiteral:
20
+ EnforcedStyleForMultiline: no_comma
21
+
22
+ Layout/AlignParameters:
23
+ EnforcedStyle: with_first_parameter
24
+
25
+ # See https://github.com/rubocop-hq/rubocop/issues/4222
26
+ Lint/AmbiguousBlockAssociation:
27
+ Exclude:
28
+ - 'spec/**/*'
@@ -0,0 +1,36 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+
5
+ dist: trusty
6
+ sudo: false
7
+
8
+ notifications:
9
+ email: false
10
+
11
+ services:
12
+ - postgresql
13
+ addons:
14
+ postgresql: "11.2"
15
+
16
+ before_install:
17
+ - gem install -v 2.0.2 bundler
18
+ - sudo apt-get update
19
+ - sudo apt-get --yes remove postgresql\*
20
+ - sudo apt-get install -y postgresql-11 postgresql-client-11
21
+ - sudo cp /etc/postgresql/{9.6,11}/main/pg_hba.conf
22
+ - sudo service postgresql restart 11
23
+
24
+ before_script:
25
+ - gem update --system
26
+ - psql -c 'CREATE ROLE travis SUPERUSER LOGIN CREATEDB;' -U postgres
27
+ - ./bin/setup
28
+
29
+ matrix:
30
+ include:
31
+ - rvm: 2.5.1
32
+ gemfile: gemfiles/rails5.gemfile
33
+ - rvm: 2.5.5
34
+ gemfile: gemfiles/rails52.gemfile
35
+ - rvm: 2.6.2
36
+ gemfile: gemfiles/rails6.gemfile
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at igor.alexandrov@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in metka.gemspec
4
+ gemspec
5
+
6
+ local_gemfile = "#{File.dirname(__FILE__)}/Gemfile.local"
7
+
8
+ if File.exist?(local_gemfile)
9
+ eval(File.read(local_gemfile)) # rubocop:disable Lint/Eval
10
+ else
11
+ gem 'activerecord', '~> 5.1.1'
12
+ end
@@ -0,0 +1,199 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ metka (1.0.0)
5
+ dry-configurable
6
+ rails (~> 5.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actioncable (5.1.7)
12
+ actionpack (= 5.1.7)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (~> 0.6.1)
15
+ actionmailer (5.1.7)
16
+ actionpack (= 5.1.7)
17
+ actionview (= 5.1.7)
18
+ activejob (= 5.1.7)
19
+ mail (~> 2.5, >= 2.5.4)
20
+ rails-dom-testing (~> 2.0)
21
+ actionpack (5.1.7)
22
+ actionview (= 5.1.7)
23
+ activesupport (= 5.1.7)
24
+ rack (~> 2.0)
25
+ rack-test (>= 0.6.3)
26
+ rails-dom-testing (~> 2.0)
27
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
28
+ actionview (5.1.7)
29
+ activesupport (= 5.1.7)
30
+ builder (~> 3.1)
31
+ erubi (~> 1.4)
32
+ rails-dom-testing (~> 2.0)
33
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
34
+ activejob (5.1.7)
35
+ activesupport (= 5.1.7)
36
+ globalid (>= 0.3.6)
37
+ activemodel (5.1.7)
38
+ activesupport (= 5.1.7)
39
+ activerecord (5.1.7)
40
+ activemodel (= 5.1.7)
41
+ activesupport (= 5.1.7)
42
+ arel (~> 8.0)
43
+ activesupport (5.1.7)
44
+ concurrent-ruby (~> 1.0, >= 1.0.2)
45
+ i18n (>= 0.7, < 2)
46
+ minitest (~> 5.1)
47
+ tzinfo (~> 1.1)
48
+ ammeter (1.1.4)
49
+ activesupport (>= 3.0)
50
+ railties (>= 3.0)
51
+ rspec-rails (>= 2.2)
52
+ arel (8.0.0)
53
+ ast (2.4.0)
54
+ builder (3.2.3)
55
+ coderay (1.1.2)
56
+ concurrent-ruby (1.1.5)
57
+ crass (1.0.5)
58
+ database_cleaner (1.7.0)
59
+ diff-lcs (1.3)
60
+ dry-configurable (0.9.0)
61
+ concurrent-ruby (~> 1.0)
62
+ dry-core (~> 0.4, >= 0.4.7)
63
+ dry-core (0.4.9)
64
+ concurrent-ruby (~> 1.0)
65
+ erubi (1.9.0)
66
+ faker (2.8.0)
67
+ i18n (>= 1.6, < 1.8)
68
+ globalid (0.4.2)
69
+ activesupport (>= 4.2.0)
70
+ i18n (1.7.0)
71
+ concurrent-ruby (~> 1.0)
72
+ jaro_winkler (1.5.4)
73
+ jetrockets-standard (1.0.4)
74
+ rubocop-rails (~> 2.3.2)
75
+ rubocop-rspec (~> 1.35.0)
76
+ standard (~> 0.1.4)
77
+ loofah (2.4.0)
78
+ crass (~> 1.0.2)
79
+ nokogiri (>= 1.5.9)
80
+ mail (2.7.1)
81
+ mini_mime (>= 0.1.1)
82
+ method_source (0.9.2)
83
+ mini_mime (1.0.2)
84
+ mini_portile2 (2.4.0)
85
+ minitest (5.13.0)
86
+ nio4r (2.5.2)
87
+ nokogiri (1.10.7)
88
+ mini_portile2 (~> 2.4.0)
89
+ parallel (1.19.1)
90
+ parser (2.6.5.0)
91
+ ast (~> 2.4.0)
92
+ pg (1.1.4)
93
+ pry (0.12.2)
94
+ coderay (~> 1.1.0)
95
+ method_source (~> 0.9.0)
96
+ rack (2.0.7)
97
+ rack-test (1.1.0)
98
+ rack (>= 1.0, < 3)
99
+ rails (5.1.7)
100
+ actioncable (= 5.1.7)
101
+ actionmailer (= 5.1.7)
102
+ actionpack (= 5.1.7)
103
+ actionview (= 5.1.7)
104
+ activejob (= 5.1.7)
105
+ activemodel (= 5.1.7)
106
+ activerecord (= 5.1.7)
107
+ activesupport (= 5.1.7)
108
+ bundler (>= 1.3.0)
109
+ railties (= 5.1.7)
110
+ sprockets-rails (>= 2.0.0)
111
+ rails-dom-testing (2.0.3)
112
+ activesupport (>= 4.2.0)
113
+ nokogiri (>= 1.6)
114
+ rails-html-sanitizer (1.3.0)
115
+ loofah (~> 2.3)
116
+ railties (5.1.7)
117
+ actionpack (= 5.1.7)
118
+ activesupport (= 5.1.7)
119
+ method_source
120
+ rake (>= 0.8.7)
121
+ thor (>= 0.18.1, < 2.0)
122
+ rainbow (3.0.0)
123
+ rake (13.0.1)
124
+ rspec (3.9.0)
125
+ rspec-core (~> 3.9.0)
126
+ rspec-expectations (~> 3.9.0)
127
+ rspec-mocks (~> 3.9.0)
128
+ rspec-core (3.9.0)
129
+ rspec-support (~> 3.9.0)
130
+ rspec-expectations (3.9.0)
131
+ diff-lcs (>= 1.2.0, < 2.0)
132
+ rspec-support (~> 3.9.0)
133
+ rspec-mocks (3.9.0)
134
+ diff-lcs (>= 1.2.0, < 2.0)
135
+ rspec-support (~> 3.9.0)
136
+ rspec-rails (3.9.0)
137
+ actionpack (>= 3.0)
138
+ activesupport (>= 3.0)
139
+ railties (>= 3.0)
140
+ rspec-core (~> 3.9.0)
141
+ rspec-expectations (~> 3.9.0)
142
+ rspec-mocks (~> 3.9.0)
143
+ rspec-support (~> 3.9.0)
144
+ rspec-support (3.9.0)
145
+ rubocop (0.75.1)
146
+ jaro_winkler (~> 1.5.1)
147
+ parallel (~> 1.10)
148
+ parser (>= 2.6)
149
+ rainbow (>= 2.2.2, < 4.0)
150
+ ruby-progressbar (~> 1.7)
151
+ unicode-display_width (>= 1.4.0, < 1.7)
152
+ rubocop-performance (1.5.1)
153
+ rubocop (>= 0.71.0)
154
+ rubocop-rails (2.3.2)
155
+ rack (>= 1.1)
156
+ rubocop (>= 0.72.0)
157
+ rubocop-rspec (1.35.0)
158
+ rubocop (>= 0.60.0)
159
+ ruby-progressbar (1.10.1)
160
+ sprockets (4.0.0)
161
+ concurrent-ruby (~> 1.0)
162
+ rack (> 1, < 3)
163
+ sprockets-rails (3.2.1)
164
+ actionpack (>= 4.0)
165
+ activesupport (>= 4.0)
166
+ sprockets (>= 3.0.0)
167
+ standard (0.1.6)
168
+ rubocop (~> 0.75.0)
169
+ rubocop-performance (~> 1.5.0)
170
+ thor (0.20.3)
171
+ thread_safe (0.3.6)
172
+ timecop (0.9.1)
173
+ tzinfo (1.2.5)
174
+ thread_safe (~> 0.1)
175
+ unicode-display_width (1.6.0)
176
+ websocket-driver (0.6.5)
177
+ websocket-extensions (>= 0.1.0)
178
+ websocket-extensions (0.1.4)
179
+
180
+ PLATFORMS
181
+ ruby
182
+
183
+ DEPENDENCIES
184
+ activerecord (~> 5.1.1)
185
+ ammeter
186
+ bundler
187
+ database_cleaner
188
+ faker
189
+ jetrockets-standard (~> 1.0.1)
190
+ metka!
191
+ pg
192
+ pry (~> 0.12.2)
193
+ rake
194
+ rspec (~> 3.9)
195
+ rspec-rails (~> 3.9)
196
+ timecop
197
+
198
+ BUNDLED WITH
199
+ 2.0.2
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Igor Alexandrov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,259 @@
1
+ [![Build Status](https://travis-ci.org/jetrockets/metka.svg?branch=master)](https://travis-ci.org/jetrockets/metka)
2
+ [![Open Source Helpers](https://www.codetriage.com/jetrockets/metka/badges/users.svg)](https://www.codetriage.com/jetrockets/metka)
3
+
4
+ # Metka
5
+
6
+ Rails gem to manage tags with SonggreSQL array columns.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'metka'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install metka
23
+
24
+ ## Tag objects
25
+
26
+ ```ruby
27
+ class Song < ActiveRecord::Base
28
+ include Metka::Model(column: 'tags')
29
+ include Metka::Model(column: 'genres')
30
+ end
31
+
32
+ @Song = Song.new(title: 'Migrate tags in Rails to SonggreSQL')
33
+ @Song.tag_list = 'top, chill'
34
+ @Song.genre_list = 'rock, jazz, pop'
35
+ @Song.save
36
+ ```
37
+
38
+ ## Find tagged objects
39
+
40
+ ### .with_all_#{column_name}
41
+ ```ruby
42
+ Song.with_all_tags('top')
43
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
44
+
45
+ Song.with_all_tags('top, 1990')
46
+ => []
47
+
48
+ Song.with_all_tags('')
49
+ => []
50
+
51
+ Song.with_all_genres('rock')
52
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
53
+ ```
54
+
55
+ ### .with_any_#{column_name}
56
+ ```ruby
57
+ Song.with_any_tags('chill')
58
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
59
+
60
+ Song.with_any_tags('chill, 1980')
61
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
62
+
63
+ Song.with_any_tags('')
64
+ => []
65
+
66
+ Song.with_any_genres('rock, rap')
67
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
68
+ ```
69
+ ### .without_all_#{column_name}
70
+ ```ruby
71
+ Song.without_all_tags('top')
72
+ => []
73
+
74
+ Song.without_all_tags('top, 1990')
75
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
76
+
77
+ Song.without_all_tags('')
78
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
79
+
80
+ Song.without_all_genres('rock, pop')
81
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
82
+
83
+ Song.without_all_genres('rock')
84
+ => []
85
+ ```
86
+
87
+ ### .without_any_#{column_name}
88
+ ```ruby
89
+ Song.without_any_tags('top, 1990')
90
+ => []
91
+
92
+ Song.without_any_tags('1990, 1980')
93
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
94
+
95
+ Song.without_any_genres('rock, pop')
96
+ => []
97
+
98
+ Song.without_any_genres('')
99
+ => [#<Song id: 1, title: 'Migrate tags in Rails to SonggreSQL', tags: ['top', 'chill'], genres: ['rock', 'jazz', 'pop']]
100
+ ```
101
+
102
+ ## Custom delimiter
103
+ By default, a comma is used as a delimiter to create tags from a string.
104
+ You can make your own custom separator:
105
+ ```ruby
106
+ Metka.config.delimiter = [',', ' ', '\|']
107
+ parsed_data = Metka::GenericParser.instance.call('cool, data|I have')
108
+ parsed_data.to_a
109
+ =>['cool', 'data', 'I', 'have']
110
+ ```
111
+
112
+ ## Tags with quote
113
+ ```ruby
114
+ parsed_data = Metka::GenericParser.instance.call("'cool, data', code")
115
+ parsed_data.to_a
116
+ => ['cool, data', 'code']
117
+ ```
118
+
119
+ ## Custom parser
120
+ By default we use [generic_parser](lib/metka/generic_parser.rb "generic_parser")
121
+ If you want use your custom parser you can do:
122
+ ```ruby
123
+ class Song < ActiveRecord::Base
124
+ include Metka::Model(column: 'tags', parser: Your::Custom::Parser.instance)
125
+ include Metka::Model(column: 'genres')
126
+ end
127
+ ```
128
+ Custom parser must be a singleton class that has a `.call` method that accepts the tag string
129
+
130
+ ## Tag Cloud Strategies
131
+
132
+ There are several strategies to get tag statistics
133
+
134
+ ### View Strategy
135
+
136
+ Data about taggings will be agregated in SQL View. The easiest way to implement but the most slow on SELECT.
137
+
138
+ ```bash
139
+ rails g metka:strategies:view --source-table-name=NAME_OF_TABLE_WITH_TAGS
140
+ ```
141
+
142
+ The code above will generate a migration that creates view to store aggregated data about tag in `NAME_OF_TABLE_WITH_TAGS` table.
143
+
144
+ Lets take a look at real example. We have a `notes` table with `tags` column.
145
+
146
+ | Column | Type | Default |
147
+ |--------|---------------------|-----------------------------------|
148
+ | id | integer | nextval('notes_id_seq'::regclass) |
149
+ | body | text | |
150
+ | tags | character varying[] | '{}'::character varying[] |
151
+
152
+ Now lets generate a migration.
153
+
154
+ ```bash
155
+ rails g metka:strategies:view --source-table-name=notes
156
+ ```
157
+
158
+ The result would be:
159
+
160
+ ```ruby
161
+ # frozen_string_literal: true
162
+
163
+ class CreateTaggedNotesView < ActiveRecord::Migration[5.0]
164
+ def up
165
+ execute <<-SQL
166
+ CREATE OR REPLACE VIEW tagged_notes AS
167
+
168
+ SELECT UNNEST
169
+ ( tags ) AS tag_name,
170
+ COUNT ( * ) AS taggings_count
171
+ FROM
172
+ notes
173
+ GROUP BY
174
+ name;
175
+ SQL
176
+ end
177
+
178
+ def down
179
+ execute <<-SQL
180
+ DROP VIEW tagged_notes;
181
+ SQL
182
+ end
183
+ end
184
+ ```
185
+
186
+ Now lets take a look at `tagged_notes` view.
187
+
188
+ | tag_name | taggings_count |
189
+ |----------|----------------|
190
+ | Ruby | 124056 |
191
+ | React | 30632 |
192
+ | Rails | 28696 |
193
+ | Crystal | 6566 |
194
+ | Elixir | 3475 |
195
+
196
+ Now you can create `TaggedNote` model and work with the view like you usually do with Rails models.
197
+
198
+ ### Materialized View Strategy
199
+
200
+ Similar to the strategy above, but the view will be Materialized and refreshed with the trigger
201
+
202
+ ```bash
203
+ rails g metka:strategies:materialized_view --source-table-name=NAME_OF_TABLE_WITH_TAGS
204
+ ```
205
+
206
+ The code above will generate a migration that creates view to store aggregated data about tag in `NAME_OF_TABLE_WITH_TAGS` table.
207
+
208
+ Lets take a look at real example. We have a `notes` table with `tags` column.
209
+
210
+ | Column | Type | Default |
211
+ |--------|---------------------|-----------------------------------|
212
+ | id | integer | nextval('notes_id_seq'::regclass) |
213
+ | body | text | |
214
+ | tags | character varying[] | '{}'::character varying[] |
215
+
216
+ Now lets generate a migration.
217
+
218
+ ```bash
219
+ rails g metka:strategies:materialized_view --source-table-name=notes
220
+ ```
221
+
222
+ The migration code you can see [here](spec/dummy/db/migrate/05_create_tagged_materialized_view_Songs_materialized_view.rb "here")
223
+
224
+ Now lets take a look at `tagged_notes` materialized view.
225
+
226
+ Now you can create `TaggedNote` model and work with the view like you usually do with Rails models.
227
+
228
+ ### Table Strategy with Triggers
229
+
230
+
231
+
232
+ TBD
233
+
234
+ ## Inspired by
235
+ 1. [ActsAsTaggableOn](https://github.com/mbleigh/acts-as-taggable-on)
236
+ 2. [ActsAsTaggableArrayOn](https://github.com/tmiyamon/acts-as-taggable-array-on)
237
+ 3. [TagColumns](https://github.com/hopsoft/tag_columns)
238
+
239
+ ## Development
240
+
241
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
242
+
243
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
244
+
245
+ ## Contributing
246
+
247
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/metka. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
248
+
249
+ ## Credits
250
+ ![JetRockets](https://jetrockets.pro/jetrockets-icons-black.png)
251
+ Metka is maintained by [JetRockets](http://www.jetrockets.ru).
252
+
253
+ ## License
254
+
255
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
256
+
257
+ ## Code of Conduct
258
+
259
+ Everyone interacting in the Metka project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/metka/blob/master/CODE_OF_CONDUCT.md).