dugway 0.5.0

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 (125) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +3 -0
  3. data/Gemfile +2 -0
  4. data/README.md +151 -0
  5. data/Rakefile +12 -0
  6. data/bin/dugway +7 -0
  7. data/dugway.gemspec +43 -0
  8. data/lib/dugway.rb +66 -0
  9. data/lib/dugway/application.rb +147 -0
  10. data/lib/dugway/cart.rb +144 -0
  11. data/lib/dugway/cli.rb +22 -0
  12. data/lib/dugway/cli/build.rb +56 -0
  13. data/lib/dugway/cli/create.rb +71 -0
  14. data/lib/dugway/cli/server.rb +37 -0
  15. data/lib/dugway/cli/templates/Gemfile.tt +2 -0
  16. data/lib/dugway/cli/templates/config.tt +24 -0
  17. data/lib/dugway/cli/templates/gitignore.tt +9 -0
  18. data/lib/dugway/cli/templates/source/cart.html +40 -0
  19. data/lib/dugway/cli/templates/source/checkout.html +33 -0
  20. data/lib/dugway/cli/templates/source/contact.html +37 -0
  21. data/lib/dugway/cli/templates/source/home.html +25 -0
  22. data/lib/dugway/cli/templates/source/images/badge.png +0 -0
  23. data/lib/dugway/cli/templates/source/javascripts/cart.js.coffee +7 -0
  24. data/lib/dugway/cli/templates/source/javascripts/product.js.coffee +7 -0
  25. data/lib/dugway/cli/templates/source/layout.html +101 -0
  26. data/lib/dugway/cli/templates/source/maintenance.html +20 -0
  27. data/lib/dugway/cli/templates/source/product.html +65 -0
  28. data/lib/dugway/cli/templates/source/products.html +25 -0
  29. data/lib/dugway/cli/templates/source/screenshot.jpg +0 -0
  30. data/lib/dugway/cli/templates/source/scripts.js +19 -0
  31. data/lib/dugway/cli/templates/source/settings.json +71 -0
  32. data/lib/dugway/cli/templates/source/styles.css +27 -0
  33. data/lib/dugway/cli/templates/source/stylesheets/cart.css.sass +38 -0
  34. data/lib/dugway/cli/templates/source/stylesheets/layout.css.sass +103 -0
  35. data/lib/dugway/cli/templates/source/stylesheets/product.css.sass +19 -0
  36. data/lib/dugway/cli/templates/source/stylesheets/products.css.sass +20 -0
  37. data/lib/dugway/cli/templates/source/stylesheets/vendor/normalize.css +396 -0
  38. data/lib/dugway/cli/templates/source/success.html +5 -0
  39. data/lib/dugway/controller.rb +148 -0
  40. data/lib/dugway/data/locales/cs.yml +26 -0
  41. data/lib/dugway/data/locales/da.yml +26 -0
  42. data/lib/dugway/data/locales/en-AU.yml +26 -0
  43. data/lib/dugway/data/locales/en-GB.yml +26 -0
  44. data/lib/dugway/data/locales/en-PH.yml +27 -0
  45. data/lib/dugway/data/locales/en-US.yml +26 -0
  46. data/lib/dugway/data/locales/es-MX.yml +26 -0
  47. data/lib/dugway/data/locales/eu.yml +26 -0
  48. data/lib/dugway/data/locales/gsw-CH.yml +26 -0
  49. data/lib/dugway/data/locales/hu.yml +26 -0
  50. data/lib/dugway/data/locales/il.yml +26 -0
  51. data/lib/dugway/data/locales/ja.yml +30 -0
  52. data/lib/dugway/data/locales/ms-MY.yml +26 -0
  53. data/lib/dugway/data/locales/nb.yml +22 -0
  54. data/lib/dugway/data/locales/pl.yml +27 -0
  55. data/lib/dugway/data/locales/pt-BR.yml +26 -0
  56. data/lib/dugway/data/locales/sv-SE.yml +26 -0
  57. data/lib/dugway/data/locales/th.yml +26 -0
  58. data/lib/dugway/data/locales/tr.yml +29 -0
  59. data/lib/dugway/data/locales/zh-TW.yml +30 -0
  60. data/lib/dugway/data/theme_fonts.yml +151 -0
  61. data/lib/dugway/extensions/time.rb +15 -0
  62. data/lib/dugway/liquid/drops/account_drop.rb +17 -0
  63. data/lib/dugway/liquid/drops/artist_drop.rb +9 -0
  64. data/lib/dugway/liquid/drops/artists_drop.rb +13 -0
  65. data/lib/dugway/liquid/drops/base_drop.rb +59 -0
  66. data/lib/dugway/liquid/drops/cart_drop.rb +13 -0
  67. data/lib/dugway/liquid/drops/cart_item_drop.rb +21 -0
  68. data/lib/dugway/liquid/drops/categories_drop.rb +13 -0
  69. data/lib/dugway/liquid/drops/category_drop.rb +9 -0
  70. data/lib/dugway/liquid/drops/contact_drop.rb +29 -0
  71. data/lib/dugway/liquid/drops/country_drop.rb +6 -0
  72. data/lib/dugway/liquid/drops/currency_drop.rb +6 -0
  73. data/lib/dugway/liquid/drops/image_drop.rb +6 -0
  74. data/lib/dugway/liquid/drops/page_drop.rb +13 -0
  75. data/lib/dugway/liquid/drops/pages_drop.rb +9 -0
  76. data/lib/dugway/liquid/drops/product_drop.rb +101 -0
  77. data/lib/dugway/liquid/drops/product_option_drop.rb +27 -0
  78. data/lib/dugway/liquid/drops/products_drop.rb +75 -0
  79. data/lib/dugway/liquid/drops/shipping_option_drop.rb +13 -0
  80. data/lib/dugway/liquid/drops/theme_drop.rb +23 -0
  81. data/lib/dugway/liquid/filters/comparison_filters.rb +48 -0
  82. data/lib/dugway/liquid/filters/core_filters.rb +138 -0
  83. data/lib/dugway/liquid/filters/default_pagination.rb +35 -0
  84. data/lib/dugway/liquid/filters/font_filters.rb +9 -0
  85. data/lib/dugway/liquid/filters/url_filters.rb +66 -0
  86. data/lib/dugway/liquid/filters/util_filters.rb +119 -0
  87. data/lib/dugway/liquid/tags/checkout_form.rb +9 -0
  88. data/lib/dugway/liquid/tags/get.rb +58 -0
  89. data/lib/dugway/liquid/tags/paginate.rb +129 -0
  90. data/lib/dugway/liquifier.rb +122 -0
  91. data/lib/dugway/logger.rb +16 -0
  92. data/lib/dugway/request.rb +36 -0
  93. data/lib/dugway/store.rb +166 -0
  94. data/lib/dugway/template.rb +44 -0
  95. data/lib/dugway/theme.rb +145 -0
  96. data/lib/dugway/theme_font.rb +68 -0
  97. data/lib/dugway/version.rb +3 -0
  98. data/spec/fixtures/store/page/about-us.js +1 -0
  99. data/spec/fixtures/store/products.js +1 -0
  100. data/spec/fixtures/store/store.js +1 -0
  101. data/spec/fixtures/theme/cart.html +33 -0
  102. data/spec/fixtures/theme/checkout.html +31 -0
  103. data/spec/fixtures/theme/contact.html +37 -0
  104. data/spec/fixtures/theme/home.html +38 -0
  105. data/spec/fixtures/theme/images/bc_badge.png +0 -0
  106. data/spec/fixtures/theme/javascripts/one.js +3 -0
  107. data/spec/fixtures/theme/javascripts/two.js.coffee +2 -0
  108. data/spec/fixtures/theme/layout.html +77 -0
  109. data/spec/fixtures/theme/maintenance.html +17 -0
  110. data/spec/fixtures/theme/product.html +73 -0
  111. data/spec/fixtures/theme/products.html +41 -0
  112. data/spec/fixtures/theme/screenshot.jpg +0 -0
  113. data/spec/fixtures/theme/scripts.js +2 -0
  114. data/spec/fixtures/theme/settings.json +55 -0
  115. data/spec/fixtures/theme/styles.css +4 -0
  116. data/spec/fixtures/theme/stylesheets/one.css +3 -0
  117. data/spec/fixtures/theme/stylesheets/two.css.sass +4 -0
  118. data/spec/fixtures/theme/success.html +5 -0
  119. data/spec/spec_helper.rb +24 -0
  120. data/spec/units/dugway/request_spec.rb +206 -0
  121. data/spec/units/dugway/store_spec.rb +194 -0
  122. data/spec/units/dugway/template_spec.rb +145 -0
  123. data/spec/units/dugway/theme_font_spec.rb +136 -0
  124. data/spec/units/dugway_spec.rb +9 -0
  125. metadata +549 -0
@@ -0,0 +1,13 @@
1
+ module Dugway
2
+ module Drops
3
+ class ArtistsDrop < BaseDrop
4
+ def all
5
+ @all ||= source
6
+ end
7
+
8
+ def active
9
+ @active ||= source.reject { |a| store.artist_products(a['permalink']).empty? }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,59 @@
1
+ module Dugway
2
+ module Drops
3
+ class BaseDrop < Liquid::Drop
4
+ attr_reader :source
5
+ attr_reader :request
6
+ attr_reader :params
7
+
8
+ def initialize(source=nil)
9
+ @source = source
10
+ end
11
+
12
+ def context=(current_context)
13
+ @request = current_context.registers[:request]
14
+ @params = current_context.registers[:params]
15
+ super
16
+ end
17
+
18
+ def store
19
+ Dugway.store
20
+ end
21
+
22
+ def theme
23
+ Dugway.theme
24
+ end
25
+
26
+ def cart
27
+ Dugway.cart
28
+ end
29
+
30
+ def before_method(method_or_key)
31
+ if respond_to?(method_or_key)
32
+ # don't do anything, just let it default here
33
+ elsif source.respond_to?(method_or_key)
34
+ return source.send(method_or_key)
35
+ elsif source.respond_to?('has_key?') && source.has_key?(method_or_key)
36
+ return source[method_or_key]
37
+ elsif source.is_a?(Array) && source.first.has_key?('permalink')
38
+ for item in source
39
+ return item if item['permalink'] == method_or_key.to_s
40
+ end
41
+ end
42
+
43
+ nil
44
+ end
45
+
46
+ def method_missing(method, *args, &block)
47
+ before_method(method.to_s)
48
+ end
49
+
50
+ def errors
51
+ @context['errors']
52
+ end
53
+
54
+ def error(msg)
55
+ errors << msg
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,13 @@
1
+ module Dugway
2
+ module Drops
3
+ class CartDrop < BaseDrop
4
+ def items
5
+ @items ||= source.items.map { |item| CartItemDrop.new(item) }
6
+ end
7
+
8
+ def price
9
+ nil # price is deprecated in favor of subtotal
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module Dugway
2
+ module Drops
3
+ class CartItemDrop < BaseDrop
4
+ def price
5
+ unit_price * quantity
6
+ end
7
+
8
+ def product
9
+ ProductDrop.new(source['product'])
10
+ end
11
+
12
+ def option
13
+ ProductOptionDrop.new(source['option'])
14
+ end
15
+
16
+ def shipping
17
+ 0.0
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module Dugway
2
+ module Drops
3
+ class CategoriesDrop < BaseDrop
4
+ def all
5
+ @all ||= source
6
+ end
7
+
8
+ def active
9
+ @active ||= source.reject { |c| store.category_products(c['permalink']).empty? }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Dugway
2
+ module Drops
3
+ class CategoryDrop < BaseDrop
4
+ def products
5
+ store.category_products(permalink).map { |p| ProductDrop.new(p) }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module Dugway
2
+ module Drops
3
+ class ContactDrop < BaseDrop
4
+ def name
5
+ params[:name] ||= ''
6
+ end
7
+
8
+ def email
9
+ params[:email] ||= ''
10
+ end
11
+
12
+ def subject
13
+ params[:subject] ||= ''
14
+ end
15
+
16
+ def message
17
+ params[:message] ||= ''
18
+ end
19
+
20
+ def captcha
21
+ %{<img id="captcha_image" src="https://s3.amazonaws.com/bigcartel/captcha/28e3d1288cbc70c0cd1a2d10845f8e11e1a90d14.png">}
22
+ end
23
+
24
+ def sent
25
+ request.path == '/contact' && request.post? && errors.blank?
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,6 @@
1
+ module Dugway
2
+ module Drops
3
+ class CountryDrop < BaseDrop
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Dugway
2
+ module Drops
3
+ class CurrencyDrop < BaseDrop
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Dugway
2
+ module Drops
3
+ class ImageDrop < BaseDrop
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,13 @@
1
+ module Dugway
2
+ module Drops
3
+ class PageDrop < BaseDrop
4
+ def meta_description
5
+ 'Example meta description'
6
+ end
7
+
8
+ def meta_keywords
9
+ 'example, key, words'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Dugway
2
+ module Drops
3
+ class PagesDrop < BaseDrop
4
+ def all
5
+ @all ||= source.select { |page| page['category'] == 'custom' }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,101 @@
1
+ module Dugway
2
+ module Drops
3
+ class ProductDrop < BaseDrop
4
+ def price
5
+ nil # price is deprecated in favor of default_price
6
+ end
7
+
8
+ def min_price
9
+ @min_price ||= price_min_max.first
10
+ end
11
+
12
+ def max_price
13
+ @max_price ||= price_min_max.last
14
+ end
15
+
16
+ def variable_pricing
17
+ @variable_pricing ||= min_price != max_price
18
+ end
19
+
20
+ def has_default_option
21
+ @has_default_option ||= options.size == 1 && option.default
22
+ end
23
+
24
+ def option
25
+ @option ||= options.blank? ? nil : options.first
26
+ end
27
+
28
+ def options
29
+ @options ||= source['options'].each_with_index.map { |o,i| ProductOptionDrop.new(o.update('position' => i+1, 'product' => self)) }
30
+ end
31
+
32
+ def options_in_stock
33
+ @options_in_stock ||= options
34
+ end
35
+
36
+ def shipping
37
+ @shipping ||= source['shipping'].map { |o| ShippingOptionDrop.new(o.update('product' => self)) }
38
+ end
39
+
40
+ def image
41
+ @image ||= images.blank? ? nil : images.first
42
+ end
43
+
44
+ def images
45
+ @images ||= source['images'].present? ? source['images'].map { |image| ImageDrop.new(image) } : []
46
+ end
47
+
48
+ def image_count
49
+ @image_count ||= images.size
50
+ end
51
+
52
+ def previous_product
53
+ @previous_product ||= begin
54
+ if previous_product = store.previous_product(permalink)
55
+ ProductDrop.new(previous_product)
56
+ else
57
+ nil
58
+ end
59
+ end
60
+ end
61
+
62
+ def next_product
63
+ @next_product ||= begin
64
+ if next_product = store.next_product(permalink)
65
+ ProductDrop.new(next_product)
66
+ else
67
+ nil
68
+ end
69
+ end
70
+ end
71
+
72
+ def edit_url
73
+ "http://bigcartel.com"
74
+ end
75
+
76
+ def categories
77
+ @categories ||= CategoriesDrop.new(source['categories'].map { |c| CategoryDrop.new(c) }) rescue []
78
+ end
79
+
80
+ def artists
81
+ @artists ||= ArtistsDrop.new(source['artists'].map { |a| ArtistDrop.new(a) }) rescue []
82
+ end
83
+
84
+ def css_class
85
+ @css_class ||= begin
86
+ c = 'product'
87
+ c += ' sold' if status == 'sold-out'
88
+ c += ' soon' if status == 'coming-soon'
89
+ c += ' sale' if on_sale
90
+ c
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def price_min_max
97
+ @price_min_max ||= options.collect(&:price).uniq.minmax
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,27 @@
1
+ module Dugway
2
+ module Drops
3
+ class ProductOptionDrop < BaseDrop
4
+ def default
5
+ name.downcase == 'default'
6
+ end
7
+
8
+ def has_custom_price
9
+ price != product.default_price
10
+ end
11
+
12
+ # Make up inventory numbers since we obviously don't want that from the API
13
+
14
+ def quantity
15
+ @quantity ||= sold_out ? 0 : rand(100)
16
+ end
17
+
18
+ def sold
19
+ @sold ||= rand(100)
20
+ end
21
+
22
+ def inventory
23
+ ((quantity.to_f / (quantity + sold).to_f) * 100).round
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,75 @@
1
+ require 'will_paginate/array'
2
+
3
+ module Dugway
4
+ module Drops
5
+ class ProductsDrop < BaseDrop
6
+ def all
7
+ sort_and_paginate source
8
+ end
9
+
10
+ def current
11
+ sort_and_paginate begin
12
+ if artist.present?
13
+ dropify store.artist_products(artist)
14
+ elsif category.present?
15
+ dropify store.category_products(category)
16
+ elsif search_terms.present?
17
+ dropify store.search_products(search_terms)
18
+ else
19
+ source
20
+ end
21
+ end
22
+ end
23
+
24
+ def on_sale
25
+ sort_and_paginate source.select { |p| p['on_sale'] }
26
+ end
27
+
28
+ private
29
+
30
+ def order
31
+ begin case @context['internal']['order']
32
+ when 'newest', 'date'
33
+ 'date'
34
+ # We don't pass these in the API, so fake it
35
+ when 'sales', 'sells', 'views'
36
+ 'shuffle'
37
+ else
38
+ 'position'
39
+ end
40
+ end
41
+ end
42
+
43
+ def sort_and_paginate(array)
44
+ if order == 'shuffle'
45
+ array.shuffle!
46
+ elsif order == 'date'
47
+ array.sort! { |a,b| b['id'] <=> a['id'] }
48
+ else
49
+ array.sort_by! { |p| p[order] }
50
+ end
51
+
52
+ array.paginate({
53
+ :page => (@context.registers[:params][:page] || 1).to_i,
54
+ :per_page => @context['internal']['per_page']
55
+ })
56
+ end
57
+
58
+ def artist
59
+ @context.registers[:artist]['permalink'] rescue nil
60
+ end
61
+
62
+ def category
63
+ @context.registers[:category]['permalink'] rescue nil
64
+ end
65
+
66
+ def search_terms
67
+ params[:search]
68
+ end
69
+
70
+ def dropify(products)
71
+ products.map { |p| ProductDrop.new(p) }
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,13 @@
1
+ module Dugway
2
+ module Drops
3
+ class ShippingOptionDrop < BaseDrop
4
+ def country
5
+ @country ||= CountryDrop.new(source['country'])
6
+ end
7
+
8
+ def strict
9
+ @strict ||= product.shipping.size > 1 || product.shipping.first.country.name.present?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Dugway
2
+ module Drops
3
+ class ThemeDrop < BaseDrop
4
+ def before_method(method_or_key)
5
+ if source.respond_to?('has_key?') && source.has_key?(method_or_key) && images.find { |image| image['variable'] == method_or_key.to_s }
6
+ return ImageDrop.new(source[method_or_key].stringify_keys)
7
+ end
8
+
9
+ super
10
+ end
11
+
12
+ private
13
+
14
+ def images
15
+ @images ||= settings.has_key?('images') ? settings['images'] : []
16
+ end
17
+
18
+ def settings
19
+ @settings ||= @context.registers[:settings]
20
+ end
21
+ end
22
+ end
23
+ end