c80_yax 0.1.0.1

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/CODE_OF_CONDUCT.md +49 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +41 -0
  7. data/Rakefile +2 -0
  8. data/app/admin/c80_yax/items.rb +128 -0
  9. data/app/admin/c80_yax/prop_names.rb +90 -0
  10. data/app/admin/c80_yax/strsubcats.rb +128 -0
  11. data/app/admin/c80_yax/uoms.rb +35 -0
  12. data/app/admin/c80_yax/x_c80_yax.rb +5 -0
  13. data/app/assets/javascripts/backend/items.js +416 -0
  14. data/app/assets/javascripts/c80_yax.js.coffee +6 -0
  15. data/app/assets/javascripts/lib_backend/collapsable-groups.js +22 -0
  16. data/app/assets/javascripts/lib_backend/jalert.js +16 -0
  17. data/app/assets/stylesheets/c80_yax/backend/active_admin/admin_items.scss +152 -0
  18. data/app/assets/stylesheets/c80_yax/backend/collapsed.scss +56 -0
  19. data/app/assets/stylesheets/c80_yax/backend/jquery-my-dialog.scss +22 -0
  20. data/app/assets/stylesheets/c80_yax/lib/loading.scss +13 -0
  21. data/app/assets/stylesheets/c80_yax/mixins.scss +89 -0
  22. data/app/assets/stylesheets/c80_yax_backend.scss +7 -0
  23. data/app/controllers/c80_yax/admin_data_controller.rb +22 -0
  24. data/app/controllers/c80_yax/app_controller.rb +6 -0
  25. data/app/helpers/c80_yax/data_helper.rb +84 -0
  26. data/app/models/c80_yax/iphoto.rb +7 -0
  27. data/app/models/c80_yax/item.rb +169 -0
  28. data/app/models/c80_yax/item_prop.rb +83 -0
  29. data/app/models/c80_yax/prop.rb +5 -0
  30. data/app/models/c80_yax/prop_name.rb +32 -0
  31. data/app/models/c80_yax/strsubcat.rb +71 -0
  32. data/app/models/c80_yax/uom.rb +5 -0
  33. data/app/uploaders/c80_yax/iphoto_uploader.rb +41 -0
  34. data/bin/console +14 -0
  35. data/bin/setup +8 -0
  36. data/c80_yax.gemspec +26 -0
  37. data/config/locales/ru.yml +52 -0
  38. data/config/routes.rb +3 -0
  39. data/db/migrate/20161030061203_create_c80_yax_strsubcats.rb +12 -0
  40. data/db/migrate/20161030225354_create_c80_yax_items.rb +20 -0
  41. data/db/migrate/20161107202727_create_c80_yax_iphotos.rb +11 -0
  42. data/db/migrate/20161107210000_create_c80_yax_props.rb +15 -0
  43. data/db/migrate/20161108095505_create_c80_yax_uoms.rb +11 -0
  44. data/db/migrate/20161108095950_create_c80_yax_prop_names.rb +13 -0
  45. data/db/migrate/20161108100707_create_c80_yax_join_table_prop_names_strsubcats.rb +12 -0
  46. data/db/migrate/20161108102828_create_c80_yax_item_props.rb +13 -0
  47. data/db/seeds/c80_yax_01_fill_props.rb +11 -0
  48. data/lib/c80_yax/engine.rb +23 -0
  49. data/lib/c80_yax/version.rb +3 -0
  50. data/lib/c80_yax.rb +8 -0
  51. metadata +135 -0
@@ -0,0 +1,13 @@
1
+ #loading {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ bottom: 0;
6
+ right: 0;
7
+ text-align: center;
8
+ font-size: 20px;
9
+ background: rgba(255,255,255,0.7) url(data:image/gif;base64,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) 50% 50% no-repeat;
10
+ z-index: 9999;
11
+ border-radius: 5px;
12
+ line-height: 3;
13
+ }
@@ -0,0 +1,89 @@
1
+ // тень
2
+ @mixin shdw($val) {
3
+ -webkit-box-shadow: 0 0 7px 0 rgba(0, 0, 0, $val);
4
+ -moz-box-shadow: 0 0 7px 0 rgba(0, 0, 0, $val);
5
+ box-shadow: 0 0 7px 0 rgba(0, 0, 0, $val);
6
+ /* For IE 8 */
7
+ -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=$val, Direction=135, Color='#000000')";
8
+ /* For IE 5.5 - 7 */
9
+ filter: progid:DXImageTransform.Microsoft.Shadow(Strength=$val, Direction=135, Color='#000000');
10
+ }
11
+
12
+ @mixin clearfx() {
13
+ &:before,
14
+ &:after {
15
+ content: " ";
16
+ display: table;
17
+ }
18
+ &:after {
19
+ clear: both;
20
+ }
21
+ }
22
+
23
+ // ссылка без подчёркивания и обводки
24
+ //@mixin nounder {
25
+ // text-decoration: none !important;
26
+ // border: none !important;
27
+ // outline: none !important;
28
+ //
29
+ // &:hover, &:active, &:focus {
30
+ // text-decoration: none !important;
31
+ // border: none !important;
32
+ // outline: none !important;
33
+ // }
34
+ //}
35
+
36
+ // анимированный переход цвета
37
+ //@mixin transition_color($sec) {
38
+ // -webkit-transition: color $sec ease-out;
39
+ // -moz-transition: color $sec ease-out;
40
+ // -ms-transition: color $sec ease-out;
41
+ // -o-transition: color $sec ease-out;
42
+ // transition: color $sec ease-out;
43
+ //}
44
+
45
+ // анимированный переход цвета границы
46
+ //@mixin transition_border_color($sec) {
47
+ // -webkit-transition: border-color $sec ease-in;
48
+ // -moz-transition: border-color $sec ease-in;
49
+ // -ms-transition: border-color $sec ease-in;
50
+ // -o-transition: border-color $sec ease-in;
51
+ // transition: border-color $sec ease-in;
52
+ //}
53
+
54
+ //@mixin transform_prop($prop_name,$sec) {
55
+ // -webkit-transition: $prop_name $sec ease-out;
56
+ // -moz-transition: $prop_name $sec ease-out;
57
+ // -ms-transition: $prop_name $sec ease-out;
58
+ // -o-transition: $prop_name $sec ease-out;
59
+ // transition: $prop_name $sec ease-out;
60
+ //}
61
+
62
+ // градиент сверху-вниз
63
+ //@mixin bg_tobottom_gradient($from,$to) {
64
+ // background-image: linear-gradient(to bottom, $from, $to) !important;
65
+ // background: -webkit-gradient(linear, left top, left bottom, from($from), to($to));
66
+ // filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='$from', endColorstr='$to', GradientType=0);
67
+ //}
68
+
69
+ //@mixin transform_scale($scale,$time) {
70
+ // -webkit-transform: scale($scale);
71
+ // -moz-transform: scale($scale);
72
+ // -ms-transform: scale($scale);
73
+ // -o-transform: scale($scale);
74
+ // transform: scale($scale);
75
+ //
76
+ // -webkit-transition: -webkit-transform $time ease-out;
77
+ // -moz-transition: -moz-transform $time ease-out;
78
+ // -ms-transition: -ms-transform $time ease-out;
79
+ // -o-transition: -o-transform $time ease-out;
80
+ // transition: transform $time ease-out;
81
+ //}
82
+
83
+ //@mixin opacity($val) {
84
+ // -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=$val)";
85
+ // filter: alpha(opacity=$val);
86
+ // -moz-opacity:$val;
87
+ // -khtml-opacity: $val;
88
+ // opacity: $val;
89
+ //}
@@ -0,0 +1,7 @@
1
+ //@import "frontend/site/mixins";
2
+ //@import "active_admin/mixins";
3
+
4
+ @import "font-awesome";
5
+ @import 'c80_yax/mixins';
6
+ @import 'c80_yax/lib/**/*';
7
+ @import 'c80_yax/backend/**/*';
@@ -0,0 +1,22 @@
1
+ module C80Yax
2
+ class AdminDataController < ApplicationController
3
+
4
+ # http://stackoverflow.com/questions/5130150/rails-how-to-use-a-helper-inside-a-controller
5
+ include DataHelper
6
+
7
+ def get_strsubcat_propnames
8
+ Rails.logger.debug '[TRACE] <admin_data_controller.get_strsubcat_propnames> Получить список имён свойств данной подкатегории.'
9
+ Rails.logger.debug "[TRACE] <admin_data_controller.get_strsubcat_propnames> request: #{request.request_parameters}"
10
+
11
+ strsubcat_id = request.params[:strsubcat_id]
12
+
13
+ obj = stdh_get_strsubcat_propnames(strsubcat_id)
14
+
15
+ respond_to do |format|
16
+ format.js { render json: obj, status: :ok }
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ module C80Yax
2
+ class AppController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ before_filter :authenticate_admin_user!
5
+ end
6
+ end
@@ -0,0 +1,84 @@
1
+ module C80Yax
2
+
3
+ # хелпер, помогающий с выборкой данных для товаров из категории "строительные материалы"
4
+ module DataHelper
5
+
6
+ include ActiveRecord
7
+
8
+ # собрать все значения свойства prop_name_id из подкатегории sub_cat
9
+ def stdh_collect_all_values(prop_name_id, sub_cat_id)
10
+
11
+ # +---------------------+
12
+ # | prop_23 |
13
+ # +---------------------+
14
+ # | 215 x 102 x 65 мм |
15
+ # | 215 x 48 x 65 мм |
16
+ # | 250 x 85 x 65 мм |
17
+ # | 254 x 95 x 65 мм |
18
+ # | 290 x 90 x 52 мм |
19
+ # | 528 x 108 x 37 мм |
20
+ # +---------------------+
21
+
22
+ table_name = "strcat_#{sub_cat_id}_items"
23
+ column_name = "prop_#{prop_name_id}"
24
+
25
+ sql = "
26
+ SELECT #{table_name}.#{column_name}
27
+ FROM #{table_name}
28
+ GROUP by #{table_name}.#{column_name};
29
+ "
30
+
31
+ ActiveRecord::Base.connection.execute(sql) # => records array
32
+
33
+ end
34
+
35
+ # выдать все характеристики, присущие данной категории, вместе с единицами измерений
36
+ def stdh_get_strsubcat_propnames(strsubcat_id)
37
+
38
+ # +----+----------------+----------------------------+-----------+
39
+ # | id | title | is_excluded_from_filtering | uom_title |
40
+ # +----+----------------+----------------------------+-----------+
41
+ # | 2 | Цена | 0 | руб. |
42
+ # | 3 | Объём | 0 | мл |
43
+ # | 4 | Вкус | 0 | NULL |
44
+ # | 5 | VG/PG | 0 | NULL |
45
+ # | 6 | Никотин | 0 | мг |
46
+ # | 7 | Страна | 0 | NULL |
47
+ # | 19 | Бренд | 0 | NULL |
48
+ # +----+----------------+----------------------------+-----------+
49
+
50
+ # {"id"=>2, "title"=>"Цена", "is_excluded_from_filtering"=>0, "uom_title"=>"руб."},
51
+ # {"id"=>3, "title"=>"Объём", "is_excluded_from_filtering"=>0, "uom_title"=>"мл"},
52
+ # {"id"=>4, "title"=>"Вкус", "is_excluded_from_filtering"=>0, "uom_title"=>nil},
53
+ # {"id"=>5, "title"=>"VG/PG", "is_excluded_from_filtering"=>0, "uom_title"=>nil},
54
+ # {"id"=>6, "title"=>"Никотин", "is_excluded_from_filtering"=>0, "uom_title"=>"мг"},
55
+ # {"id"=>7, "title"=>"Страна", "is_excluded_from_filtering"=>0, "uom_title"=>nil},
56
+ # {"id"=>19, "title"=>"Бренд", "is_excluded_from_filtering"=>0, "uom_title"=>nil}
57
+
58
+ Rails.logger.debug '[TRACE] <stdh_get_strsubcat_propnames> BEGIN'
59
+ sql = "
60
+ SELECT
61
+ `c80_yax_prop_names`.`id`,
62
+ `c80_yax_prop_names`.`title`,
63
+ `c80_yax_prop_names`.`is_excluded_from_filtering`,
64
+ `c80_yax_uoms`.`title` as `uom_title`
65
+ FROM `c80_yax_prop_names`
66
+ INNER JOIN `c80_yax_prop_names_strsubcats` ON `c80_yax_prop_names`.`id` = `c80_yax_prop_names_strsubcats`.`prop_name_id`
67
+ LEFT OUTER JOIN `c80_yax_uoms` ON `c80_yax_uoms`.`id` = `c80_yax_prop_names`.`uom_id`
68
+ WHERE `c80_yax_prop_names_strsubcats`.`strsubcat_id` = #{strsubcat_id};
69
+ "
70
+
71
+ result = []
72
+ rows = Base.connection.execute(sql)
73
+ rows.each(:as => :hash) do |row|
74
+ result << row
75
+ end
76
+
77
+ Rails.logger.debug '[TRACE] <stdh_get_strsubcat_propnames> END'
78
+ result
79
+
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,7 @@
1
+ module C80Yax
2
+ class Iphoto < ActiveRecord::Base
3
+ belongs_to :item
4
+ mount_uploader :image, IphotoUploader
5
+ end
6
+
7
+ end
@@ -0,0 +1,169 @@
1
+ require 'babosa'
2
+
3
+ class C80Yax::Item < ActiveRecord::Base
4
+
5
+ validates :title,
6
+ presence: true,
7
+ uniqueness: true,
8
+ length: { in: 2..150 }
9
+
10
+ validates :strsubcat,
11
+ presence: true
12
+
13
+ # has_and_belongs_to_many :vendors
14
+ belongs_to :strsubcat
15
+
16
+ has_many :iphotos, :dependent => :destroy
17
+ accepts_nested_attributes_for :iphotos,
18
+ :reject_if => lambda { |attributes|
19
+ !attributes.present?
20
+ },
21
+ :allow_destroy => true
22
+
23
+ has_many :item_props, :dependent => :destroy
24
+ accepts_nested_attributes_for :item_props,
25
+ :reject_if => lambda { |attributes|
26
+ # puts "attributes:: #{attributes}"
27
+ # attributes:: {"value"=>"", "prop_name_id"=>""}
28
+ !attributes.present? || \
29
+ !attributes[:value].present? || \
30
+ !attributes[:prop_name_id].present?
31
+ },
32
+ :allow_destroy => true
33
+
34
+ has_many :related_childs, :class_name => 'C80Yax::Item', :foreign_key => 'related_parent_id'
35
+ belongs_to :related_parent, :class_name => 'C80Yax::Item'
36
+
37
+ extend FriendlyId
38
+ friendly_id :slug_candidates, :use => :slugged
39
+
40
+ def slug_candidates
41
+ [:title] + Array.new(6) {|index| [:title, index+2]}
42
+ end
43
+
44
+ def normalize_friendly_id(input)
45
+ input.to_s.to_slug.normalize(transliterations: :russian).to_s
46
+ end
47
+
48
+
49
+
50
+
51
+ # выдать все товары, которые надо показать на главной странице "строительные материалы"
52
+ # с помощью методов ActiveRecord
53
+ def self.all_on_main_ar
54
+
55
+ # * В первую очередь отображаются товары с пометкой "Выводится на главной"
56
+ items_is_main = self.where('is_main = ?',1)
57
+
58
+ # * Далее во вторую очередь выводятся товары, у которых есть обе отметки "hit" и "sale"
59
+ items_is_hit_and_is_sale = self.where('is_hit = ? AND is_sale = ?',1,1)
60
+
61
+ # * Далее выводятся товары, которые отмечены только "hit"
62
+ items_is_hit = self.where('is_hit = ? AND is_sale = ?',1,0)
63
+
64
+ # * Потом товары, которые отмечены только sale
65
+ items_is_sale = self.where('is_sale = ? AND is_hit = ?',1,0)
66
+
67
+ # * А далее все товары, которые по цене (по возрастанию) - от наиболее дешевого товара к наиболее дорогому.
68
+ items_other = self.where.not('is_main = ?',1)
69
+ .where.not('is_hit = ? AND is_sale = ?',1,1)
70
+ .where.not('is_hit = ?',1)
71
+ .where.not('is_sale = ?',1)
72
+
73
+ items_is_main.union(items_is_hit_and_is_sale)
74
+ .union(items_is_hit)
75
+ .union(items_is_sale)
76
+ .union(items_other)
77
+
78
+ end
79
+
80
+ # выдать все товары, которые надо показать на любой странице подкатегории (БЕЗ ФИЛЬТРОВ)
81
+ # с помощью методов ActiveRecord и c80_active_record_union
82
+ def self.all_on_subcat_ar(strsubcat_id)
83
+
84
+ # Сначала выводятся только те, товары, у которых есть обе отметки "hit" и "sale"
85
+ items_is_hit_and_is_sale = self.where('is_hit = ? AND is_sale = ?',1,1)
86
+
87
+ # * Далее выводятся товары, которые отмечены только "hit"
88
+ items_is_hit = self.where('is_hit = ? AND is_sale = ?',1,0)
89
+
90
+ # * Потом товары, которые отмечены только sale
91
+ items_is_sale = self.where('is_sale = ? AND is_hit = ?',1,0)
92
+
93
+ # * А далее все товары, которые по цене (по возрастанию) - от наиболее дешевого товара к наиболее дорогому.
94
+ items_other = self.where.not('is_main = ?',1)
95
+ .where.not('is_hit = ? AND is_sale = ?',1,1)
96
+ .where.not('is_hit = ?',1)
97
+ .where.not('is_sale = ?',1)
98
+
99
+ items_is_hit_and_is_sale.union(items_is_hit)
100
+ .union(items_is_sale)
101
+ .union(items_other)
102
+ .where(:strsubcat_id => strsubcat_id)
103
+
104
+ end
105
+
106
+ before_save :before_save_format_desc
107
+
108
+ # сформировать и вернуть список "похожих товаров"
109
+ def self.get_related_items_list(item_id)
110
+ # Rails.logger.debug("<Item.get_related_items_list> [123] item_id = #{item_id}")
111
+ # Он формируется по следующим правилам:
112
+ # 1. Попадают товары из этой же подкатегории (обязательно, если в подкатегории один товар, то блока просто нет)
113
+ # 2. Тот же бренд
114
+ # 3. Наиболее близкая цена
115
+ # 4. Если есть параметр "Размер", то наиболее близкие по размеру.
116
+ # Также должа быть возможность ручного управления блоком "Похожие товары",
117
+ # где администратор просто указывает ссылки на похожие товары.
118
+
119
+ # результат
120
+ all_related_items = []
121
+
122
+ # кол-во элементов в результате не более этого значения
123
+ n = 4
124
+
125
+ item = self.find(item_id)
126
+
127
+ if item.related_childs.count > 0
128
+ item.related_childs.each do |itm|
129
+ if all_related_items.count < n
130
+ all_related_items << itm
131
+ else
132
+ break
133
+ end
134
+ end
135
+ end
136
+
137
+ all_related_items
138
+
139
+ end
140
+
141
+ private
142
+
143
+ def before_save_format_desc
144
+ if self.full_desc.present?
145
+ v = self.full_desc
146
+
147
+ # удаляем inline css стили
148
+ v = v.gsub(/ style=["'][^"']+["']/, '')
149
+
150
+ # удаляем тэг <font>
151
+ v = v.gsub(/<font[^>]*>/, '')
152
+ v = v.gsub('</font>', '')
153
+
154
+ # <strong>...</strong> заменяем на <span class='bold'>...</span>
155
+ v = v.gsub(/<(em|strong)>/, '<span class="bold">')
156
+ v = v.gsub(/<\/(em|strong)>/, '</span>')
157
+
158
+ # заголовки h1, h2, h3 заменяем на h4
159
+ v = v.gsub(/<(h1|h2|h3)>/, '<h4>')
160
+ v = v.gsub(/<\/(h1|h2|h3)>/, '<h4>')
161
+
162
+ v = v.gsub('&nbsp;', ' ')
163
+
164
+ self.full_desc = v
165
+ end
166
+
167
+ end
168
+
169
+ end
@@ -0,0 +1,83 @@
1
+ module C80Yax
2
+ class ItemProp < ActiveRecord::Base
3
+ belongs_to :item
4
+ belongs_to :prop_name
5
+
6
+ before_save :before_save_format_value
7
+
8
+ private
9
+
10
+ def self.capz
11
+ [24, # страна
12
+ 36, # бренд
13
+ 46] # бренд (старый)
14
+ end
15
+
16
+ def self.uppz
17
+ [26, # коэф-т теплопроводности
18
+ 27, # морозостойкость
19
+ 38, # формат
20
+ 80, # Марка прочности кирпича (дубликат, созданный заказчиком)
21
+ 75] # марка прочности
22
+ end
23
+
24
+ def self.do_not_touch
25
+ [
26
+ 78, # Завод производитель
27
+ ]
28
+ end
29
+
30
+ def self.siz
31
+ [23] # размер
32
+ end
33
+
34
+ def before_save_format_value
35
+
36
+ v = self.value
37
+ uom = prop_name.uom
38
+
39
+ # удаляем пробелы в начале и в конце строки
40
+ v = v.strip! || v
41
+
42
+ # числовые значения преобразуем в числа
43
+ if uom.present? && uom.is_number
44
+
45
+ v = v.gsub(' ', '')
46
+ v = v.gsub(',', '.')
47
+ v = v[/([0-9.]+)/]
48
+
49
+ # нечисловые значения: либо capitalize, либо upcase, либо downcase
50
+ else
51
+
52
+ if prop_name_id.in?(ItemProp.capz)
53
+ v = v.mb_chars.capitalize.to_s
54
+ elsif prop_name_id.in?(ItemProp.uppz)
55
+ v = v.mb_chars.upcase.to_s
56
+ elsif prop_name_id.in?(ItemProp.do_not_touch)
57
+ v = v
58
+ else
59
+ v = v.mb_chars.downcase.to_s
60
+ end
61
+
62
+ end
63
+
64
+ if prop_name_id.in?(ItemProp.siz)
65
+ v = v.gsub(',', '.')
66
+ sizes = v.scan(/([0-9,]+)/)
67
+ oum = v.scan(/[мс]*м/)
68
+ v = sizes.join(" x ")
69
+ if oum.count > 0
70
+ v += " #{oum[0]}"
71
+ else
72
+ v += ' мм'
73
+ end
74
+ end
75
+
76
+
77
+ self.value = v
78
+
79
+ end
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,5 @@
1
+ module C80Yax
2
+ class Prop < ActiveRecord::Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,32 @@
1
+ module C80Yax
2
+ class PropName < ActiveRecord::Base
3
+
4
+ validates :title,
5
+ presence: true,
6
+ uniqueness: true,
7
+ length: { in: 2..150 }
8
+
9
+ # has_many :item_props, :dependent => :destroy
10
+ # has_and_belongs_to_many :strsubcats
11
+ belongs_to :uom
12
+ accepts_nested_attributes_for :uom
13
+
14
+ has_many :im_old_price_and_this_is_link_to_my_normal_price, # NOTE:: поправить, как дойдёт дело
15
+ class_name: 'C80Yax::ItemProp',
16
+ foreign_key: 'related_id'
17
+
18
+ # для цены можно указать старую цену (если она есть)
19
+ belongs_to :related,
20
+ class_name: 'C80Yax::ItemProp'
21
+
22
+ # has_and_belongs_to_many :main_props
23
+ # has_and_belongs_to_many :common_props
24
+ # has_and_belongs_to_many :price_props
25
+
26
+ default_scope {order(:title => :asc)}
27
+
28
+ # validates_with PropNameValidator
29
+ # TODO:: добавить валидацию title на уникальность и длину
30
+
31
+ end
32
+ end
@@ -0,0 +1,71 @@
1
+ require 'babosa'
2
+
3
+ class C80Yax::Strsubcat < ActiveRecord::Base
4
+
5
+ validates :title,
6
+ presence: true,
7
+ uniqueness: true,
8
+ length: { in: 6..50 }
9
+
10
+ has_many :subordinates,
11
+ class_name: 'C80Yax::Strsubcat',
12
+ foreign_key: 'parent_id'
13
+
14
+ belongs_to :parent,
15
+ class_name: 'C80Yax::Strsubcat'
16
+
17
+ has_and_belongs_to_many :prop_names#,
18
+ #:after_add => :after_add_prop_names,
19
+ #:after_remove => :after_remove_prop_names
20
+
21
+ has_many :items, :dependent => :destroy
22
+
23
+ # has_many :main_props, :dependent => :destroy
24
+ # accepts_nested_attributes_for :main_props,
25
+ # :reject_if => lambda { |attributes|
26
+ # !attributes.present?
27
+ # },
28
+ # :allow_destroy => true
29
+ #
30
+ # has_many :price_props, :dependent => :destroy
31
+ # accepts_nested_attributes_for :price_props,
32
+ # :reject_if => lambda { |attributes|
33
+ # !attributes.present?
34
+ # },
35
+ # :allow_destroy => true
36
+ #
37
+ # has_many :common_props, :dependent => :destroy
38
+ # accepts_nested_attributes_for :common_props,
39
+ # :reject_if => lambda { |attributes|
40
+ # !attributes.present?
41
+ # },
42
+ # :allow_destroy => true
43
+
44
+ extend FriendlyId
45
+ friendly_id :slug_candidates, :use => :slugged
46
+
47
+ def slug_candidates
48
+ [:title] + Array.new(6) {|index| [:title, index+2]}
49
+ end
50
+
51
+ def normalize_friendly_id(input)
52
+ input.to_s.to_slug.normalize(transliterations: :russian).to_s
53
+ end
54
+
55
+ scope :menu_order, -> {order(:ord => :asc)}
56
+
57
+ # ------------------------------------------------------------------------------------------------------------------------
58
+
59
+ ransacker :parent_id,
60
+ formatter: proc { |v|
61
+ # Rails.logger.debug "[TRACE] <v> #{v}"
62
+ results = C80Yax::Strsubcat
63
+ .where(:parent_id => v)
64
+ .map(&:id)
65
+ # Rails.logger.debug "[TRACE] <results> #{results}"
66
+ results = results.present? ? results : nil
67
+ }, splat_params: true do |parent|
68
+ parent.table[:id]
69
+ end
70
+
71
+ end
@@ -0,0 +1,5 @@
1
+ module C80Yax
2
+ class Uom < ActiveRecord::Base
3
+ has_one :prop_name
4
+ end
5
+ end
@@ -0,0 +1,41 @@
1
+ module C80Yax
2
+
3
+ # грузит картинку услуги
4
+ class IphotoUploader < BaseFileUploader
5
+
6
+ process :resize_to_limit => [1024,768]
7
+
8
+ version :thumb_sm do
9
+ begin
10
+ p = C80Yax::Prop.first
11
+ process :resize_to_fill => [p.thumb_sm_width, p.thumb_sm_height]
12
+ rescue => e
13
+ Rails.logger.debug "[TRACE] <iphoto_uploader.thumb_sm> [ERROR] #{e}"
14
+ end
15
+ end
16
+
17
+ version :thumb_md do
18
+ begin
19
+ p = C80Yax::Prop.first
20
+ process :resize_to_fill => [p.thumb_md_width, p.thumb_md_height]
21
+ rescue => e
22
+ Rails.logger.debug "[TRACE] <iphoto_uploader.thumb_md> [ERROR] #{e}"
23
+ end
24
+ end
25
+
26
+ version :thumb_lg do
27
+ begin
28
+ p = C80Yax::Prop.first
29
+ process :resize_to_fill => [p.thumb_lg_width, p.thumb_lg_height]
30
+ rescue => e
31
+ Rails.logger.debug "[TRACE] <iphoto_uploader.thumb_lg> [ERROR] #{e}"
32
+ end
33
+ end
34
+
35
+ def store_dir
36
+ 'uploads/items'
37
+ end
38
+
39
+ end
40
+
41
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "c80_yax"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start