wco_models 3.1.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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