virtuatable-core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/lib/core/models/account.rb +102 -0
  3. data/lib/core/models/authentication/session.rb +30 -0
  4. data/lib/core/models/authentication.rb +9 -0
  5. data/lib/core/models/campaign.rb +110 -0
  6. data/lib/core/models/campaigns/invitation.rb +31 -0
  7. data/lib/core/models/campaigns/tag.rb +23 -0
  8. data/lib/core/models/campaigns.rb +10 -0
  9. data/lib/core/models/chatrooms/base.rb +16 -0
  10. data/lib/core/models/chatrooms/campaign.rb +13 -0
  11. data/lib/core/models/chatrooms/conversation.rb +9 -0
  12. data/lib/core/models/chatrooms/membership.rb +17 -0
  13. data/lib/core/models/chatrooms/message.rb +33 -0
  14. data/lib/core/models/chatrooms.rb +13 -0
  15. data/lib/core/models/concerns/activable.rb +20 -0
  16. data/lib/core/models/concerns/diagnosticable.rb +24 -0
  17. data/lib/core/models/concerns/enumerable.rb +50 -0
  18. data/lib/core/models/concerns/historizable.rb +64 -0
  19. data/lib/core/models/concerns/mime_typable.rb +44 -0
  20. data/lib/core/models/concerns/premiumable.rb +17 -0
  21. data/lib/core/models/concerns/sluggable.rb +29 -0
  22. data/lib/core/models/concerns/typable.rb +19 -0
  23. data/lib/core/models/concerns.rb +16 -0
  24. data/lib/core/models/decorators/errors/env_variable_missing.rb +16 -0
  25. data/lib/core/models/decorators/errors.rb +11 -0
  26. data/lib/core/models/decorators/gateway.rb +111 -0
  27. data/lib/core/models/event.rb +31 -0
  28. data/lib/core/models/factories/errors/gateway_not_found.rb +16 -0
  29. data/lib/core/models/factories/errors.rb +11 -0
  30. data/lib/core/models/factories.rb +10 -0
  31. data/lib/core/models/files/document.rb +52 -0
  32. data/lib/core/models/files/permission.rb +24 -0
  33. data/lib/core/models/files.rb +8 -0
  34. data/lib/core/models/monitoring/route.rb +44 -0
  35. data/lib/core/models/monitoring/service.rb +33 -0
  36. data/lib/core/models/monitoring.rb +10 -0
  37. data/lib/core/models/notification.rb +24 -0
  38. data/lib/core/models/oauth/access_token.rb +34 -0
  39. data/lib/core/models/oauth/application.rb +58 -0
  40. data/lib/core/models/oauth/authorization.rb +33 -0
  41. data/lib/core/models/oauth/refresh_token.rb +20 -0
  42. data/lib/core/models/oauth.rb +12 -0
  43. data/lib/core/models/permissions/category.rb +17 -0
  44. data/lib/core/models/permissions/group.rb +32 -0
  45. data/lib/core/models/permissions/right.rb +21 -0
  46. data/lib/core/models/permissions.rb +12 -0
  47. data/lib/core/models/ruleset.rb +32 -0
  48. data/lib/core/models.rb +27 -0
  49. data/lib/core/version.rb +3 -0
  50. data/lib/core.rb +10 -0
  51. metadata +329 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: eaef58986378462da6ff78e3aeac595cbed418f3351254e3e99b303059fe21d1
4
+ data.tar.gz: 1ed97f4ff856ecfbe5ccf969547ee0141f03f1e9c59304661880f6d4e87bfaf7
5
+ SHA512:
6
+ metadata.gz: ffc760f437db7e376de09c5e88fa794e440729600819f8cce7474d4db6a122185fc20472020e131ae889d6371f1cf95f0fb7389506ac4ec1f5a9840a3ed2dc98
7
+ data.tar.gz: '08addb4e9612082536359a2a624f5eded215ba382a8f9b16aebf016cfc35b9c9dbc12a503a3160780dc81896bdabf0434f1966062d5eb339e999eadef92cbec1'
@@ -0,0 +1,102 @@
1
+ module Core
2
+ module Models
3
+ # A user account with all related attributes. It holds credentials and informations about a designated user.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ class Account
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+ include ActiveModel::SecurePassword
9
+ include Core::Models::Concerns::Enumerable
10
+
11
+ # @!attribute [rw] username
12
+ # @return [String] the nickname the user chose at subscription, must be given, unique, and 6 or more characters long.
13
+ field :username, type: String
14
+ # @!attribute [r] password_digest
15
+ # @return [String] the password of the user, encrypted with the Blowfish algorithm.
16
+ field :password_digest, type: String
17
+ # @!attribute [rw] lastname
18
+ # @return [String] the last name (family name) of the user.
19
+ field :lastname, type: String, default: ''
20
+ # @!attribute [rw] firstname
21
+ # @return [String] the first name of the user.
22
+ field :firstname, type: String, default: ''
23
+ # @!attribute [rw] email
24
+ # @return [String] the email address of the user, useful to contact them ; it must be given, unique, and have an email format.
25
+ field :email, type: String
26
+ # @!attribute [rw] language
27
+ # @return [Symbol] the language preferred by this user.
28
+ enum_field :language, [:en_GB, :fr_FR], default: :fr_FR
29
+ # @!attribute [rw] gender
30
+ # @return [Symbol] the way you prefer the application to gender you.
31
+ enum_field :gender, [:female, :male, :neutral], default: :neutral
32
+
33
+ # @!attribute [w] password
34
+ # @return [String] password, in clear, of the user ; do not attempt to get the value, just set it when changing the password.
35
+ # @!attribute [w] password_confirmation
36
+ # @return [String] the confirmation of the password, do not get, just set it ; it must be the same as the password.
37
+ has_secure_password validations: false
38
+
39
+ # @!attribute [rw] groups
40
+ # @return [Array<Core::Models::Permissions::Group>] the groups giving their corresponding rights to the current account.
41
+ has_and_belongs_to_many :groups, class_name: 'Core::Models::Permissions::Group', inverse_of: :accounts
42
+
43
+ # @!attribute [rw] applications
44
+ # @return [Array<Core::Models::OAuth::Application] the applications this user has created and owns.
45
+ has_many :applications, class_name: 'Core::Models::OAuth::Application', inverse_of: :creator
46
+ # @!attribute [rw] authorizations
47
+ # @return [Array<Core::Models::OAuth::Authorization>] the authorization issued by this account to third-party applications to access its data.
48
+ has_many :authorizations, class_name: 'Core::Models::OAuth::Authorization', inverse_of: :account
49
+ # @!attribute [rw] services
50
+ # @return [Array<Core::Models::Monitoring::Service>] the services created by this user.
51
+ has_many :services, class_name: 'Core::Models::Monitoring::Service', inverse_of: :creator
52
+ # @!attribute [rw] sessions
53
+ # @return [Array<Core::Models::Authentication::Session>] the sessions on which this account is, or has been logged in.
54
+ has_many :sessions, class_name: 'Core::Models::Authentication::Session', inverse_of: :account
55
+ # @!attribute [rw] invitations
56
+ # @return [Array<Core::Models::Campaigns::Invitation>] the invitations in campaigns you have been issued.
57
+ has_many :invitations, class_name: 'Core::Models::Campaigns::Invitation', inverse_of: :account
58
+ # @!attribute [rw] invitations
59
+ # @return [Array<Core::Models::Campaigns::Invitation>] the invitations you've issued yourself to other players.
60
+ has_many :created_invitations, class_name: 'Core::Models::Campaigns::Invitation', inverse_of: :creator
61
+ # @!attribute [rw] permissions
62
+ # @return [Array<Core::Models::Files::Permission>] the file access permissions granted to this account.
63
+ has_many :permissions, class_name: 'Core::Models::Files::Permission', inverse_of: :account
64
+ # @!attribute [rw] messages
65
+ # @return [Array<Core::Models::Chatrooms::Messages>] all the messages ever sent by the user.
66
+ has_many :messages, class_name: 'Core::Models::Chatrooms::Message', inverse_of: :account
67
+
68
+ has_many :memberships, class_name: 'Core::Models::Chatrooms::Membership', inverse_of: :account
69
+
70
+ # @!attribute [rw] notifications
71
+ # @return [Array<Core::Models::Notification>] the notifications linked to this user.
72
+ embeds_many :notifications, class_name: 'Core::Models::Notification', inverse_of: :account
73
+
74
+ # @return [Array<Core::Models::Notification>] the unread notifications that should be displayed first for the user.
75
+ def unread_notifications
76
+ notifications.where(read: false)
77
+ end
78
+
79
+ # @return [Array<Core::Models::Notification>] the notifications already read, less important to display than the unread ones.
80
+ def read_notifications
81
+ notifications.where(read: true)
82
+ end
83
+
84
+ validates :username,
85
+ presence: {message: 'required'},
86
+ length: {minimum: 6, message: 'minlength', if: :username?},
87
+ uniqueness: {message: 'uniq', if: :username?}
88
+
89
+ validates :email,
90
+ presence: {message: 'required'},
91
+ format: {with: /\A[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\z/, message: 'pattern', if: :email?},
92
+ uniqueness: {message: 'uniq', if: :email?}
93
+
94
+ validates :password,
95
+ presence: {message: 'required', if: ->{ !persisted? || password_digest_changed? }},
96
+ confirmation: {message: 'confirmation', if: :password_digest_changed?}
97
+
98
+ validates :password_confirmation,
99
+ presence: {message: 'required', if: :password_digest_changed?}
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,30 @@
1
+ module Core
2
+ module Models
3
+ module Authentication
4
+ # A session represents the connection of the user on our frontend application. Nobody else than our frontend should
5
+ # have access to the session or it's content (in particular to the token), instead they shall use the OAuth2.0 protocol.
6
+ # A session shall ONLY be created by a premium application (only our frontend applications are premium).
7
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
8
+ class Session
9
+ include Mongoid::Document
10
+ include Mongoid::Timestamps
11
+
12
+ # @!attribute [rw] token
13
+ # @return [String] the unique token for this session, used to identify it and be sure the user is connected on this application.
14
+ field :token, type: String
15
+ # @!attribute [rw] websocket_id
16
+ # @return [String] the ID of the websocket on which the session is connected. It's not an association because instances are embedded.
17
+ field :websocket_id, type: String, default: ''
18
+
19
+ # @!attribute [rw] account
20
+ # @return [Core::Models::Account] the account connected to the application.
21
+ belongs_to :account, class_name: 'Core::Models::Account', inverse_of: :sessions
22
+
23
+ validates :token,
24
+ presence: {message: 'required'},
25
+ uniqueness: {message: 'uniq', if: :token?},
26
+ length: {minimum: 10, message: 'minlength', if: :token?}
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ module Core
2
+ module Models
3
+ # This module holds the logic for user authentication to our frontend.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ module Authentication
6
+ autoload :Session, 'core/models/authentication/session'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,110 @@
1
+ module Core
2
+ module Models
3
+ # A campaign is a gathering of accounts playing on the same interface, and interacting in a common game.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ class Campaign
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+
9
+ # @!attribute [rw] title
10
+ # @return [String] the title, or name, of the campaign, used to identify it in the list.
11
+ field :title, type: String
12
+ # @!attribute [rw] description
13
+ # @return [String] a more detailed description, used to give further information about the campaign in general.
14
+ field :description, type: String
15
+ # @!attribute [rw] is_private
16
+ # @return [Boolean] TRUE if the campaign can be joined only by being invited by the creator, FALSE if it's publicly displayed and accessible.
17
+ field :is_private, type: Boolean, default: true
18
+ # @!attribute [rw] tags
19
+ # @return [Array<String>] an array of tags describing characteristics of this campaign.
20
+ field :tags, type: Array, default: []
21
+ # @!attributes [rw] max_players
22
+ # @return [Integer] the maximum number of players allowed in this campaign.
23
+ field :max_players, type: Integer, default: 5
24
+
25
+ # @!attribute [rw] invitations
26
+ # @return [Array<Core::Models::Campaigns::Invitation>] the invitations to players that have been made for this campaign.
27
+ has_many :invitations, class_name: 'Core::Models::Campaigns::Invitation', inverse_of: :campaign
28
+ # @!attribute [rw] files
29
+ # @return [Array<Core::Models::Files::Document>] the files uploaded in this campaign.
30
+ has_many :files, class_name: 'Core::Models::Files::Document'
31
+
32
+ # @!attribute [rw] chatroom
33
+ # @return [Core::Models::Chatrooms::Campaign] the chatroom linked to this campaign.
34
+ embeds_one :chatroom, class_name: 'Core::Models::Chatrooms::Campaign', inverse_of: :campaign
35
+
36
+ # @!attribute [rw] ruleset
37
+ # @return [Core::Models::Ruleset] the set of rules this campaign is based upon.
38
+ belongs_to :ruleset, class_name: 'Core::Models::Ruleset', inverse_of: :campaigns, optional: true
39
+
40
+ validates :title,
41
+ presence: {message: 'required'},
42
+ length: {minimum: 4, message: 'minlength', if: :title?}
43
+
44
+ validates :max_players,
45
+ numericality: {less_than: 21, message: 'maximum'}
46
+
47
+ validate :title_unicity
48
+
49
+ validate :max_players_minimum
50
+
51
+ # Sets the creator of the campaign. This method is mainly used for backward-compatibility needs.
52
+ # @param account [Core::Models::Account] the account of the creator for this campaign.
53
+ def creator=(account)
54
+ if !invitations.where(account: account).exists?
55
+ invitation = Core::Models::Campaigns::Invitation.create(
56
+ campaign: self,
57
+ account: account,
58
+ status: :creator
59
+ )
60
+ end
61
+ end
62
+
63
+ # Getter for the creator account of this campaign.
64
+ # @return [Core::Models::Account] the account of the player creating this campaign.
65
+ def creator
66
+ invitations.to_a.find(&:status_creator?).account
67
+ end
68
+
69
+ # Adds an error message if the account creating this campaign already has a campaign with the very same name.
70
+ def title_unicity
71
+ # First we take all the other campaign ids of the user.
72
+ campaign_ids = creator.invitations.where(:campaign_id.ne => _id).pluck(:campaign_id)
73
+ # With this list of campaign IDs, we look for a campaign with the same title.
74
+ same_title_campaign = Core::Models::Campaign.where(:_id.in => campaign_ids, title: title)
75
+ if !creator.nil? && title? && same_title_campaign.exists?
76
+ errors.add(:title, 'uniq')
77
+ end
78
+ end
79
+
80
+ # Validation for the max number of players for a campaign.
81
+ # If there is a max number of players, and the current number of
82
+ # players is above it, or the max number of players is 0, raises an error.
83
+ def max_players_minimum
84
+ if max_players? && (max_players < players_count || max_players < 1)
85
+ errors.add(:max_players, 'minimum')
86
+ end
87
+ end
88
+
89
+ # @return [Array<Core::Models::Campaigns::Invitation>] the players in this campaign.
90
+ def players
91
+ invitations.to_a.select do |invitation|
92
+ [:creator, :accepted].include? invitation.enum_status
93
+ end
94
+ end
95
+
96
+ # @return [Integer] the number of players in this campaign.
97
+ def players_count
98
+ players.count
99
+ end
100
+
101
+ def messages
102
+ chatroom.messages
103
+ end
104
+
105
+ after_initialize do
106
+ self.chatroom = Core::Models::Chatrooms::Campaign.new(campaign: self)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Core
4
+ module Models
5
+ module Campaigns
6
+ # An invitation is the linked between a player and a campaign.
7
+ # It keeps the history of the interaction between the player and the campaign.
8
+ #
9
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
10
+ class Invitation
11
+ include Mongoid::Document
12
+ include Mongoid::Timestamps
13
+ include Core::Models::Concerns::Enumerable
14
+ include Core::Models::Concerns::Historizable
15
+
16
+ # @!attribute [rw] account
17
+ # @return [Core::Models::Account] the account the invitation has been issued to.
18
+ belongs_to :account, class_name: 'Core::Models::Account', inverse_of: :invitations
19
+ # @!attribute [rw] campaign
20
+ # @return [Core::Models::Campaign] the campaign the invitation has been made in.
21
+ belongs_to :campaign, class_name: 'Core::Models::Campaign', inverse_of: :invitations
22
+
23
+ # @!attribute [rw] status
24
+ # @return [Symbol] the current status of the invitation.
25
+ historize enum_field :status,
26
+ [:pending, :request, :accepted, :refused, :expelled, :left, :master, :creator],
27
+ default: :pending
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Core
4
+ module Models
5
+ module Campaigns
6
+ # A campaign tag is a string describing a characteristic of the campaign it's in.
7
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
8
+ class Tag
9
+ include Mongoid::Document
10
+ include Mongoid::Timestamps
11
+
12
+ # @!attribute [rw] content
13
+ # @return [String] the string content of the tag, describing a characteristic.
14
+ field :content, type: String
15
+ # @!attribute [rw] count
16
+ # @return [Integer] the number of campaigns this tag is in, avoiding a join.
17
+ field :count, type: Integer, default: 1
18
+
19
+ validates :content, presence: { message: 'required' }, uniqueness: { message: 'uniq' }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ module Core
2
+ module Models
3
+ # The campaigns module is holding the logic for some objects related to campaigns.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ module Campaigns
6
+ autoload :Invitation, 'core/models/campaigns/invitation'
7
+ autoload :Tag , 'core/models/campaigns/tag'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module Core
2
+ module Models
3
+ module Chatrooms
4
+ # The base chatroom class, made to be subclassed in campaign and personal chatrooms.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class Base
7
+ include Mongoid::Document
8
+ include Mongoid::Timestamps
9
+
10
+ # @!attribute [rw] messages
11
+ # @return [Array<Core::Models::Chatrooms::Message>] the messages sent in this chatroom.
12
+ has_many :messages, class_name: 'Core::Models::Chatrooms::Message', inverse_of: :chatroom
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module Core
2
+ module Models
3
+ module Chatrooms
4
+ # Represents the chatroom embedded in a campaign.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class Campaign < Core::Models::Chatrooms::Base
7
+ # @!attribute [rw] campaign
8
+ # @return [Core::Models::Campaign] the campaign the chatroom is linked to.
9
+ embedded_in :campaign, class_name: 'Core::Models::Campaign', inverse_of: :chatroom
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Core
2
+ module Models
3
+ module Chatrooms
4
+ class Conversation < Core::Models::Chatrooms::Base
5
+ has_many :memberships, class_name: 'Core::Models::Chatrooms::Membership', inverse_of: :chatroom
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module Core
2
+ module Models
3
+ module Chatrooms
4
+ class Membership
5
+ include Mongoid::Document
6
+ include Mongoid::Timestamps
7
+ include Core::Models::Concerns::Enumerable
8
+
9
+ enum_field :status, [:shown, :hidden], default: :shown
10
+
11
+ belongs_to :chatroom, class_name: 'Core::Models::Chatrooms::Private', inverse_of: :memberships
12
+
13
+ belongs_to :account, class_name: 'Core::Models::Account', inverse_of: :memberships
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ module Core
2
+ module Models
3
+ module Chatrooms
4
+ # This model represents an in-game tchat message sent in the tchat of a campaign.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class Message
7
+ include Mongoid::Document
8
+ include Mongoid::Timestamps
9
+ include Core::Models::Concerns::Enumerable
10
+
11
+ # @!attribute [rw] type
12
+ # @return [Symbol] the type of message (plain text or command) contained in the data, used to parse and display it.
13
+ enum_field :type, [:text, :command], default: :text
14
+ # @!attribute [rw] data
15
+ # @return [Hash] the additional data passed to the message (arguments of the command, or content of the text)
16
+ field :data, type: Hash, default: {}
17
+ # @!attribute {rw} raw
18
+ # @return [String] the content as typed by the user, without any parsing or transformation.
19
+ field :raw, type: String, default: ''
20
+ # @!attribute [rw] deleted
21
+ # @return [Boolean] TRUE if the message has been marked as deleted by its user, FALSE otherwise.
22
+ field :deleted, type: Boolean, default: false
23
+
24
+ # @!attribute [rw] campaign
25
+ # @return [Core::Models::Chatrooms::Campaign] the chatroom in which the message has been emitted.
26
+ belongs_to :chatroom, class_name: 'Core::Models::Chatrooms::Campaign', inverse_of: :messages
27
+ # @!attribute [rw] player
28
+ # @return [Core::Models::Account] the account that has emitted the message in the campaign.
29
+ belongs_to :account, class_name: 'Core::Models::Account', inverse_of: :messages
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ module Core
2
+ module Models
3
+ # The chatrooms modules regroup all classes concerning messages between players.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ module Chatrooms
6
+ autoload :Base , 'core/models/chatrooms/base'
7
+ autoload :Campaign , 'core/models/chatrooms/campaign'
8
+ autoload :Conversation, 'core/models/chatrooms/conversation'
9
+ autoload :Message , 'core/models/chatrooms/message'
10
+ autoload :Membership , 'core/models/chatrooms/membership'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ module Core
2
+ module Models
3
+ module Concerns
4
+ # Concerns for the objects that can be activated or deactivated, included the corresponding scopes.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ module Activable
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # @!attribute [rw] active
11
+ # @return [Boolean] the active status of the instance, indicating if someone has deactivated it or not.
12
+ field :active, type: Boolean, default: true
13
+
14
+ scope :active , ->{ where(active: true) }
15
+ scope :inactive, ->{ where(active: false) }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Core
2
+ module Models
3
+ module Concerns
4
+ # Includes the diagnostic URL field, and the related validations.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ module Diagnosticable
7
+ extend ActiveSupport::Concern
8
+
9
+ # Module holding the class methods for the classes including this concern.
10
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
11
+ included do
12
+ # @!attribute [rw] diagnostic
13
+ # @return [String] the diagnostic URL to know the status of an entity (usually a gateway, or an instance of a service).
14
+ field :diagnostic, type: String, default: '/status'
15
+
16
+ validates :diagnostic,
17
+ presence: {message: "required"},
18
+ length: {minimum: 4, message: "minlength"},
19
+ format: {with: /\A(\/[a-z]+)+\z/, message: "pattern"}
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ module Core
2
+ module Models
3
+ module Concerns
4
+ # Defines enumerations for the Mongoid models. An enumeration is a field that can only use a given set of values.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ module Enumerable
7
+ extend ActiveSupport::Concern
8
+
9
+ # Submodule holding all the static methods add to the current subclass.
10
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
11
+ module ClassMethods
12
+
13
+ # Creates the field with the given name, set of possible values, and options.
14
+ # @param field_name [String] the name of the enumerated field.
15
+ # @param values [Array<Symbol>] the possible values of the enumerated field.
16
+ # @param options [Hash<Symbol, Any>] the possible options for the field.
17
+ def enum_field(field_name, values, options = {})
18
+ returned = field :"enum_#{field_name}", type: Symbol, default: options[:default]
19
+
20
+ validates :"enum_#{field_name}", inclusion: {in: values.map(&:to_sym), message: 'inclusion'}
21
+
22
+ define_method field_name do
23
+ return self["enum_#{field_name}"]
24
+ end
25
+
26
+ define_method "#{field_name}=" do |value|
27
+ if values.include? value.to_sym
28
+ self["enum_#{field_name}"] = value.to_sym
29
+ end
30
+ end
31
+
32
+ values.map(&:to_sym).each do |value|
33
+ define_method "#{field_name}_#{value}!" do
34
+ self["enum_#{field_name}"] = value
35
+ end
36
+
37
+ define_method "#{field_name}_#{value}?" do
38
+ self["enum_#{field_name}"] == value
39
+ end
40
+ end
41
+
42
+ # This is to make enumerations historizable by
43
+ # returning the field object created by Mongoid.
44
+ returned
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Core
4
+ module Models
5
+ module Concerns
6
+ # This module is what I call "a beautiful piece of Ruby engineering"
7
+ # It takes any mongoid field that you may have declared, and historizes
8
+ # it in a dedicated relation if this relation does not already exists.
9
+ #
10
+ # What it does exactly :
11
+ # - Creates the :history relation if it does not already exists.
12
+ # - Creates a method to check for changes of a specific attribute.
13
+ # - Check for changes at initialization to insert the first value.
14
+ # - Check for changes at update/save to store the history.
15
+ #
16
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
17
+ module Historizable
18
+ extend ActiveSupport::Concern
19
+
20
+ # Adds an entry in the history table for the given field.
21
+ # It checks several things to make the history entry valid :
22
+ # - the new value is different from the old value
23
+ # - the old value is identical to the last recorded new value.
24
+ #
25
+ # @param field [String] the name of the field to historize
26
+ # @param from [Any] the old value before update
27
+ # @param to [Any] the new value after update.
28
+ def add_history(field:, from:, to:)
29
+ return if from == to
30
+ return if !history.empty? && history.order_by(:created_at.desc).first.to != from
31
+
32
+ event = Core::Models::Event.create(field: field, from: from, to: to, document: self)
33
+ event.save
34
+ end
35
+
36
+ # Submodule holding all the static methods add to the current subclass.
37
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
38
+ module ClassMethods
39
+
40
+ # Takes the Mongoid declared field and creates the callbacks
41
+ # to intercept any value change and add it to the history.
42
+ # @field field [Mongoid::Fields::Standard] the Mongoid field to historize.
43
+ def historize(field)
44
+
45
+ unless relations.key?('history')
46
+ embeds_many :history, class_name: 'Core::Models::Event'
47
+ end
48
+
49
+ after_initialize do |doc|
50
+ add_history(field: field.name, from: nil, to: doc[field.name])
51
+ end
52
+
53
+ after_save do |doc|
54
+ if doc.changed_attributes.key?(field.name)
55
+ from = doc.changed_attributes[field.name]
56
+ add_history(field: field.name, from: from, to: doc[field.name])
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ module Core
2
+ module Models
3
+ module Concerns
4
+ # Includes the MIME type field to files to ensure only supported types are included.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ module MimeTypable
7
+ extend ActiveSupport::Concern
8
+
9
+ # Submodule holding all the static methods add to the current subclass.
10
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
11
+ module ClassMethods
12
+ # Defines the MIME type attribute with the given possible MIME types.
13
+ # @param values [Array<String>] the possible MIME types, * can be used as wild cards.
14
+ def mime_type(values)
15
+
16
+ # @!attribute [rw] mime_type
17
+ # @return [String] the MIME type of the file, obtained from the uploaded file.
18
+ field :mime_type, type: String
19
+
20
+ validates :mime_type, presence: {message: 'required'}
21
+
22
+ validate :mime_type_validity, if: :mime_type?
23
+
24
+ # Validates the validity of the MIME type by checking if it respects any of the given mime types.
25
+ # If it does not respect any MIME types possible, it adds an error to the mime_type field and invalidates.
26
+ define_method :mime_type_validity do
27
+ checked_types = if values.is_a? Symbol
28
+ self.send(values) rescue []
29
+ else
30
+ values
31
+ end
32
+ return true if checked_types.empty?
33
+ checked_types.each do |type|
34
+ type_regex = ::Regexp.new("^#{type.sub(/\*/, '(.+)')}$")
35
+ return true if !type_regex.match(mime_type).nil?
36
+ end
37
+ errors.add(:mime_type, 'pattern')
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ module Core
2
+ module Models
3
+ module Concerns
4
+ # Includes the premium field to make the entity including it accessible only to premium applications or not.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ module Premiumable
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # @!attribute [rw] premium
11
+ # @return [Boolean] TRUE if the entity is made to be accessible only to premiuma pplications, FALSE otherwise.
12
+ field :premium, type: Boolean, default: false
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end