wco_models 3.1.0.31

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/README.txt +2 -0
  3. data/Rakefile +15 -0
  4. data/app/assets/config/wco_models_manifest.js +4 -0
  5. data/app/assets/stylesheets/wco_models/application.css +22 -0
  6. data/app/assets/stylesheets/wco_models/utils.scss +53 -0
  7. data/app/controllers/ish_models/application_controller.rb +4 -0
  8. data/app/controllers/wco/products_controller.rb +7 -0
  9. data/app/helpers/ish_models/application_helper.rb +4 -0
  10. data/app/jobs/wco_hosting/certbot_job.rb +24 -0
  11. data/app/mailers/ish_models/application_mailer.rb +6 -0
  12. data/app/models/wco/gallery.rb +56 -0
  13. data/app/models/wco/lead.rb +7 -0
  14. data/app/models/wco/leadset.rb +45 -0
  15. data/app/models/wco/newsitem.rb +9 -0
  16. data/app/models/wco/office_action.rb +32 -0
  17. data/app/models/wco/photo.rb +85 -0
  18. data/app/models/wco/premium_item.rb +13 -0
  19. data/app/models/wco/premium_tier.rb +13 -0
  20. data/app/models/wco/price.rb +25 -0
  21. data/app/models/wco/product.rb +18 -0
  22. data/app/models/wco/profile.rb +22 -0
  23. data/app/models/wco/publisher.rb +49 -0
  24. data/app/models/wco/site.rb +57 -0
  25. data/app/models/wco/subscription.rb +22 -0
  26. data/app/models/wco/tag.rb +16 -0
  27. data/app/models/wco/utils.rb +45 -0
  28. data/app/models/wco_email/campaign.rb +55 -0
  29. data/app/models/wco_email/context.rb +114 -0
  30. data/app/models/wco_email/conversation.rb +48 -0
  31. data/app/models/wco_email/email_filter.rb +45 -0
  32. data/app/models/wco_email/message_template.rb +6 -0
  33. data/app/models/wco_email/scheduled_email_action.rb +64 -0
  34. data/app/models/wco_email/tag.rb-trash +15 -0
  35. data/app/models/wco_hosting/appliance.rb +44 -0
  36. data/app/models/wco_hosting/appliance_tmpl.rb +57 -0
  37. data/app/models/wco_hosting/domain.rb +24 -0
  38. data/app/models/wco_hosting/serverhost.rb +205 -0
  39. data/app/views/layouts/ish_models/application.html.erb +15 -0
  40. data/app/views/wco/application/_alerts_notices.haml +22 -0
  41. data/app/views/wco/application/_meta.haml +13 -0
  42. data/app/views/wco_hosting/docker-compose/dc-any.erb +16 -0
  43. data/app/views/wco_hosting/docker-compose/dc-helloworld.erb +16 -0
  44. data/app/views/wco_hosting/docker-compose/dc-wordpress.erb +16 -0
  45. data/app/views/wco_hosting/scripts/create_subdomain.json.erb-trash +16 -0
  46. data/app/views/wco_hosting/scripts/create_volume.erb +21 -0
  47. data/app/views/wco_hosting/scripts/nginx_site.conf.erb +10 -0
  48. data/config/routes.rb +3 -0
  49. data/lib/tasks/ish_models_tasks.rake +4 -0
  50. data/lib/wco/engine.rb +4 -0
  51. data/lib/wco_models.rb +12 -0
  52. metadata +250 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 107a72e8dbf9e3b59eb0664e267460df99d052c5e34456db964d94974a573b02
4
+ data.tar.gz: '08df2b530187d084924b3c3bc0a77d4708ca1548d167b65f149dfd8824dd3039'
5
+ SHA512:
6
+ metadata.gz: 25550a9eb2560ac522a9802ac7d5f3dfd611081ea2acd0cee1985be0377945ae966cb7931c2071f764a7d9803d222b091fa506d45f8f4c86d066475a3855b6b3
7
+ data.tar.gz: 7d6e61440d0654ef853d83677cbe91ad4fcada13b5bc7a8c2de8d8ca9c8aba4a078f7f0ef6a86d11ee4e550e77db420e300900eefaa4d9e6ce3a5b8ed1c9347d
data/README.txt ADDED
@@ -0,0 +1,2 @@
1
+
2
+ IshModels, soon to be WcoModels
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler/setup"
2
+
3
+ load "rails/tasks/statistics.rake"
4
+
5
+ require "bundler/gem_tasks"
6
+
7
+ require "rake/testtask"
8
+
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'test'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = false
13
+ end
14
+
15
+ task default: :test
@@ -0,0 +1,4 @@
1
+ //
2
+ //= link_directory ../stylesheets/wco_models .css
3
+ //
4
+ console.log('Loaded wco_models/wco_models_manifest.js')
@@ -0,0 +1,22 @@
1
+ /*
2
+ *= require_tree .
3
+ *= require_self
4
+ **/
5
+
6
+ /* body {
7
+ &:before {
8
+ content: 'wco_models/application.css';
9
+ color: red;
10
+ }
11
+ } */
12
+
13
+ /* body {
14
+ color: red;
15
+ } */
16
+
17
+ /* .min-w-100px {
18
+ min-width: 100px;
19
+ }
20
+ .min-w-40px {
21
+ min-width: 40px;
22
+ } */
@@ -0,0 +1,53 @@
1
+
2
+ // label {
3
+ // display: block;
4
+ // }
5
+
6
+ .code,
7
+ .monospace {
8
+ font-family: monospace;
9
+ font-size: 0.8em;
10
+ }
11
+
12
+ textarea {
13
+ border: 1px solid red;
14
+
15
+ min-width: 400px;
16
+ width: 100%;
17
+
18
+ min-height: 300px;
19
+ }
20
+
21
+ .flex-row {
22
+ display: flex;
23
+ }
24
+
25
+ .maxwidth {
26
+ max-width: 1000px;
27
+ margin: auto;
28
+ }
29
+ .padded {
30
+ margin: 0.5em;
31
+ }
32
+
33
+ table.bordered {
34
+
35
+ border: 1px solid gray;
36
+ border-bottom: 0;
37
+ border-right: 0;
38
+
39
+ th {
40
+ background: #ddd;
41
+ }
42
+
43
+ td {
44
+ border: 1px solid gray;
45
+ border-top: 0;
46
+ border-left: 0;
47
+ }
48
+
49
+ }
50
+
51
+ hr {
52
+ border-top: 1px solid red;
53
+ }
@@ -0,0 +1,4 @@
1
+ module IshModels
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+
2
+ class Wco::ProductsController < ActionController::Base
3
+
4
+ def index
5
+ end
6
+
7
+ end
@@ -0,0 +1,4 @@
1
+ module IshModels
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,24 @@
1
+
2
+ require 'net/scp'
3
+ require 'open3'
4
+
5
+ class CertbotJob
6
+ include Sidekiq::Job
7
+ sidekiq_options queue: 'wasya_co_rb'
8
+
9
+ def perform id
10
+ puts! id, 'CertbotJob#perform...'
11
+
12
+ app = WcoHosting::Appliance.find id
13
+ cmd = "ssh #{app.serverhost.ssh_host} 'certbot run -d #{app.host} --nginx -n ' "
14
+ stdout, stderr, status = Open3.capture3(cmd)
15
+ status = status.to_s.split.last.to_i
16
+
17
+ if 0 != status && 0 != app.n_retries
18
+ app.update_attributes({ n_retries: app.n_retries - 1 })
19
+ CertbotJob.perform_in( 15.minutes, app.id.to_s )
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,6 @@
1
+ module IshModels
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,56 @@
1
+
2
+ class Wco::Gallery
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ # include Wco::PremiumItem
6
+ include Wco::Utils
7
+ store_in collection: 'wco_galleries'
8
+
9
+ PER_PAGE = 6
10
+
11
+ field :name
12
+ validates :name, :uniqueness => true
13
+ index({ :name => -1 }) ## 2023-09-23 removed uniqueness
14
+
15
+ field :subhead
16
+ field :descr, :as => :description
17
+
18
+ field :is_public, type: Boolean, default: false
19
+ # has_and_belongs_to_many :shared_profiles, :class_name => 'Wco::Profile', :inverse_of => :shared_galleries
20
+
21
+ field :is_trash, type: Boolean, default: false
22
+ field :is_done, type: Boolean, default: false
23
+
24
+ def published
25
+ where({ :is_public => true, :is_trash => false }).order_by({ :created_at => :desc })
26
+ end
27
+
28
+ field :x, :type => Float
29
+ field :y, :type => Float
30
+ field :z, :type => Float
31
+
32
+ field :lang, :default => 'en'
33
+ # field :username
34
+ # field :lead_id, type: :integer
35
+
36
+ field :slug
37
+ index({ :slug => -1 }, { :unique => true })
38
+ validates :slug, presence: true, uniqueness: true
39
+ before_validation :set_slug, :on => :create
40
+
41
+ def self.list conditions = { :is_trash => false }
42
+ out = self.unscoped.where( conditions ).order_by( :created_at => :desc )
43
+ [['', nil]] + out.map { |item| [ "#{item.created_at.strftime('%Y%m%d')} #{item.name}", item.id ] }
44
+ end
45
+
46
+ # belongs_to :profile, class_name: 'Wco::Profile', inverse_of: :galleries
47
+
48
+ # has_many :newsitems, class_name: 'Wco::Newsitem'
49
+ has_many :photos, class_name: '::Wco::Photo', order: { weight: :asc }
50
+
51
+ def export_fields
52
+ %w| name subhead descr |
53
+ end
54
+
55
+ end
56
+
@@ -0,0 +1,7 @@
1
+
2
+ class Wco::Lead
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ store_in collection: 'wco_leads'
6
+
7
+ end
@@ -0,0 +1,45 @@
1
+
2
+ class Wco::Leadset
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ store_in collection: 'wco_leadsets'
6
+
7
+ field :company_url
8
+ def domain; company_url; end ## @TODO: remove
9
+ validates :company_url, presence: true, uniqueness: true
10
+ index({ company_url: 1 }, { name: 'company_url' })
11
+
12
+ field :email
13
+ index({ email: 1 }, { name: 'email' })
14
+ validates :email, presence: true # , uniqueness: true ## @TODO: should it be unique? _vp_ 2023-12-22
15
+
16
+ has_many :profiles, class_name: 'Wco::Profile', inverse_of: :leadset
17
+ has_many :appliances, class_name: 'WcoHosting::Appliance', inverse_of: :leadset
18
+ has_many :subscriptions, class_name: 'Wco::Subscription', inverse_of: :leadset
19
+
20
+ has_and_belongs_to_many :serverhosts, class_name: 'WcoHosting::Serverhost' # , inverse_of: :leadset
21
+ def next_serverhost
22
+ serverhosts.first
23
+ end
24
+
25
+ ##
26
+ ## stripe
27
+ ##
28
+ field :customer_id
29
+ def customer_id
30
+ if self[:customer_id].blank?
31
+ return nil if !email
32
+ existing = Stripe::Customer.search({ query: "email: '#{email}'" })
33
+ # puts! existing, 'existing'
34
+ if existing.data.present?
35
+ update_attributes( customer_id: existing.data[0][:id] )
36
+ else
37
+ customer = Stripe::Customer.create({ email: email })
38
+ # puts! customer, 'customer'
39
+ update_attributes( customer_id: customer[:id] )
40
+ end
41
+ end
42
+ self[:customer_id]
43
+ end
44
+
45
+ end
@@ -0,0 +1,9 @@
1
+
2
+
3
+ class Wco::Newsitem
4
+ include Mongoid::Document
5
+ include Mongoid::Timestamps
6
+ store_in collection: 'wco_newsitems'
7
+
8
+ belongs_to :profile, class_name: 'Wco::Profile', optional: true
9
+ end
@@ -0,0 +1,32 @@
1
+
2
+ ##
3
+ ## 2023-09-13 _vp_ I don't know if it's even used.
4
+ ##
5
+ class Wco::OfficeAction
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+ store_in collection: 'office_actions'
9
+
10
+ field :slug, type: :string
11
+ validates :slug, uniqueness: true, allow_nil: true
12
+
13
+ field :descr, type: :string ## optional
14
+
15
+
16
+ STATE_ACTIVE = 'active'
17
+ STATE_INACTIVE = 'inactive'
18
+ STATES = [ STATE_ACTIVE, STATE_INACTIVE ]
19
+ field :state, type: :string
20
+ scope :active, ->{ where( state: STATE_ACTIVE ) }
21
+
22
+
23
+ has_many :ties, class_name: '::Office::ActionTie', inverse_of: :office_action
24
+ has_many :prev_ties, class_name: '::Office::ActionTie', inverse_of: :next_office_action
25
+ accepts_nested_attributes_for :ties
26
+
27
+ field :action_exe, type: :string
28
+
29
+ field :perform_at, type: :time
30
+
31
+ end
32
+ OAct = Wco::OfficeAction
@@ -0,0 +1,85 @@
1
+
2
+ # require 'aws-sdk'
3
+ require 'mongoid_paperclip'
4
+
5
+ class Wco::Photo
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+ include Mongoid::Paperclip
9
+ include Wco::Utils
10
+
11
+ # belongs_to :email_message, :optional => true, :class_name => 'Office::EmailMessage'
12
+ # belongs_to :report, :optional => true
13
+ belongs_to :gallery, class_name: 'Wco::Gallery', :optional => true
14
+ # belongs_to :newsitem, :optional => true
15
+
16
+ # photo.photo.to_s.split('/').last.split('?').first
17
+ field :name, :type => String
18
+ def name
19
+ return self[:name] if self[:name]
20
+ update_attribute(:name, self.photo.to_s.split('/').last.split('?').first)
21
+ name
22
+ end
23
+
24
+ field :ordering, type: :integer
25
+ index({ ordering: -1 })
26
+
27
+ field :descr, :type => String
28
+ field :subhead
29
+ field :weight, :type => Integer, :default => 10
30
+
31
+ field :is_public, :type => Boolean, :default => true
32
+ field :is_trash, :type => Boolean, :default => false # @TODO: nuke this boolean _vp_ 20170515
33
+ default_scope ->{ where({ :is_trash => false }) }
34
+
35
+ has_mongoid_attached_file :photo,
36
+ :styles => {
37
+ :mini => '20x20#',
38
+ :thumb => "100x100#",
39
+ :thumb2 => "200x200#",
40
+ :s169 => "640x360#",
41
+ :small => "400x400>",
42
+ :large => '950x650>',
43
+ },
44
+ :storage => :s3,
45
+ :s3_credentials => ::S3_CREDENTIALS,
46
+ :path => "photos/:style/:id/:filename",
47
+ :s3_protocol => 'https',
48
+ :validate_media_type => false,
49
+ s3_region: ::S3_CREDENTIALS[:region]
50
+ validates_attachment_content_type :photo, :content_type => ["image/webp", "image/jpg", "image/jpeg", "image/png", "image/gif", 'application/octet-stream' ]
51
+
52
+ def self.n_per_manager_gallery
53
+ 25
54
+ end
55
+
56
+ def export_fields
57
+ %w|
58
+ gallery_id
59
+ name descr weight is_public is_trash
60
+
61
+ photo_file_name photo_content_type photo_file_size photo_updated_at photo_fingerprint
62
+ |
63
+ end
64
+
65
+ ## From: https://gist.github.com/WizardOfOgz/1012107?permalink_comment_id=1442486
66
+ attr_accessor :content_type, :image_data, :original_filename
67
+ def decode_base64_image
68
+ if image_data && content_type && original_filename
69
+ decoded_data = Base64.decode64(image_data)
70
+
71
+ data = StringIO.new(decoded_data)
72
+ data.class_eval do
73
+ attr_accessor :content_type, :original_filename
74
+ end
75
+
76
+ data.content_type = content_type
77
+ data.original_filename = File.basename(original_filename)
78
+
79
+ self.photo = data
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+
@@ -0,0 +1,13 @@
1
+
2
+ module Wco::PremiumItem
3
+
4
+ def self.included base
5
+ base.send :field, :premium_tier, type: Integer, default: 0 # how many unlocks are need, to get access? 0 = free
6
+ base.send :has_many, :premium_purchases, class_name: '::Ish::Payment', as: :item
7
+ end
8
+
9
+ def is_premium
10
+ premium_tier > 0
11
+ end
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+
2
+ module Wco::PremiumItem
3
+
4
+ def self.included base
5
+ base.send :field, :premium_tier, type: Integer, default: 0 # how many unlocks are need, to get access? 0 = free
6
+ base.send :has_many, :premium_purchases, class_name: '::Ish::Payment', as: :item
7
+ end
8
+
9
+ def is_premium
10
+ premium_tier > 0
11
+ end
12
+
13
+ end
@@ -0,0 +1,25 @@
1
+
2
+ class Wco::Price
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+
6
+ belongs_to :product, class_name: 'Wco::Product', inverse_of: :prices
7
+ has_many :subscriptions, class_name: 'Wco::Subscription', inverse_of: :price, foreign_key: :wco_price_id
8
+
9
+ field :amount_cents, type: Integer
10
+
11
+ INTERVALS = [ nil, 'day', 'week', 'month', 'year' ]
12
+ field :interval, type: String
13
+
14
+ field :price_id # stripe
15
+
16
+ def name
17
+ "$#{ amount_cents.to_f/100 } / #{interval}"
18
+ end
19
+ def name_simple
20
+ "$#{ amount_cents.to_f/100 }"
21
+ end
22
+
23
+ end
24
+
25
+
@@ -0,0 +1,18 @@
1
+
2
+ class Wco::Product
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+
6
+ field :name
7
+
8
+ field :product_id # stripe
9
+
10
+ has_many :prices, class_name: 'Wco::Price', inverse_of: :product
11
+
12
+ def self.list
13
+ [ [nil,nil] ] + self.all.order_by({ name: :asc }).map { |i| [i.name, i.id] }
14
+ end
15
+
16
+ end
17
+
18
+
@@ -0,0 +1,22 @@
1
+
2
+ class Wco::Profile
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ store_in collection: 'ish_user_profiles'
6
+
7
+ field :email
8
+ index({ email: 1 }, { name: 'email' })
9
+ validates :email, presence: true, uniqueness: true
10
+
11
+ field :per_page, type: :integer, default: 25
12
+
13
+ belongs_to :leadset, class_name: 'Wco::Leadset', inverse_of: :profile, optional: true
14
+ has_many :newsitems, class_name: 'Wco::Newsitem'
15
+ has_and_belongs_to_many :shared_galleries, class_name: 'Wco::Gallery', inverse_of: :shared_profiles
16
+
17
+
18
+ def self.list
19
+ all.map { |p| [ p.email, p.id ] }
20
+ end
21
+
22
+ end
@@ -0,0 +1,49 @@
1
+
2
+ class Wco::Publisher
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ store_in collection: 'wco_publishers'
6
+
7
+ field :name
8
+
9
+ KIND_ARTICLE = 'article'
10
+ KIND_IMAGE = 'image'
11
+ field :kind, type: :string
12
+
13
+ belongs_to :to_site, class_name: 'Wco::Site'
14
+
15
+
16
+ field :context_eval
17
+ field :post_body_tmpl
18
+
19
+
20
+
21
+ end
22
+
23
+ =begin
24
+
25
+ curl --include \
26
+ --request POST \
27
+ --user admin:<>, \
28
+ --header 'Content-type: application/hal+json' \
29
+ http://pi.local/node?_format=hal_json \
30
+ --data-binary '{
31
+ "_links": {
32
+ "type":{"href":"http://pi.local/rest/type/node/article"}
33
+ },
34
+ "title":[{"value":"Node +++ 123 bac +++" }],
35
+ "body":[{"value": "<b>hello, wor</b>ld!", "format": "full_html" }],
36
+ "type":[{"target_id":"article"}],
37
+ "status": [{"value": 1}],
38
+ "_embedded": {
39
+ "http://pi.local/rest/relation/node/article/field_issue": [
40
+ { "uuid": [{ "value": "56229a95-d675-43e1-99b1-f9e11b5579c5" }] }
41
+ ],
42
+ "http://pi.local/rest/relation/node/article/field_tags": [
43
+ { "uuid": [{ "value": "45646a7d-1a16-42e8-b758-f6e1c8d976f7" }] },
44
+ { "uuid": [{ "value": "834e34e2-05ae-498d-b876-453798872ce1" }] }
45
+ ]
46
+ }
47
+ }'
48
+
49
+ =end
@@ -0,0 +1,57 @@
1
+
2
+ class Wco::Site
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ store_in collection: 'wco_sites'
6
+
7
+ KIND_DRUPAL = 'drupal'
8
+ KIND_IG = 'instagram'
9
+ KIND_WP = 'wordpress'
10
+ KINDS = %w| drupal instagram wordpress |
11
+ field :kind, type: :string
12
+ def self.kinds_list
13
+ [nil] + KINDS
14
+ end
15
+
16
+ has_many :publishers, class_name: 'Wco::Publisher'
17
+
18
+ field :origin # http://pi.local
19
+ field :post_path # /node?_format=hal_json
20
+ field :username
21
+ field :password
22
+
23
+ def self.list
24
+ [[nil,nil]] + all.map { |s| [ s.origin, s.id ] }
25
+ end
26
+
27
+ def body
28
+ {
29
+ "_links": {
30
+ "type":{"href":"http://pi.local/rest/type/node/article"}
31
+ },
32
+ "title":[{"value":"Node +++ 123 bac +++" }],
33
+ "body":[{"value": "<b>hello, wor</b>ld!", "format": "full_html" }],
34
+ "type":[{"target_id":"article"}],
35
+ "status": [{"value": 1}],
36
+ "_embedded": {
37
+ "http://pi.local/rest/relation/node/article/field_issue": [
38
+ { "uuid": [{ "value": "56229a95-d675-43e1-99b1-f9e11b5579c5" }] }
39
+ ],
40
+ "http://pi.local/rest/relation/node/article/field_tags": [
41
+ { "uuid": [{ "value": "45646a7d-1a16-42e8-b758-f6e1c8d976f7" }] },
42
+ { "uuid": [{ "value": "834e34e2-05ae-498d-b876-453798872ce1" }] }
43
+ ]
44
+ }
45
+
46
+ }
47
+ end
48
+
49
+ def do_post
50
+ HTTParty.post( post_url,
51
+ body: JSON.generate( body ),
52
+ headers: { 'Content-Type' => 'application/hal+json' },
53
+ basic_auth: { username: username, password: password },
54
+ )
55
+ end
56
+
57
+ end
@@ -0,0 +1,22 @@
1
+
2
+ class Wco::Subscription
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+
6
+ field :customer_id, type: :string # stripe
7
+ field :price_id, type: :string # stripe
8
+
9
+ field :leadset_id
10
+ def leadset
11
+ Leadset.find leadset_id
12
+ end
13
+
14
+ field :quantity, type: :integer
15
+
16
+ belongs_to :product, class_name: 'Wco::Product', inverse_of: :subscriptions
17
+ belongs_to :price, class_name: 'Wco::Price', inverse_of: :subscriptions, foreign_key: :wco_price_id
18
+ belongs_to :leadset, class_name: 'Wco::Leadset', inverse_of: :subscriptions
19
+
20
+ end
21
+
22
+
@@ -0,0 +1,16 @@
1
+
2
+ class Wco::Tag
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ store_in collection: 'wco_tags'
6
+
7
+ field :slug
8
+ # index
9
+ # validate presence
10
+ # validate uniqueness ?
11
+
12
+ # parent-child
13
+
14
+ has_and_belongs_to_many :email_conversations, class_name: 'WcoEmail::Conversation'
15
+
16
+ end