hot-glue 0.5.8 → 0.5.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -2
  3. data/Gemfile +1 -1
  4. data/README.md +267 -149
  5. data/app/helpers/hot_glue/controller_helper.rb +9 -6
  6. data/config/hot_glue.yml +2 -0
  7. data/lib/generators/hot_glue/direct_upload_install_generator.rb +48 -0
  8. data/lib/generators/hot_glue/dropzone_install_generator.rb +42 -0
  9. data/lib/generators/hot_glue/field_factory.rb +57 -0
  10. data/lib/generators/hot_glue/fields/association_field.rb +76 -0
  11. data/lib/generators/hot_glue/fields/attachment_field.rb +9 -0
  12. data/lib/generators/hot_glue/fields/boolean_field.rb +18 -0
  13. data/lib/generators/hot_glue/fields/date_field.rb +10 -0
  14. data/lib/generators/hot_glue/fields/date_time_field.rb +23 -0
  15. data/lib/generators/hot_glue/fields/enum_field.rb +27 -0
  16. data/lib/generators/hot_glue/fields/field.rb +51 -0
  17. data/lib/generators/hot_glue/fields/float_field.rb +11 -0
  18. data/lib/generators/hot_glue/fields/integer_field.rb +26 -0
  19. data/lib/generators/hot_glue/fields/string_field.rb +33 -0
  20. data/lib/generators/hot_glue/fields/text_field.rb +14 -0
  21. data/lib/generators/hot_glue/fields/time_field.rb +6 -0
  22. data/lib/generators/hot_glue/fields/uuid_field.rb +12 -0
  23. data/lib/generators/hot_glue/layout/builder.rb +15 -6
  24. data/lib/generators/hot_glue/layout_strategy/base.rb +2 -0
  25. data/lib/generators/hot_glue/layout_strategy/bootstrap.rb +4 -0
  26. data/lib/generators/hot_glue/markup_templates/erb.rb +158 -117
  27. data/lib/generators/hot_glue/scaffold_generator.rb +296 -306
  28. data/lib/generators/hot_glue/templates/computer_code.jpg +0 -0
  29. data/lib/generators/hot_glue/templates/controller.rb.erb +15 -9
  30. data/lib/generators/hot_glue/templates/erb/_list.erb +19 -6
  31. data/lib/generators/hot_glue/templates/erb/_show.erb +6 -4
  32. data/lib/generators/hot_glue/templates/javascript/dropzone_controller.js +191 -0
  33. data/lib/generators/hot_glue/templates/system_spec.rb.erb +32 -111
  34. data/lib/hotglue/version.rb +1 -1
  35. data/script/clean_generated_code +1 -1
  36. metadata +22 -4
@@ -79,11 +79,14 @@ module HotGlue
79
79
  hawk_schema.each do |hawk_key,hawk_definition|
80
80
  hawk_root = hawk_definition[0]
81
81
  # hawk_scope = hawk_definition[1]
82
- begin
83
- eval("hawk_root").find(modified_params[hawk_key.to_s])
84
- rescue ActiveRecord::RecordNotFound => e
85
- @hawk_alarm << "You aren't allowed to set #{hawk_key.to_s} to #{modified_params[hawk_key.to_s]}. "
86
- modified_params.tap { |hs| hs.delete(hawk_key.to_s) }
82
+
83
+ unless modified_params[hawk_key.to_s].blank?
84
+ begin
85
+ eval("hawk_root").find(modified_params[hawk_key.to_s])
86
+ rescue ActiveRecord::RecordNotFound => e
87
+ @hawk_alarm << "You aren't allowed to set #{hawk_key.to_s} to #{modified_params[hawk_key.to_s]}. "
88
+ modified_params.tap { |hs| hs.delete(hawk_key.to_s) }
89
+ end
87
90
  end
88
91
  end
89
92
  modified_params
@@ -95,4 +98,4 @@ module HotGlue
95
98
  Time.now.strftime("%z").to_i/100
96
99
  end
97
100
  end
98
- end
101
+ end
data/config/hot_glue.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  ---
2
2
  :layout: hotglue
3
3
  :markup: erb
4
+
5
+ :sample_file_path: spec/files/computer_code.jpg
@@ -0,0 +1,48 @@
1
+
2
+
3
+ module HotGlue
4
+ class DirectUploadInstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ def filepath_prefix
8
+ # todo: inject the context
9
+ 'spec/dummy/' if Rails.env.test?
10
+ end
11
+
12
+ def initialize(*args) #:nodoc:
13
+ super
14
+
15
+ if Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.group_by{ |g| g.name }['importmap-rails'] # impomrtmaps
16
+
17
+ file_contents = File.read("#{filepath_prefix}app/javascript/application.js")
18
+
19
+ if !file_contents.include?("//= require activestorage")
20
+ file_contents << "//= require activestorage"
21
+ File.write("#{filepath_prefix}app/javascript/application.js", file_contents)
22
+ puts " HOTGLUE --> added to #{filepath_prefix}app/javascript/application.js: `//= require activestorage"
23
+ else
24
+ puts " HOTGLUE --> #{filepath_prefix}app/javascript/application.js already contains `//= require activestorage`"
25
+ end
26
+
27
+ elsif Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.group_by{ |g| g.name }['jsbundling-rails']
28
+ file_contents = File.read("#{filepath_prefix}app/javascript/application.js")
29
+
30
+ if !file_contents.include?("ActiveStorage.start()")
31
+ file_contents << "import * as ActiveStorage from \"@rails/activestorage\"
32
+ ActiveStorage.start()"
33
+ File.write("#{filepath_prefix}app/javascript/application.js", file_contents)
34
+ puts " HOTGLUE --> added to #{filepath_prefix}app/javascript/application.js: `ActiveStorage.start()"
35
+ else
36
+ puts " HOTGLUE --> #{filepath_prefix}app/javascript/application.js already contains `ActiveStorage.start()`"
37
+ end
38
+
39
+
40
+ else
41
+ puts " HOTGLUE --> could not detect either importmap-rails or jsbundling-rails app"
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+
48
+
@@ -0,0 +1,42 @@
1
+ module HotGlue
2
+ class DropzoneInstallGenerator < Rails::Generators::Base
3
+ source_root File.expand_path('templates', __dir__)
4
+
5
+ def filepath_prefix
6
+ # todo: inject the context
7
+ 'spec/dummy/' if Rails.env.test?
8
+ end
9
+
10
+
11
+ def initialize(*args) #:nodoc:
12
+ super
13
+ system("./bin/rails generate stimulus Dropzone")
14
+ copy_file "javascript/dropzone_controller.js", "#{filepath_prefix}app/javascript/controllers/dropzone_controller.js"
15
+ puts "HOT GLUE --> copying dropzone stimulus controller into app/javascript/controllers/dropzone_controller.js"
16
+
17
+
18
+ if File.exist?("#{filepath_prefix}app/assets/stylesheets/application.bootstrap.scss")
19
+ scss_file = "#{filepath_prefix}app/assets/stylesheets/application.bootstrap.scss"
20
+ elsif File.exist?("#{filepath_prefix}app/assets/stylesheets/application.scss")
21
+ scss_file = "#{filepath_prefix}app/assets/stylesheets/application.scss"
22
+ else
23
+ raise HotGlue::Error, "Could not detect your stylesheet, aborting..."
24
+ end
25
+
26
+ file_contents = File.read(scss_file)
27
+
28
+ if !file_contents.include?("@import \"dropzone/dist/dropzone\"")
29
+ file_contents << "\n@import \"dropzone/dist/dropzone\";\n@import \"dropzone/dist/basic\";"
30
+
31
+
32
+ File.write(scss_file, file_contents)
33
+ puts " HOTGLUE --> added to #{scss_file}: @import dropzone ... "
34
+ else
35
+ puts " HOTGLUE --> #{scss_file} already contains @import dropzone"
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+
42
+
@@ -0,0 +1,57 @@
1
+ require_relative "fields/association_field"
2
+ require_relative "fields/boolean_field"
3
+ require_relative "fields/date_field"
4
+ require_relative "fields/date_time_field"
5
+ require_relative "fields/enum_field"
6
+ require_relative "fields/float_field"
7
+ require_relative "fields/integer_field"
8
+ require_relative "fields/string_field"
9
+ require_relative "fields/text_field"
10
+ require_relative "fields/time_field"
11
+ require_relative "fields/uuid_field"
12
+ require_relative "fields/attachment_field"
13
+
14
+
15
+ class FieldFactory
16
+ attr_accessor :field, :class_name
17
+ def initialize(type: , name: , generator: )
18
+ field_class = case type
19
+ when :integer
20
+ if name.to_s.ends_with?("_id")
21
+ AssociationField
22
+ else
23
+ IntegerField
24
+ end
25
+ when :uuid
26
+ UUIDField
27
+ when :string
28
+ StringField
29
+ when :text
30
+ TextField
31
+ when :float
32
+ FloatField
33
+ when :datetime
34
+ DateTimeField
35
+ when :date
36
+ DateField
37
+ when :time
38
+ TimeField
39
+ when :boolean
40
+ BooleanField
41
+ when :enum
42
+ EnumField
43
+ when :attachment
44
+ AttachmentField
45
+ end
46
+ @class_name = class_name
47
+
48
+ @field = field_class.new(name: name,
49
+ hawk_keys: generator.hawk_keys,
50
+ auth: generator.auth,
51
+ class_name: generator.singular_class,
52
+ alt_lookups: generator.alt_lookups,
53
+ singular: generator.singular,
54
+ update_show_only: generator.update_show_only,
55
+ sample_file_path: generator.sample_file_path)
56
+ end
57
+ end
@@ -0,0 +1,76 @@
1
+
2
+ require_relative './field.rb'
3
+
4
+ class AssociationField < Field
5
+
6
+ def initialize(name: , class_name: , alt_lookups: , singular: , update_show_only: ,
7
+ hawk_keys: , auth: , sample_file_path: )
8
+ super
9
+ assoc_model = eval("#{class_name}.reflect_on_association(:#{assoc})")
10
+
11
+ if assoc_model.nil?
12
+ exit_message = "*** Oops: The model #{class_name} is missing an association for :#{assoc_name} or the model #{assoc_name.titlecase} doesn't exist. TODO: Please implement a model for #{assoc_name.titlecase}; or add to #{class_name} `belongs_to :#{assoc_name}`. To make a controller that can read all records, specify with --god."
13
+ puts exit_message
14
+ raise(HotGlue::Error, exit_message)
15
+ end
16
+
17
+ begin
18
+ assoc_class = eval(assoc_model.try(:class_name))
19
+ end
20
+
21
+ name_list = [:name, :to_label, :full_name, :display_name, :email]
22
+
23
+ if assoc_class && name_list.collect{ |field|
24
+ assoc_class.respond_to?(field.to_s) || assoc_class.instance_methods.include?(field)
25
+ }.none?
26
+ exit_message = "Oops: Missing a label for `#{assoc_class}`. Can't find any column to use as the display label for the #{@assoc_name} association on the #{class_name} model. TODO: Please implement just one of: 1) name, 2) to_label, 3) full_name, 4) display_name 5) email. You can implement any of these directly on your`#{assoc_class}` model (can be database fields or model methods) or alias them to field you want to use as your display label. Then RERUN THIS GENERATOR. (Field used will be chosen based on rank here.)"
27
+ raise(HotGlue::Error, exit_message)
28
+ end
29
+
30
+ # set teh assoc label
31
+ end
32
+
33
+ def assoc_label
34
+
35
+ end
36
+
37
+ def assoc_name
38
+ assoc
39
+ end
40
+
41
+ def assoc
42
+ name.to_s.gsub('_id','')
43
+ end
44
+
45
+ def spec_setup_and_change_act(which_partial)
46
+ if which_partial == :update && update_show_only.include?(name)
47
+ # do not update tests
48
+ elsif alt_lookups.keys.include?(name.to_s)
49
+ lookup = alt_lookups[name.to_s][:lookup_as]
50
+ " find(\"[name='#{singular}[__lookup_#{lookup}]']\").fill_in( with: #{assoc}1.#{lookup} )"
51
+ else
52
+ " #{name}_selector = find(\"[name='#{singular}[#{name}]']\").click \n" +
53
+ " #{name}_selector.first('option', text: #{assoc}1.name).select_option"
54
+ end
55
+ end
56
+
57
+ def spec_make_assertion
58
+ " expect(page).to have_content(#{assoc}1.name)"
59
+ end
60
+
61
+ def spec_setup_let_arg
62
+ "#{name.to_s.gsub('_id','')}: #{name.to_s.gsub('_id','')}1"
63
+ end
64
+
65
+ def spec_list_view_assertion
66
+
67
+ end
68
+
69
+ def spec_related_column_lets
70
+ the_foreign_class = eval(class_name + ".reflect_on_association(:" + assoc + ")").class_name.split("::").last.underscore
71
+
72
+ hawk_keys_on_lets = (hawk_keys["#{assoc}_id".to_sym] ? ", #{auth.gsub('current_', '')}: #{auth}": "")
73
+
74
+ " let!(:#{assoc}1) {create(:#{the_foreign_class}" + hawk_keys_on_lets + ")}"
75
+ end
76
+ end
@@ -0,0 +1,9 @@
1
+ class AttachmentField < Field
2
+ def spec_setup_let_arg
3
+ nil
4
+ end
5
+
6
+ def spec_setup_and_change_act(which_partial = nil)
7
+ " attach_file(\"#{singular}[#{name.to_s}]\", \"#{sample_file_path}\")"
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ class BooleanField < Field
2
+ def spec_setup_and_change_act(which_partial = nil)
3
+ " new_#{name} = 1 \n" +
4
+ " find(\"[name='#{testing_name}[#{name}]'][value='\#{new_" + name.to_s + "}']\").choose"
5
+ end
6
+
7
+ def spec_make_assertion
8
+ ["expect(page).to have_content(#{singular}#{1}.#{name} ? 'YES' : 'NO')"].join("\n ")
9
+ end
10
+
11
+ def spec_setup_let_arg
12
+ "#{name}: !!rand(2).floor"
13
+ end
14
+
15
+ def spec_list_view_assertion
16
+ " " + ["expect(page).to have_content(#{singular}#{1}.#{name} ? 'YES' : 'NO')"].join("\n ")
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ class DateField < Field
2
+ def spec_setup_and_change_act(which_partial = nil)
3
+ " " + "new_#{name} = Date.current + (rand(100).days) \n" +
4
+ ' ' + "find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})"
5
+ end
6
+
7
+ def spec_setup_let_arg
8
+ "#{name}: Date.current + rand(50).days"
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ class DateTimeField < Field
2
+ def spec_random_data
3
+ Time.now + rand(1..5).days
4
+ end
5
+
6
+ def spec_setup_and_change_act(which_partial = nil)
7
+ " " + "new_#{name} = DateTime.current + (rand(100).days) \n" +
8
+ ' ' + "find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})"
9
+
10
+ end
11
+
12
+ def spec_make_assertion
13
+ super
14
+ end
15
+
16
+ def spec_setup_let_arg
17
+ "#{name}: DateTime.current + rand(1000).seconds"
18
+ end
19
+
20
+ def spec_list_view_assertion
21
+ " " + ["expect(page).to have_content(#{singular}#{1}.#{name}.in_time_zone(current_timezone).strftime('%m/%d/%Y @ %l:%M %p ').gsub(' ', ' ') + timezonize(current_timezone) )"].join("\n ")
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ class EnumField < Field
2
+ def spec_setup_and_change_act(which_partial = nil)
3
+ " list_of_#{name.to_s} = #{singular_class}.defined_enums['#{name.to_s}'].keys \n" +
4
+ " " + "new_#{name.to_s} = list_of_#{name.to_s}[rand(list_of_#{name.to_s}.length)].to_s \n" +
5
+ ' find("select[name=\'' + singular + '[' + name.to_s + ']\'] option[value=\'#{new_' + name.to_s + '}\']").select_option'
6
+ end
7
+
8
+ def spec_make_assertion
9
+ if(eval("#{singular_class}.respond_to?(:#{name}_labels)"))
10
+ "expect(page).to have_content(#{singular_class}.#{name}_labels[new_#{name}])"
11
+ else
12
+ "expect(page).to have_content(new_#{name})"
13
+ end
14
+ end
15
+
16
+ def spec_setup_let_args
17
+ super
18
+ end
19
+
20
+ def spec_list_view_assertion
21
+ if(eval("#{singular_class}.respond_to?(:#{name}_labels)"))
22
+ " " + "expect(page).to have_content(#{singular_class}.#{name}_labels[#{singular}#{1}.#{name}])"
23
+ else
24
+ " " + "expect(page).to have_content(#{singular}1.#{name})"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,51 @@
1
+ class Field
2
+ attr_accessor :name, :object, :singular_class, :class_name, :singular,
3
+ :update_show_only
4
+ attr_accessor :assoc_model, :assoc_name, :assoc_class, :associations, :alt_lookups, :assoc_label
5
+
6
+ attr_accessor :hawk_keys, :auth, :sample_file_path
7
+
8
+ def initialize(name: , class_name: , alt_lookups: , singular: , update_show_only: ,
9
+ hawk_keys: , auth: , sample_file_path: nil)
10
+ @name = name
11
+ @alt_lookups = alt_lookups
12
+ @singular = singular
13
+ @class_name = class_name
14
+ @update_show_only = update_show_only
15
+ @hawk_keys = hawk_keys
16
+ @auth = auth
17
+ @sample_file_path = sample_file_path
18
+ end
19
+
20
+ def getName
21
+ @name
22
+ end
23
+
24
+ def spec_random_data
25
+
26
+ end
27
+
28
+ def testing_name
29
+ class_name.to_s.gsub("::","_").underscore
30
+ end
31
+
32
+ def spec_setup_and_change_act(which_partial = nil)
33
+ ""
34
+ end
35
+
36
+ def spec_make_assertion
37
+ "expect(page).to have_content(new_#{name})"
38
+ end
39
+
40
+ def spec_setup_let_arg
41
+
42
+ end
43
+
44
+ def spec_list_view_assertion
45
+ " " + ["expect(page).to have_content(#{singular}#{1}.#{name})"].join("\n ")
46
+ end
47
+
48
+ def spec_related_column_lets
49
+ ""
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ class FloatField < Field
2
+ def spec_setup_and_change_act(which_partial = nil)
3
+ " " + "new_#{name} = rand(10) \n" +
4
+ " find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})"
5
+
6
+ end
7
+
8
+ def spec_setup_let_arg
9
+ "#{name}: rand(1)*10000"
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ class IntegerField < Field
2
+ def spec_random_data
3
+ rand(1...1000)
4
+ end
5
+
6
+ def spec_setup_and_change_act(which_partial = nil)
7
+ if name.to_s.ends_with?("_id")
8
+ capybara_block_for_association(name_name: name, which_partial: which_partial)
9
+ else
10
+ " new_#{name} = rand(10) \n" +
11
+ " find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})"
12
+ end
13
+ end
14
+
15
+ def spec_make_assertion
16
+ "expect(page).to have_content(new_#{name})"
17
+ end
18
+
19
+ def spec_setup_let_arg
20
+ "#{name}: rand(100)"
21
+ end
22
+
23
+ def spec_list_view_assertion
24
+ " " + ["expect(page).to have_content(#{singular}#{1}.#{name})"].join("\n ")
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ class StringField < Field
2
+ def spec_random_data
3
+ FFaker::AnimalUS.common_name
4
+ end
5
+
6
+ def spec_setup_and_change_act(which_partial = nil)
7
+ faker_string =
8
+ if name.to_s.include?('email')
9
+ "FFaker::Internet.email"
10
+ elsif name.to_s.include?('domain')
11
+ "FFaker::Internet.domain_name"
12
+ elsif name.to_s.include?('ip_address') || name.to_s.ends_with?('_ip')
13
+ "FFaker::Internet.ip_v4_address"
14
+ else
15
+ "FFaker::Movie.title"
16
+ end
17
+
18
+ return " " + "new_#{name} = #{faker_string} \n" +
19
+ " find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})"
20
+ end
21
+
22
+ def spec_setup_let_arg
23
+ if name.to_s.include?('email')
24
+ "#{name}: FFaker::Internet.email"
25
+ elsif name.to_s.include?('domain')
26
+ "#{name}: FFaker::Internet.domain_name"
27
+ elsif name.to_s.include?('ip_address') || name.to_s.ends_with?('_ip')
28
+ "#{name}: FFaker::Internet.ip_v4_address"
29
+ else
30
+ "#{name}: FFaker::Movie.title"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ class TextField < Field
2
+ def spec_random_data
3
+ FFaker::AnimalUS.common_name
4
+ end
5
+
6
+ def spec_setup_and_change_act(which_partial = nil)
7
+ " " + "new_#{name} = FFaker::Lorem.paragraphs(1).join("") \n" +
8
+ " find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})"
9
+ end
10
+
11
+ def spec_setup_let_arg
12
+ "#{name}: FFaker::Lorem.paragraphs(10).join(" ")"
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ class TimeField < Field
2
+
3
+ def spec_setup_let_arg
4
+ "#{name}: Time.current + rand(5000).seconds"
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ class UUIDField < Field
2
+ def spec_setup_let_arg
3
+ "#{name.to_s.gsub('_id','')}: #{name.to_s.gsub('_id','')}1"
4
+ end
5
+
6
+ def spec_list_view_assertion
7
+ assoc_name = name.to_s.gsub('_id','')
8
+ association = eval("#{singular_class}.reflect_on_association(:#{assoc_name})")
9
+ " " + ["expect(page).to have_content(#{singular}#{1}.#{assoc_name}.#{HotGlue.derrive_reference_name(association.class_name)})"].join("\n ")
10
+
11
+ end
12
+ end
@@ -6,13 +6,14 @@ module HotGlue
6
6
  attr_reader :include_setting,
7
7
  :downnest_object,
8
8
  :buttons_width, :columns,
9
- :smart_layout, :specified_grouping_mode
9
+ :smart_layout, :specified_grouping_mode, :stacked_downnesting
10
10
 
11
11
  def initialize(include_setting: nil,
12
12
  downnest_object: nil,
13
13
  buttons_width: nil,
14
14
  smart_layout: nil,
15
- columns: nil)
15
+ columns: nil,
16
+ stacked_downnesting: false)
16
17
  @include_setting = include_setting
17
18
  @downnest_object = downnest_object
18
19
  @buttons_width = buttons_width
@@ -21,6 +22,7 @@ module HotGlue
21
22
 
22
23
  @no_buttons = @buttons_width == 0
23
24
  @specified_grouping_mode = include_setting.include?(":")
25
+ @stacked_downnesting = stacked_downnesting
24
26
  end
25
27
 
26
28
  def construct
@@ -44,21 +46,28 @@ module HotGlue
44
46
 
45
47
  bootstrap_columns = (12 - @buttons_width )
46
48
 
47
- bootstrap_columns = bootstrap_columns - (downnest_object.collect{|k,v| v}.sum)
49
+ if(!stacked_downnesting)
50
+ bootstrap_columns = bootstrap_columns - (downnest_object.collect{|k,v| v}.sum)
51
+ else
52
+ bootstrap_columns = bootstrap_columns - 4
53
+ end
48
54
 
49
55
  available_columns = (bootstrap_columns / 2).floor # bascially turns the 12-column grid into a 6-column grid
50
56
 
51
57
  if available_columns < 0
52
58
  raise "Cannot build layout with #{how_many_downnest} downnested portals"
53
59
  end
54
-
60
+ #
61
+ # if !stacked_downnesting
62
+ #
63
+ # else
64
+ #
65
+ # end
55
66
  downnest_children_width = []
56
-
57
67
  downnest_object.each do |child, size|
58
68
  layout_object[:portals][child] = {size: size}
59
69
  end
60
70
 
61
-
62
71
  if smart_layout
63
72
  # automatic control
64
73
  #
@@ -5,6 +5,7 @@ module LayoutStrategy
5
5
  @builder = scaffold_builder
6
6
  end
7
7
 
8
+ def button_applied_classes; end
8
9
  def column_classes_for_button_column; ""; end
9
10
  def button_classes; ""; end
10
11
  def button_column_style; "" ; end
@@ -23,6 +24,7 @@ module LayoutStrategy
23
24
  (col_width/(builder.columns.count)).to_i
24
25
  end
25
26
  def list_classes; ""; end
27
+ def magic_button_classes; ""; end
26
28
  def row_classes; ""; end
27
29
  def row_heading_classes; ""; end
28
30
  def page_begin; '<div> '; end
@@ -41,6 +41,10 @@ class LayoutStrategy::Bootstrap < LayoutStrategy::Base
41
41
  "col-sm-#{ builder.layout_object[:portals][downnest][:size] }"
42
42
  end
43
43
 
44
+ def downnest_portal_stacked_column_width
45
+ "col-sm-4"
46
+ end
47
+
44
48
  def page_begin
45
49
  '<div class="row"> <div class="col-md-12">'
46
50
  end