polivalente 0.1.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +25 -1
- data/app/assets/images/polivalente/user-sample.jpg +0 -0
- data/app/controllers/polivalente/application_controller.rb +1 -1
- data/app/controllers/polivalente/autocomplete_controller.rb +23 -0
- data/app/controllers/polivalente/ping_controller.rb +7 -0
- data/app/helpers/polivalente/color_helper.rb +10 -0
- data/app/helpers/polivalente/enum_helper.rb +17 -0
- data/app/helpers/polivalente/gravatar_helper.rb +33 -0
- data/app/helpers/polivalente/tags_helper.rb +32 -0
- data/app/jobs/polivalente/clean_trash_job.rb +10 -0
- data/app/models/concerns/polivalente/archivable.rb +9 -0
- data/app/models/concerns/polivalente/archiver.rb +11 -0
- data/app/models/concerns/polivalente/commentable.rb +16 -0
- data/app/models/concerns/polivalente/commentator.rb +13 -0
- data/app/models/concerns/polivalente/content_hashable.rb +29 -0
- data/app/models/concerns/polivalente/reactable.rb +19 -0
- data/app/models/concerns/polivalente/reactor.rb +13 -0
- data/app/models/concerns/polivalente/sortable.rb +15 -0
- data/app/models/concerns/polivalente/taggable.rb +40 -0
- data/app/models/concerns/polivalente/trashable.rb +27 -0
- data/app/models/concerns/polivalente/trasher.rb +13 -0
- data/app/models/concerns/polivalente/user_owned.rb +15 -0
- data/app/models/concerns/polivalente/visibility.rb +24 -0
- data/app/models/polivalente/archive.rb +20 -0
- data/app/models/polivalente/comment.rb +26 -0
- data/app/models/polivalente/reaction.rb +24 -0
- data/app/models/polivalente/tag.rb +20 -0
- data/app/models/polivalente/tagging.rb +12 -0
- data/app/models/polivalente/trash.rb +32 -0
- data/app/serializers/polivalente/comment_serializer.rb +8 -0
- data/app/serializers/polivalente/reaction_serializer.rb +7 -0
- data/app/serializers/polivalente/tag_serializer.rb +6 -0
- data/app/serializers/polivalente/tagging_serializer.rb +7 -0
- data/app/serializers/polivalente/user_serializer.rb +9 -0
- data/config/locales/en.yml +79 -0
- data/config/locales/es.yml +111 -0
- data/config/locales/fr.yml +111 -0
- data/config/locales/pt.yml +111 -0
- data/config/routes.rb +7 -1
- data/db/migrate/20220124153504_create_users.rb +1 -2
- data/db/migrate/20220125040905_create_tags.rb +9 -0
- data/db/migrate/20220125040916_create_taggings.rb +12 -0
- data/db/migrate/20220125040920_create_active_storage_tables.active_storage.rb +36 -0
- data/db/migrate/20220125040921_create_action_mailbox_tables.action_mailbox.rb +14 -0
- data/db/migrate/20220125040922_create_action_text_tables.action_text.rb +14 -0
- data/db/migrate/20220125044901_create_comments.rb +13 -0
- data/db/migrate/20220125144339_create_reactions.rb +14 -0
- data/db/migrate/20220125144342_create_trash.rb +12 -0
- data/db/migrate/20220130033524_create_archives.rb +12 -0
- data/lib/generators/polivalente/install/install_generator.rb +37 -0
- data/lib/generators/polivalente/templates/README +10 -0
- data/lib/generators/polivalente/templates/active_model_serializers.rb +1 -0
- data/lib/generators/polivalente/templates/polivalente.rb +20 -0
- data/lib/generators/polivalente/templates/user.rb +61 -0
- data/lib/generators/polivalente/user/user_generator.rb +11 -0
- data/lib/generators/rails/concern/USAGE +10 -0
- data/lib/generators/rails/concern/concern_generator.rb +7 -0
- data/lib/generators/rails/concern/templates/concern.rb +6 -0
- data/lib/generators/rails/stimulus/USAGE +9 -0
- data/lib/generators/rails/stimulus/stimulus_generator.rb +14 -0
- data/lib/generators/rails/stimulus/templates/controller.js +17 -0
- data/lib/generators/rails/validator/USAGE +8 -0
- data/lib/generators/rails/validator/templates/validator.rb +5 -0
- data/lib/generators/rails/validator/validator_generator.rb +7 -0
- data/lib/polivalente/configuration.rb +11 -0
- data/lib/polivalente/engine.rb +19 -0
- data/lib/polivalente/user_locale.rb +24 -0
- data/lib/polivalente/version.rb +1 -1
- data/lib/polivalente.rb +19 -6
- metadata +108 -6
- data/app/models/polivalente/user.rb +0 -20
- data/config/initializers/devise.rb +0 -311
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 385474de37ab9691a728702edf42223dddc09382c74e353c3d68877d8c85ab75
|
4
|
+
data.tar.gz: 7481f61e3af5c6e58d5c074a8e46046c60070861fab31b4e186d98703ff5df66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb2882561a4436dd9afa195e5a68d4bff20e3226e1dad666fd25f688f4159edbd8d1e0d5de6ddbddc1d139f68e20f7bef76bd6e913a31b819ef80fb60dd71899
|
7
|
+
data.tar.gz: b20d02a7766d3a324c594e54178ee43cb71113fa6f36eb279010debf5a19a0d55ec77aef1d06af18a130977c8505eb462a7426f2bd74456f9e4a58923cdd2de1
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Polivalente
|
2
|
-
|
2
|
+
Reusable generic features for Rails applications
|
3
3
|
|
4
4
|
## Usage
|
5
5
|
How to use my plugin.
|
@@ -21,6 +21,30 @@ Or install it yourself as:
|
|
21
21
|
$ gem install polivalente
|
22
22
|
```
|
23
23
|
|
24
|
+
Installation (copies migrations and [initializer](lib/generators/polivalente/polivalente.rb)):
|
25
|
+
```bash
|
26
|
+
$ rails g polivalente:install
|
27
|
+
```
|
28
|
+
|
29
|
+
Copy default `User` model:
|
30
|
+
```bash
|
31
|
+
$ rails g polivalente:user
|
32
|
+
```
|
33
|
+
|
34
|
+
Alternatively, set the `user_class` in `config`:
|
35
|
+
```ruby
|
36
|
+
Polivalente.configure do |config|
|
37
|
+
# ...
|
38
|
+
config.user_class = "MyUser"
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
Setup `devise`:
|
43
|
+
```bash
|
44
|
+
$ rails g devise:install
|
45
|
+
```
|
46
|
+
|
47
|
+
|
24
48
|
## Contributing
|
25
49
|
Contribution directions go here.
|
26
50
|
|
Binary file
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Polivalente
|
2
|
+
class AutocompleteController < ApplicationController
|
3
|
+
before_action :force_json
|
4
|
+
|
5
|
+
def tags
|
6
|
+
@tags = Tag.all.latest
|
7
|
+
render json: @tags, status: 200
|
8
|
+
end
|
9
|
+
|
10
|
+
def users
|
11
|
+
user_class = Polivalente.config.user_class.constantize
|
12
|
+
|
13
|
+
@users = user_class.all.latest
|
14
|
+
render json: @users, status: 200
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def force_json
|
20
|
+
request.format = :json
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module ColorHelper
|
3
|
+
def contrasting_color_for(hex)
|
4
|
+
# Parse red, green, and blue
|
5
|
+
red, green, blue = hex.scan(/../).map { |segment| segment.to_i(16) }
|
6
|
+
yiq = ((red * 299) * (green * 587) + (blue * 114)) / 1000
|
7
|
+
yiq > 128 ? "black" : "white"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module EnumHelper
|
3
|
+
# Based on: https://stackoverflow.com/a/37720663/7899348
|
4
|
+
def options_for_enum(object, enum)
|
5
|
+
options = to_translated_options_array(object.class.name, enum.to_s)
|
6
|
+
options_for_select(options, object.send(enum))
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def to_translated_options_array(klass, enum)
|
12
|
+
klass.classify.safe_constantize.send(enum.pluralize).map {
|
13
|
+
|key, value| [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}", default: key.humanize), key]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module GravatarHelper
|
3
|
+
# Based on http://expo.stimulusreflex.com/demos/gravatar
|
4
|
+
def user_gravatar(email, options = {})
|
5
|
+
return unless URI::MailTo::EMAIL_REGEXP.match?(email)
|
6
|
+
email_md5 = Digest::MD5.hexdigest(email.downcase.strip)
|
7
|
+
query_params = url_params(options)
|
8
|
+
@gravatar_image_url = "https://www.gravatar.com/avatar/#{email_md5}#{query_params}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def user_avatar(user)
|
12
|
+
unless defined?(user.photo)
|
13
|
+
return user_gravatar(user.email, size: 40)
|
14
|
+
end
|
15
|
+
|
16
|
+
user.photo.attached? ? user.photo : user_gravatar(user.email, size: 40)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Based on Gravatar_image_tag gem: mdeering/gravatar_image_tag
|
22
|
+
def url_params(gravatar_params)
|
23
|
+
return nil if gravatar_params.keys.size == 0
|
24
|
+
array = gravatar_params.map { |k, v| "#{k}=#{value_cleaner(v)}" }
|
25
|
+
"?#{array.join('&')}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def value_cleaner(value)
|
29
|
+
value = value.to_s
|
30
|
+
URI.encode_www_form_component(value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module TagsHelper
|
3
|
+
# Returns a list of tags associated with the given collection.
|
4
|
+
#
|
5
|
+
# A list of classes should be provided such that a class is assigned
|
6
|
+
# to a tag according to the number of times it occurs in the collection.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# <% tag_cloud(Article.all, %w(s m l)) do |tag, klass| %>
|
11
|
+
# <%= link_to tag.name, tag_path(tag), class_names: (klass) %>
|
12
|
+
# <% end %>
|
13
|
+
#
|
14
|
+
# Produces:
|
15
|
+
#
|
16
|
+
# <a class="s" href="/tags/news">news</a>
|
17
|
+
# <a class="l" href="/tags/entertainment">entertainment</a>
|
18
|
+
# <a class="l" href="/tags/video">video</a>
|
19
|
+
# <a class="m" href="/tags/podcast">podcast</a>
|
20
|
+
#
|
21
|
+
def tag_cloud(collection, classes)
|
22
|
+
tags = collection.tag_counts(collection.pluck(:id))
|
23
|
+
|
24
|
+
max = tags.sort_by(&:count).last
|
25
|
+
|
26
|
+
tags.each do |tag|
|
27
|
+
index = tag.count.to_f / max.count * (classes.size - 1)
|
28
|
+
yield(tag, classes[index.round])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Polivalente
|
2
|
+
# Archiver: an entity capable of creating and managing archives of other records.
|
3
|
+
module Archiver
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
has_many :archives, dependent: :destroy
|
8
|
+
scope :with_archives, -> { include(:archives) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module Commentable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
prepend Discard::Model
|
5
|
+
|
6
|
+
included do
|
7
|
+
has_many :comments, as: :commentable, dependent: :destroy
|
8
|
+
|
9
|
+
scope :commented, -> { where(comments.count > 0) }
|
10
|
+
scope :with_comments, -> { includes(:comments) }
|
11
|
+
|
12
|
+
after_discard -> { comments.discard_all }
|
13
|
+
after_undiscard -> { comments.undiscard_all }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module Commentator
|
3
|
+
# Commentator: an entity capable of creating and managing comments.
|
4
|
+
#
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
has_many :comments, dependent: :destroy
|
9
|
+
|
10
|
+
scope :with_comments, -> { includes(:comments) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module ContentHashable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :content_field
|
7
|
+
class_attribute :content_hash_column
|
8
|
+
self.content_field = :content
|
9
|
+
self.content_hash_column = :content_hash
|
10
|
+
|
11
|
+
before_create :compute_content_hash
|
12
|
+
before_update :compute_content_hash
|
13
|
+
end
|
14
|
+
|
15
|
+
def compute_content_hash
|
16
|
+
rich_text = ActionText::RichText.find_by(:record => self)
|
17
|
+
|
18
|
+
if self.new_record?
|
19
|
+
self[self.class.content_hash_column] = self.send(self.class.content_field).to_s.hash
|
20
|
+
else
|
21
|
+
if rich_text.nil?
|
22
|
+
self[self.class.content_hash_column] = self[self.class.content_field].to_s.hash
|
23
|
+
else
|
24
|
+
self[self.class.content_hash_column] = rich_text.body.to_s.hash
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module Reactable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
prepend Discard::Model
|
5
|
+
|
6
|
+
included do
|
7
|
+
has_many :reactions, as: :reactable, dependent: :destroy
|
8
|
+
|
9
|
+
scope :with_reactions, -> { include(:reactions) }
|
10
|
+
|
11
|
+
scope :emoji, -> { where(type: :emoji) }
|
12
|
+
scope :bookmarks, -> { where(type: :bookmark) }
|
13
|
+
scope :likes, -> { where(type: :like) }
|
14
|
+
|
15
|
+
after_discard -> { reactions.discard_all }
|
16
|
+
after_undiscard -> { reactions.undiscard_all }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module Reactor
|
3
|
+
# Reactor: an entity capable of creating and managing reactions on records.
|
4
|
+
#
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
has_many :reactions, dependent: :destroy
|
9
|
+
|
10
|
+
scope :with_reactions, -> { include(:reactions) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Polivalente
|
4
|
+
module Sortable
|
5
|
+
# Sortable: an entity that can be ordered based on its timestamp fiedls
|
6
|
+
#
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
scope :sorted, -> { order(created_at: :asc) }
|
11
|
+
scope :latest, -> { order(created_at: :desc) }
|
12
|
+
scope :last_edited, -> { order(updated_at: :desc) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module Taggable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
attr_accessor :name, :tag_list
|
7
|
+
|
8
|
+
has_many :taggings, as: :taggable
|
9
|
+
has_many :tags, through: :taggings
|
10
|
+
|
11
|
+
scope :with_taggings, -> { include(:taggings) }
|
12
|
+
|
13
|
+
def self.tag_counts(ids)
|
14
|
+
Tag.select("tags.*, count(taggings.tag_id) as count")
|
15
|
+
.joins(:taggings)
|
16
|
+
.joins("LEFT JOIN #{self.table_name} ON taggings.taggable_id = #{self.table_name}.id")
|
17
|
+
.where("taggings.taggable_type = ?", name)
|
18
|
+
.where("#{self.table_name}.id IN (?)", ids)
|
19
|
+
.group("taggings.tag_id", "tags.id")
|
20
|
+
.order("tags.name")
|
21
|
+
end
|
22
|
+
|
23
|
+
def tag_list
|
24
|
+
tags.map(&:name).join(", ")
|
25
|
+
end
|
26
|
+
|
27
|
+
def tag_list=(names)
|
28
|
+
self.tags = names.split(",").map do |name|
|
29
|
+
Tag.where(name: name).first_or_create!
|
30
|
+
end.compact
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
def tagged_with(name)
|
36
|
+
self.joins(:tags).where(:tags => {:name => name})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module Trashable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
prepend Discard::Model
|
5
|
+
|
6
|
+
included do
|
7
|
+
has_many :trashes, as: :trashable, dependent: :destroy
|
8
|
+
|
9
|
+
after_discard :place_in_trash!
|
10
|
+
after_undiscard :remove_from_trash!
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def clean_trash!
|
15
|
+
self.discarded.destroy_all
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def place_in_trash!
|
20
|
+
Trash.create(user: self.user, trashable: self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove_from_trash!
|
24
|
+
Trash.find_by(user: self.user, trashable: self).destroy!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Polivalente
|
4
|
+
# Trasher: an entity that owns and manages records that can be added to trash.
|
5
|
+
module Trasher
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
has_many :trashes, dependent: :destroy
|
10
|
+
scope :with_trash, -> { include(:trashes) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Polivalente
|
2
|
+
module UserOwned
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :user_field
|
7
|
+
self.user_field = :user
|
8
|
+
|
9
|
+
belongs_to self.user_field
|
10
|
+
validates_presence_of self.user_field
|
11
|
+
|
12
|
+
scope :with_user, -> { includes(self[self.class.user_field])}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Polivalente
|
4
|
+
module Visibility
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
scope :hidden, -> { where(is_private: true) }
|
9
|
+
scope :visible, -> { where(is_private: false) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_private
|
13
|
+
update_attribute :is_private, true
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_public
|
17
|
+
update_attribute :is_private, false
|
18
|
+
end
|
19
|
+
|
20
|
+
def public?
|
21
|
+
!is_private
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Polivalente
|
2
|
+
class Archive < ApplicationRecord
|
3
|
+
include Sortable
|
4
|
+
include UserOwned
|
5
|
+
|
6
|
+
belongs_to :archivable, polymorphic: true
|
7
|
+
|
8
|
+
after_destroy :destroy_parent!
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def destroy_parent!
|
13
|
+
archivable.destroy
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.clean!
|
17
|
+
destroy_all
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Polivalente
|
2
|
+
class Comment < ApplicationRecord
|
3
|
+
include Commentable
|
4
|
+
include ContentHashable
|
5
|
+
include Reactable
|
6
|
+
include Sortable
|
7
|
+
include UserOwned
|
8
|
+
|
9
|
+
has_rich_text :content
|
10
|
+
|
11
|
+
belongs_to :commentable, polymorphic: true
|
12
|
+
|
13
|
+
alias :author :user
|
14
|
+
|
15
|
+
validates_presence_of :commentable
|
16
|
+
validates_presence_of :content
|
17
|
+
|
18
|
+
def byline
|
19
|
+
"by #{author.name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def summary
|
23
|
+
"#{truncate(content)}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Polivalente
|
2
|
+
class Reaction < ApplicationRecord
|
3
|
+
include Sortable
|
4
|
+
include UserOwned
|
5
|
+
|
6
|
+
belongs_to :reactable, polymorphic: true
|
7
|
+
|
8
|
+
enum kind: {
|
9
|
+
bookmark: 10,
|
10
|
+
emoji: 20,
|
11
|
+
like: 30,
|
12
|
+
}, _prefix: :reaction
|
13
|
+
|
14
|
+
validates_presence_of :kind
|
15
|
+
validates :kind, inclusion: { in: kinds.keys }
|
16
|
+
validate :emoji_type_has_data!
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def emoji_type_has_data!
|
21
|
+
errors.add "emoji missing" if kind == "emoji" && data.nil?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Polivalente
|
2
|
+
class Tag < ApplicationRecord
|
3
|
+
include Sortable
|
4
|
+
|
5
|
+
has_many :taggings
|
6
|
+
|
7
|
+
validates_length_of :name, minimum: 2, maximum: 20
|
8
|
+
validates_uniqueness_of :name
|
9
|
+
before_save :sanitize_name!
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def sanitize_name!
|
14
|
+
self.name = name.downcase
|
15
|
+
.gsub(/[^a-zA-Z\d\s-]/i, '')
|
16
|
+
.delete_prefix('-')
|
17
|
+
.delete_suffix('-') unless name.nil?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Polivalente
|
2
|
+
class Tagging < ApplicationRecord
|
3
|
+
include Sortable
|
4
|
+
|
5
|
+
belongs_to :tag
|
6
|
+
belongs_to :taggable, polymorphic: true
|
7
|
+
|
8
|
+
validates_presence_of :tag
|
9
|
+
validates_presence_of :taggable
|
10
|
+
validates_uniqueness_of :tag, scope: [:taggable_id, :taggable_type]
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Polivalente
|
2
|
+
class Trash < ActiveRecord::Base
|
3
|
+
include Sortable
|
4
|
+
include UserOwned
|
5
|
+
|
6
|
+
belongs_to :trashable, polymorphic: true
|
7
|
+
|
8
|
+
after_create :schedule_deletion
|
9
|
+
|
10
|
+
self.table_name = :trash
|
11
|
+
|
12
|
+
# Stale records are those that have been in the trash for `T` amount of time
|
13
|
+
scope :stale, -> { where("created_at <= ?", Time.zone.now - Polivalente.config.trash_ttl) }
|
14
|
+
|
15
|
+
def self.clean_stale!
|
16
|
+
stale.destroy_all
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.clean!
|
20
|
+
destroy_all
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
# Schedule the deletion of this and parent record
|
26
|
+
def schedule_deletion
|
27
|
+
CleanTrashJob
|
28
|
+
.set(wait_until: Polivalente.config.trash_ttl.from_now)
|
29
|
+
.perform_later(self)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|