cloudrider 0.3.0 → 0.3.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (227) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -1
  3. data/cloudrider.gemspec +1 -0
  4. data/generica/.gitignore +18 -0
  5. data/generica/Gemfile +8 -1
  6. data/generica/Gemfile.lock +3 -0
  7. data/generica/app/assets/javascripts/apiv1.js.ls +6 -1
  8. data/generica/app/assets/javascripts/components/offers-summary.js.em +2 -0
  9. data/generica/app/assets/javascripts/components/picture-chunk.js.em +17 -0
  10. data/generica/app/assets/javascripts/components/pictures-group.js.em +21 -0
  11. data/generica/app/assets/javascripts/components/search-and-filter.js.em +4 -0
  12. data/generica/app/assets/javascripts/components/site-nav.js.em +5 -1
  13. data/generica/app/assets/javascripts/components/taggable-field.js.em +28 -0
  14. data/generica/app/assets/javascripts/components/tree/taxon-li.js.em +6 -2
  15. data/generica/app/assets/javascripts/controllers/index_controller.js.em +19 -2
  16. data/generica/app/assets/javascripts/controllers/modals/login_controller.js.em +23 -9
  17. data/generica/app/assets/javascripts/controllers/modals/register_controller.js.em +24 -0
  18. data/generica/app/assets/javascripts/controllers/products/index_controller.js.em +23 -13
  19. data/generica/app/assets/javascripts/controllers/products/product/offers/new_controller.js.em +17 -0
  20. data/generica/app/assets/javascripts/controllers/users/offers/index_controller.js.em +10 -0
  21. data/generica/app/assets/javascripts/controllers/users/product/edit_controller.js.em +31 -0
  22. data/generica/app/assets/javascripts/controllers/users/products/index_controller.js.em +8 -0
  23. data/generica/app/assets/javascripts/controllers/users/products/new_controller.js.em +22 -0
  24. data/generica/app/assets/javascripts/controllers/users/products/slice_controller.js.em +4 -0
  25. data/generica/app/assets/javascripts/helpers/filte_previewer.js.ls +21 -0
  26. data/generica/app/assets/javascripts/helpers/listing_picture.js.em +17 -0
  27. data/generica/app/assets/javascripts/models/offer.js.em +9 -8
  28. data/generica/app/assets/javascripts/models/user.js.em +12 -0
  29. data/generica/app/assets/javascripts/models/users/product.js.em +12 -0
  30. data/generica/app/assets/javascripts/routes/application_route.js.em +10 -2
  31. data/generica/app/assets/javascripts/routes/products/index_route.js.em +23 -2
  32. data/generica/app/assets/javascripts/routes/products/product/offers/new_route.js.em +18 -0
  33. data/generica/app/assets/javascripts/routes/users/offers/index_route.js.em +1 -0
  34. data/generica/app/assets/javascripts/routes/users/product_route.js.em +3 -0
  35. data/generica/app/assets/javascripts/routes/users/products/index_route.js.em +3 -0
  36. data/generica/app/assets/javascripts/routes/users/products/new_route.js.em +3 -0
  37. data/generica/app/assets/javascripts/templates/admin/product/edit.emblem +2 -0
  38. data/generica/app/assets/javascripts/templates/admin/products/_form-core.emblem +44 -0
  39. data/generica/app/assets/javascripts/templates/admin/products/new.emblem +4 -42
  40. data/generica/app/assets/javascripts/templates/components/offers-summary.emblem +9 -0
  41. data/generica/app/assets/javascripts/templates/components/picture-chunk.emblem +9 -0
  42. data/generica/app/assets/javascripts/templates/components/pictures-group.emblem +6 -0
  43. data/generica/app/assets/javascripts/templates/components/search-and-filter.emblem +5 -1
  44. data/generica/app/assets/javascripts/templates/components/taggable-field.emblem +1 -0
  45. data/generica/app/assets/javascripts/templates/modals/register.emblem +57 -0
  46. data/generica/app/assets/javascripts/templates/products/product/offers/_anonymous-form.emblem +11 -0
  47. data/generica/app/assets/javascripts/templates/products/product/offers/_offer-form.emblem +55 -0
  48. data/generica/app/assets/javascripts/templates/products/product/offers/new.emblem +37 -0
  49. data/generica/app/assets/javascripts/templates/products/product/show.emblem +26 -1
  50. data/generica/app/assets/javascripts/templates/users.emblem +24 -0
  51. data/generica/app/assets/javascripts/templates/users/index.emblem +3 -0
  52. data/generica/app/assets/javascripts/templates/users/offers/index.emblem +25 -0
  53. data/generica/app/assets/javascripts/templates/users/offers/slice.emblem +31 -0
  54. data/generica/app/assets/javascripts/templates/users/product/edit.emblem +82 -0
  55. data/generica/app/assets/javascripts/templates/users/products/index.emblem +22 -0
  56. data/generica/app/assets/javascripts/templates/users/products/new.emblem +28 -0
  57. data/generica/app/assets/javascripts/templates/users/products/slice.emblem +27 -0
  58. data/generica/app/assets/stylesheets/apiv1/_application.css.scss +4 -0
  59. data/generica/app/assets/stylesheets/apiv1/components/_offers-summary.css.scss +20 -0
  60. data/generica/app/assets/stylesheets/apiv1/components/_pictures-group.css.scss +32 -0
  61. data/generica/app/assets/stylesheets/apiv1/components/_taggable-field.css.scss +32 -0
  62. data/generica/app/assets/stylesheets/apiv1/products/product/offers/_new.css.scss +8 -0
  63. data/generica/app/controllers/admin/base_controller.rb +1 -1
  64. data/generica/app/controllers/admin/sessions/destroy_controller.rb +16 -0
  65. data/generica/app/controllers/apiv1/offers/create_controller.rb +30 -0
  66. data/generica/app/controllers/apiv1/offers/index_controller.rb +22 -0
  67. data/generica/app/controllers/apiv1/offers/show_controller.rb +12 -0
  68. data/generica/app/controllers/apiv1/products/destroy_controller.rb +6 -0
  69. data/generica/app/controllers/apiv1/products/index_controller.rb +8 -2
  70. data/generica/app/controllers/apiv1/products/show_controller.rb +7 -3
  71. data/generica/app/controllers/apiv1/taxons/index_controller.rb +5 -1
  72. data/generica/app/controllers/apiv1/users/create_controller.rb +17 -0
  73. data/generica/app/controllers/apiv1/users/show_controller.rb +12 -0
  74. data/generica/app/controllers/apiv1/users_controller.rb +9 -0
  75. data/generica/app/controllers/application_controller.rb +3 -0
  76. data/generica/app/controllers/users/offers/create_controller.rb +26 -0
  77. data/generica/app/controllers/users/products/create_controller.rb +26 -0
  78. data/generica/app/controllers/users/products/update_controller.rb +35 -0
  79. data/generica/app/mailers/apiv1/aggregate_mailer.rb +13 -0
  80. data/generica/app/mailers/apiv1/aggregate_mailer/summary_context.rb +121 -0
  81. data/generica/app/mailers/apiv1/notifications_mailer.rb +19 -0
  82. data/generica/app/mailers/apiv1/notifications_mailer/new_offer_context.rb +53 -0
  83. data/generica/app/models/admin/user.rb +43 -2
  84. data/generica/app/models/apiv1/attachment.rb +2 -0
  85. data/generica/app/models/apiv1/email_object.rb +35 -0
  86. data/generica/app/models/apiv1/email_request.rb +107 -0
  87. data/generica/app/models/apiv1/offer_message.rb +53 -0
  88. data/generica/app/models/apiv1/offer_postboy.rb +33 -0
  89. data/generica/app/models/apiv1/picture.rb +2 -0
  90. data/generica/app/models/apiv1/product.rb +28 -6
  91. data/generica/app/models/apiv1/products/offer_message_factory.rb +13 -0
  92. data/generica/app/models/apiv1/products_machine.rb +8 -2
  93. data/generica/app/models/apiv1/taxon.rb +1 -1
  94. data/generica/app/models/apiv1/users/product_factory.rb +22 -0
  95. data/generica/app/models/apiv1/users/product_relationship.rb +19 -0
  96. data/generica/app/models/users/products/offer_factory.rb +40 -0
  97. data/generica/app/varissets/javascripts/templates/application.emblem.erb +2 -2
  98. data/generica/app/varissets/javascripts/templates/components/offers-overview.emblem.erb +1 -0
  99. data/generica/app/varissets/javascripts/templates/components/search-and-filter.emblem.erb +33 -0
  100. data/generica/app/varissets/javascripts/templates/components/site-nav.emblem.erb +8 -0
  101. data/generica/app/varissets/javascripts/templates/components/taggable-field.emblem.erb +11 -0
  102. data/generica/app/varissets/javascripts/templates/modals/login.emblem.erb +36 -0
  103. data/generica/app/varissets/javascripts/templates/products.emblem.erb +4 -0
  104. data/generica/app/varissets/javascripts/templates/products/index.emblem.erb +17 -0
  105. data/generica/app/varissets/javascripts/templates/products/product/offers/_anonymous-form.emblem.erb +11 -0
  106. data/generica/app/varissets/javascripts/templates/products/product/offers/_offer-form.emblem.erb +55 -0
  107. data/generica/app/varissets/javascripts/templates/products/product/offers/new.emblem.erb +37 -0
  108. data/generica/app/varissets/javascripts/templates/products/product/show.emblem.erb +108 -0
  109. data/generica/app/varissets/stylesheets/apiv1/components/_offers-overview.css.scss.erb +3 -0
  110. data/generica/app/varissets/stylesheets/apiv1/components/_offers_overview.css.scss.erb +3 -0
  111. data/generica/app/varissets/stylesheets/apiv1/components/_search-and-filter.css.scss.erb +15 -0
  112. data/generica/app/varissets/stylesheets/apiv1/components/_site-footer.css.scss.erb +23 -0
  113. data/generica/app/varissets/stylesheets/apiv1/components/_taggable-field.css.scss.erb +32 -0
  114. data/generica/app/views/apiv1/home/index.html.haml +3 -1
  115. data/generica/app/views/apiv1/layouts/application.html.haml +37 -2
  116. data/generica/config/{database.yml.erb → database.yml.str} +3 -3
  117. data/generica/config/email.yml +11 -0
  118. data/generica/config/email_slave.god +9 -0
  119. data/generica/config/initializers/premailer.rb +1 -0
  120. data/generica/config/initializers/squash.rb.erb +3 -0
  121. data/generica/config/nginx.conf.erb +4 -4
  122. data/generica/config/routes.rb +14 -0
  123. data/generica/db/migrate/20141114213427_add_user_rank_to_admin_users.rb +9 -0
  124. data/generica/db/migrate/20141114225019_create_apiv1_users_products.rb +9 -0
  125. data/generica/db/migrate/20141116053522_create_apiv1_offer_messages.rb +16 -0
  126. data/generica/db/migrate/20141116235011_add_deleted_at_to_apiv1_products.rb +5 -0
  127. data/generica/db/migrate/20141116235209_add_deleted_at_to_apiv1_offer_messages.rb +5 -0
  128. data/generica/db/migrate/20141116235651_add_deleted_at_to_apiv1_pictures.rb +5 -0
  129. data/generica/db/migrate/20141116235808_add_deleted_at_to_apiv1_attachments.rb +5 -0
  130. data/generica/db/migrate/20141127230304_create_apiv1_email_requests.rb +16 -0
  131. data/generica/db/migrate/20141127230455_create_apiv1_email_objects.rb +11 -0
  132. data/generica/lib/generica/assets_unretardifier.rb +32 -0
  133. data/generica/lib/generica/email_slave.rb +92 -0
  134. data/generica/lib/tasks/email_slave.rake +11 -0
  135. data/generica/lib/tasks/production.rake +1 -1
  136. data/generica/public/tests/bermese.jpg +0 -0
  137. data/generica/public/tests/chow.jpg +0 -0
  138. data/generica/public/tests/malamute.jpg +0 -0
  139. data/generica/public/tests/malamute2.JPG +0 -0
  140. data/generica/spec/controllers/apiv1/users/create_controller_spec.rb +17 -0
  141. data/generica/spec/controllers/users/products/create_controller_spec.rb +31 -0
  142. data/generica/spec/mailers/apiv1/aggregate_mailer_spec.rb +29 -0
  143. data/generica/spec/models/apiv1/email_request_spec.rb +4 -0
  144. data/generica/spec/models/apiv1/offer_postboy_spec.rb +22 -0
  145. data/generica/spec/models/apiv1/product_spec.rb +1 -0
  146. data/generica/spec/models/apiv1/products/offer_message_factory_spec.rb +37 -0
  147. data/generica/spec/models/apiv1/products_machine_spec.rb +77 -0
  148. data/generica/spec/models/apiv1/users/product_factory_spec.rb +47 -0
  149. data/generica/spec/models/users/products/offer_factory_spec.rb +38 -0
  150. data/generica/test/mailers/.keep +0 -0
  151. data/generica/test/mailers/previews/apiv1/aggregate_preview.rb +6 -0
  152. data/generica/test/mailers/previews/apiv1/notifications_preview.rb +5 -0
  153. data/lib/cloudrider.rb +1 -1
  154. data/lib/cloudrider/apiv1/base.rb +2 -2
  155. data/lib/cloudrider/apiv1/login_modal_emblem.rb +25 -0
  156. data/lib/cloudrider/apiv1/offers_anonymous_form_partial_emblem.rb +12 -0
  157. data/lib/cloudrider/apiv1/offers_new_emblem.rb +15 -0
  158. data/lib/cloudrider/apiv1/offers_overview_component.rb +8 -0
  159. data/lib/cloudrider/apiv1/offers_overview_sass.rb +8 -0
  160. data/lib/cloudrider/apiv1/product_show_emblem.rb +31 -0
  161. data/lib/cloudrider/apiv1/products_emblem.rb +11 -0
  162. data/lib/cloudrider/apiv1/products_index_emblem.rb +14 -0
  163. data/lib/cloudrider/apiv1/search_and_filter_component.rb +11 -0
  164. data/lib/cloudrider/apiv1/search_and_filter_sass.rb +11 -0
  165. data/lib/cloudrider/apiv1/site_footer_component.rb +7 -7
  166. data/lib/cloudrider/apiv1/site_footer_sass.rb +9 -0
  167. data/lib/cloudrider/application_commander.rb +46 -34
  168. data/lib/cloudrider/backend_commander.rb +15 -9
  169. data/lib/cloudrider/protofile.rb +18 -2
  170. data/lib/cloudrider/protosite.example.json +26 -0
  171. data/lib/cloudrider/protosite.rb +66 -0
  172. data/lib/cloudrider/serverside/base.rb +2 -2
  173. data/lib/cloudrider/serverside/database_yaml.rb +9 -4
  174. data/lib/cloudrider/serverside/nginx_conf.rb +7 -2
  175. data/lib/cloudrider/serverside/squash_ruby.rb +14 -0
  176. data/lib/cloudrider/serverside/unicorn_conf_ruby.rb +2 -1
  177. data/lib/cloudrider/version.rb +1 -1
  178. data/lib/cloudrider/web_firm.rb +16 -0
  179. data/lib/cloudrider/web_firm/component_architect.rb +23 -0
  180. data/lib/cloudrider/web_firm/page_designer.rb +13 -0
  181. data/lib/cloudrider/web_firm/project_lead.rb +30 -0
  182. data/lib/cloudrider/web_firm/scumbag_engineer.rb +40 -0
  183. data/lib/cloudrider/web_firm/support_intern.rb +113 -0
  184. data/lib/generators/cloudrider/server/server_generator.rb +4 -8
  185. data/lib/generators/cloudrider/website/website_generator.rb +25 -0
  186. data/spec/cloudrider/protosite_spec.rb +63 -0
  187. data/vanilla/.bowerrc +4 -0
  188. data/vanilla/.editorconfig +33 -0
  189. data/vanilla/.ember-cli +9 -0
  190. data/vanilla/.gitignore +17 -0
  191. data/vanilla/.jshintrc +32 -0
  192. data/vanilla/.travis.yml +16 -0
  193. data/vanilla/Brocfile.js +20 -0
  194. data/vanilla/README.md +51 -0
  195. data/vanilla/app/app.js +16 -0
  196. data/vanilla/app/components/.gitkeep +0 -0
  197. data/vanilla/app/controllers/.gitkeep +0 -0
  198. data/vanilla/app/controllers/admin.coffee +5 -0
  199. data/vanilla/app/helpers/.gitkeep +0 -0
  200. data/vanilla/app/index.html +21 -0
  201. data/vanilla/app/models/.gitkeep +0 -0
  202. data/vanilla/app/router.js +11 -0
  203. data/vanilla/app/routes/.gitkeep +0 -0
  204. data/vanilla/app/styles/.gitkeep +0 -0
  205. data/vanilla/app/styles/app.css +3 -0
  206. data/vanilla/app/templates/.gitkeep +0 -0
  207. data/vanilla/app/templates/application.hbs +3 -0
  208. data/vanilla/app/templates/components/.gitkeep +0 -0
  209. data/vanilla/app/views/.gitkeep +0 -0
  210. data/vanilla/bower.json +17 -0
  211. data/vanilla/config/environment.js +47 -0
  212. data/vanilla/package.json +39 -0
  213. data/vanilla/public/.gitkeep +0 -0
  214. data/vanilla/public/crossdomain.xml +15 -0
  215. data/vanilla/public/robots.txt +3 -0
  216. data/vanilla/testem.json +11 -0
  217. data/vanilla/tests/.jshintrc +74 -0
  218. data/vanilla/tests/helpers/resolver.js +11 -0
  219. data/vanilla/tests/helpers/start-app.js +25 -0
  220. data/vanilla/tests/index.html +45 -0
  221. data/vanilla/tests/test-helper.js +12 -0
  222. data/vanilla/tests/unit/.gitkeep +0 -0
  223. data/vanilla/tests/unit/controllers/admin-test.coffee +12 -0
  224. data/vanilla/vendor/.gitkeep +0 -0
  225. metadata +189 -5
  226. data/generica/app/views/layouts/application.html.erb +0 -14
  227. data/lib/cloudrider/master_context.rb +0 -105
@@ -0,0 +1,19 @@
1
+ class Apiv1::NotificationsMailer < ActionMailer::Base
2
+ layout 'apiv1/layouts/email'
3
+ default from: "from@example.com"
4
+
5
+ # Subject can be set in your I18n file at config/locales/en.yml
6
+ # with the following lookup:
7
+ #
8
+ # en.notifications.new_offer.subject
9
+ #
10
+ def new_offer(offer_message)
11
+ @ctx = Apiv1::NotificationsMailer::NewOfferContext.new offer_message
12
+
13
+ mail to: @ctx.to,
14
+ from: @ctx.from,
15
+ cc: @ctx.cc,
16
+ bcc: @ctx.bcc,
17
+ subject: @ctx.subject
18
+ end
19
+ end
@@ -0,0 +1,53 @@
1
+ class Apiv1::NotificationsMailer::NewOfferContext
2
+ delegate :created_at,
3
+ :message,
4
+ :product,
5
+ :company_address,
6
+ :sender_email,
7
+ :phone_number,
8
+ to: :_offer
9
+ def initialize(offer_message)
10
+ @offer = offer_message
11
+ end
12
+ def to
13
+ product.try(:user).try(:email)
14
+ end
15
+ def from
16
+ "secretary@plasticscrapmarket.org"
17
+ end
18
+ def cc
19
+ end
20
+ def bcc
21
+ end
22
+ def product_summary
23
+ product.try :rough_summary
24
+ end
25
+ def subject
26
+ ["new offer", "-", price_terms].join " "
27
+ end
28
+ def price_terms
29
+ _offer.price_terms.present? ? _offer.price_terms : "[missing price terms]"
30
+ end
31
+ def product_id
32
+ product.try :id
33
+ end
34
+ def material
35
+ product.try :material
36
+ end
37
+ def link_path(*relative_path)
38
+ File.join "http://www.plasticscrapmarket.org", '#', *relative_path
39
+ end
40
+ def cta_path
41
+ link_path "products/product", "#{product_id}"
42
+ end
43
+ def company_who_made_offer
44
+ _offer.from_company
45
+ end
46
+ def product_owner
47
+ product.try(:user).try(:company_name) || "listing owner"
48
+ end
49
+ private
50
+ def _offer
51
+ @offer
52
+ end
53
+ end
@@ -10,16 +10,57 @@
10
10
  # updated_at :datetime
11
11
  # remember_me_token :string(255)
12
12
  # remember_me_token_expires_at :datetime
13
+ # user_rank :string(255)
14
+ # company_name :string(255)
15
+ # phone_number :string(255)
16
+ # about_me :text
17
+ # address :string(255)
13
18
  #
14
19
 
15
20
  class Admin::User < ActiveRecord::Base
16
21
  authenticates_with_sorcery!
17
22
  validates :email,
18
- presence: true
23
+ :company_name,
24
+ presence: true,
25
+ uniqueness: true
26
+ validates :password,
27
+ presence: true,
28
+ length: { minimum: 2 },
29
+ confirmation: true
30
+
31
+ has_many :product_relationships,
32
+ class_name: 'Apiv1::Users::ProductRelationship'
33
+
34
+ has_many :products,
35
+ class_name: 'Apiv1::Product',
36
+ through: :product_relationships
37
+
38
+ has_many :offers,
39
+ -> { order "#{Apiv1::OfferMessage.table_name}.created_at desc" },
40
+ through: :products,
41
+ class_name: 'Apiv1::OfferMessage'
42
+
19
43
  def to_ember_hash
20
44
  {
21
45
  id: id,
22
- email: email
46
+ email: email,
47
+ user_rank: user_rank,
48
+ company_name: company_name,
49
+ phone_number: phone_number,
50
+ address: address,
51
+ about_me: about_me
23
52
  }
24
53
  end
54
+
55
+ def admin?
56
+ user_rank.to_s == "admin"
57
+ end
58
+
59
+ def admin!
60
+ update user_rank: "admin"
61
+ end
62
+
63
+ def owns?(product)
64
+ product_relationships.exists?(product_id: product.id)
65
+ end
25
66
  end
@@ -11,9 +11,11 @@
11
11
  # document_updated_at :datetime
12
12
  # created_at :datetime
13
13
  # updated_at :datetime
14
+ # deleted_at :datetime
14
15
  #
15
16
 
16
17
  class Apiv1::Attachment < ActiveRecord::Base
18
+ acts_as_paranoid
17
19
  DocumentTypes = [
18
20
  "application/pdf",
19
21
  "application/docx",
@@ -0,0 +1,35 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: apiv1_email_objects
4
+ #
5
+ # id :integer not null, primary key
6
+ # email_id :integer
7
+ # class_name :string(255)
8
+ # permalink_id :string(255)
9
+ # deleted_at :datetime
10
+ # created_at :datetime
11
+ # updated_at :datetime
12
+ #
13
+
14
+ class Apiv1::EmailObject < ActiveRecord::Base
15
+ acts_as_paranoid
16
+ belongs_to :email,
17
+ class_name: 'Apiv1::EmailRequest'
18
+
19
+ validates :class_name,
20
+ :permalink_id,
21
+ presence: true
22
+
23
+ class << self
24
+ def paramify(some_object)
25
+ {
26
+ class_name: some_object.class,
27
+ permalink_id: some_object.id
28
+ }
29
+ end
30
+ end
31
+
32
+ def objectify
33
+ Object.const_get(class_name).find permalink_id
34
+ end
35
+ end
@@ -0,0 +1,107 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: apiv1_email_requests
4
+ #
5
+ # id :integer not null, primary key
6
+ # to :string(255) not null
7
+ # cc :string(255)
8
+ # bcc :string(255)
9
+ # subject :string(255)
10
+ # status :string(255)
11
+ # from :string(255)
12
+ # mailer_class :string(255) not null
13
+ # deleted_at :datetime
14
+ # created_at :datetime
15
+ # updated_at :datetime
16
+ #
17
+
18
+ class Apiv1::EmailRequest < ActiveRecord::Base
19
+ class MailerClassValidator < ActiveModel::Validator
20
+ def validate(record)
21
+ _enforce_class_existence!(record)
22
+ _enforce_method_existence!(record)
23
+ end
24
+ private
25
+ def _enforce_class_existence!(record)
26
+ begin
27
+ record.send "_mailer_class"
28
+ rescue
29
+ record.errors[:mailer_class] << "There is no #{record.send '_mailer_class_name'} class"
30
+ end
31
+ end
32
+ def _enforce_method_existence!(record)
33
+ begin
34
+ throw :bad unless record.send("_mailer_class").action_methods.include? record.send("_mailer_method")
35
+ rescue
36
+ record.errors[:mailer_class] << "The class #{record.send '_mailer_class_name'} doesn't respond to #{record.send '_mailer_method'}"
37
+ end
38
+ end
39
+ end
40
+ acts_as_paranoid
41
+ has_many :email_objects,
42
+ class_name: 'Apiv1::EmailObject',
43
+ foreign_key: 'email_id'
44
+
45
+ scope :awaiting_delivery,
46
+ -> { where "#{self.table_name}.status is null" }
47
+
48
+ scope :already_delivered,
49
+ -> { where "#{self.table_name}.status is not null" }
50
+
51
+ scope :meant_for_email,
52
+ -> (email) { where "#{self.table_name}.to" => email }
53
+
54
+ scope :last_delivered_email_for,
55
+ -> (email) { meant_for_email(email).already_delivered.latest_first.limit 1 }
56
+
57
+ scope :latest_first,
58
+ -> { order "#{self.table_name}.updated_at desc" }
59
+
60
+ scope :oldest_first,
61
+ -> { order "#{self.table_name}.updated_at asc" }
62
+
63
+ scope :the_null_scope,
64
+ -> { where("id != id").limit 0 }
65
+
66
+ validates :to,
67
+ presence: true
68
+
69
+ validates :mailer_class,
70
+ presence: true,
71
+ format: { with: /[A-Z][a-zA-Z0-9_:]+#[a-z0-9_]+/ }
72
+
73
+ validates_with MailerClassValidator
74
+
75
+ class << self
76
+ def oldest_undelivered_request
77
+ awaiting_delivery.oldest_first.limit(1).first
78
+ end
79
+ def belonging_to_the_user_with_oldest_undelivered
80
+ return the_null_scope if oldest_undelivered_request.blank?
81
+ awaiting_delivery.oldest_first.meant_for_email oldest_undelivered_request.to
82
+ end
83
+ end
84
+
85
+ def mailify
86
+ _mailer_class.send _mailer_method, *objectified_objects
87
+ end
88
+
89
+ def objectified_objects
90
+ email_objects.map(&:objectify)
91
+ end
92
+
93
+ def mark_as_delivered!
94
+ update status: :delivered
95
+ end
96
+
97
+ private
98
+ def _mailer_class
99
+ Object.const_get _mailer_class_name
100
+ end
101
+ def _mailer_class_name
102
+ mailer_class.to_s.split('#').first
103
+ end
104
+ def _mailer_method
105
+ mailer_class.to_s.split('#').last
106
+ end
107
+ end
@@ -0,0 +1,53 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: apiv1_offer_messages
4
+ #
5
+ # id :integer not null, primary key
6
+ # product_id :integer
7
+ # from_company :string(255)
8
+ # sender_email :string(255)
9
+ # subject_text :string(255)
10
+ # phone_number :string(255)
11
+ # contact_person :string(255)
12
+ # company_address :string(255)
13
+ # status :string(255)
14
+ # message :text
15
+ # created_at :datetime
16
+ # updated_at :datetime
17
+ # deleted_at :datetime
18
+ #
19
+
20
+ class Apiv1::OfferMessage < ActiveRecord::Base
21
+ acts_as_paranoid
22
+ belongs_to :product,
23
+ class_name: 'Apiv1::Product'
24
+
25
+ validates :price_terms,
26
+ :product,
27
+ presence: true
28
+
29
+ def to_ember_hash
30
+ attributes.merge price_terms: price_terms,
31
+ notes: notes
32
+ end
33
+
34
+ def to_public_ember_hash
35
+ to_ember_hash.symbolize_keys.permit(:id, :product_id, :created_at, :price_terms, :status)
36
+ end
37
+
38
+ def price_terms
39
+ read_attribute :subject_text
40
+ end
41
+
42
+ def price_terms=(value)
43
+ write_attribute(:subject_text, value)
44
+ end
45
+
46
+ def notes
47
+ read_attribute :message
48
+ end
49
+
50
+ def notes=(m)
51
+ write_attribute :message, m
52
+ end
53
+ end
@@ -0,0 +1,33 @@
1
+ class Apiv1::OfferPostboy
2
+ def initialize(offer)
3
+ @offer = offer
4
+ end
5
+
6
+ def request_work!
7
+ _email_objects && _email_request.valid? && _email_request.tap(&:save!)
8
+ end
9
+
10
+ private
11
+ def _email_request
12
+ @email_request ||= Apiv1::EmailRequest.new _request_params
13
+ end
14
+ def _email_objects
15
+ @email_objects ||= _email_request.email_objects.new Apiv1::EmailObject.paramify @offer
16
+ end
17
+ def _request_params
18
+ {
19
+ to: _mail_to_with_default,
20
+ cc: _mail.cc.join(","),
21
+ bcc: _mail.bcc.join(","),
22
+ from: _mail.from.first,
23
+ subject: _mail.subject,
24
+ mailer_class: "Apiv1::NotificationsMailer#new_offer"
25
+ }
26
+ end
27
+ def _mail_to_with_default
28
+ _mail.to.first || "missing_destination@mailinator.com"
29
+ end
30
+ def _mail
31
+ @mail ||= Apiv1::NotificationsMailer.new_offer @offer
32
+ end
33
+ end
@@ -11,9 +11,11 @@
11
11
  # pic_content_type :string(255)
12
12
  # pic_file_size :integer
13
13
  # pic_updated_at :datetime
14
+ # deleted_at :datetime
14
15
  #
15
16
 
16
17
  class Apiv1::Picture < ActiveRecord::Base
18
+ acts_as_paranoid
17
19
  belongs_to :depictable,
18
20
  polymorphic: true
19
21
 
@@ -14,9 +14,11 @@
14
14
  # created_at :datetime
15
15
  # updated_at :datetime
16
16
  # showcase_order :integer
17
+ # deleted_at :datetime
17
18
  #
18
19
 
19
20
  class Apiv1::Product < ActiveRecord::Base
21
+ acts_as_paranoid
20
22
  include ::Elasticsearch::Model
21
23
  include ::Elasticsearch::Model::Callbacks
22
24
  Fields = [
@@ -35,6 +37,9 @@ class Apiv1::Product < ActiveRecord::Base
35
37
  has_many :taxons,
36
38
  through: :taxon_relationships,
37
39
  class_name: 'Apiv1::Taxon'
40
+ has_many :offers,
41
+ -> { order "#{Apiv1::OfferMessage.table_name}.created_at desc" },
42
+ class_name: 'Apiv1::OfferMessage'
38
43
  has_many :pictures,
39
44
  class_name: 'Apiv1::Picture',
40
45
  as: :depictable,
@@ -43,15 +48,27 @@ class Apiv1::Product < ActiveRecord::Base
43
48
  class_name: 'Apiv1::Attachment',
44
49
  as: :attachable,
45
50
  dependent: :destroy
51
+ has_one :ownership,
52
+ class_name: 'Apiv1::Users::ProductRelationship',
53
+ foreign_key: 'product_id'
54
+ has_one :user,
55
+ through: :ownership,
56
+ class_name: 'Admin::User'
46
57
 
47
58
  scope :belonging_to_taxon,
48
- -> (taxon) { joins(:taxons).where("#{Apiv1::Taxon.table_name}.id = ?", taxon.id) }
59
+ -> (taxon) { union_of_taxon_ids taxon.id }
60
+
61
+ scope :union_of_taxon_ids,
62
+ -> (taxon_ids) { joins(:taxon_relationships).where("#{Apiv1::Listings::TaxonRelationship.table_name}.taxon_id" => taxon_ids) }
49
63
 
50
- scope :involving_taxon_ids,
51
- -> (taxon_ids) { joins(:taxons).where("#{Apiv1::Taxon.table_name}.id" => taxon_ids) }
64
+ scope :grouped_by_count,
65
+ -> { select("#{Apiv1::Product.table_name}.*, COUNT(#{Apiv1::Product.table_name}.id) as product_count").group("#{Apiv1::Product.table_name}.id") }
52
66
 
53
- scope :involving_taxons,
54
- -> (taxons) { involving_taxon_ids taxons.map(&:id) }
67
+ scope :intersection_of_taxon_ids,
68
+ -> (taxon_ids) { taxon_ids.count > 1 ? intersection_of_two_or_more_taxon_ids(taxon_ids) : union_of_taxon_ids(taxon_ids) }
69
+
70
+ scope :intersection_of_two_or_more_taxon_ids,
71
+ -> (taxon_ids) { union_of_taxon_ids(taxon_ids).grouped_by_count.having("product_count = ?", taxon_ids.count) }
55
72
 
56
73
  scope :order_by_created_at,
57
74
  -> { order "#{self.table_name}.created_at desc"}
@@ -78,9 +95,14 @@ class Apiv1::Product < ActiveRecord::Base
78
95
  def to_ember_hash
79
96
  attributes.merge pictures: pictures.map(&:id),
80
97
  taxons: taxons.map(&:id),
81
- attachments: attachments.map(&:id)
98
+ attachments: attachments.map(&:id),
99
+ offers: offers.map(&:id),
100
+ user_id: user.try(:id)
82
101
  end
83
102
 
103
+ def rough_summary
104
+ [material, sku, quality, place, amount].select(&:present?).join(" - ")
105
+ end
84
106
  private
85
107
  def _make_permalink
86
108
  self.permalink ||= Apiv1::Permalinkifier.permalinkify material