potassium 5.2.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +42 -0
  3. data/.editorconfig +3 -0
  4. data/.node-version +1 -0
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +94 -0
  7. data/README.md +41 -47
  8. data/docs/DSL.md +5 -5
  9. data/lib/potassium/assets/.buildpacks +0 -1
  10. data/lib/potassium/assets/.circleci/config.yml.erb +102 -0
  11. data/lib/potassium/assets/.eslintrc.json +352 -0
  12. data/lib/potassium/assets/.github/pull_request_template.md +9 -0
  13. data/lib/potassium/assets/.pryrc +1 -1
  14. data/lib/potassium/assets/.rubocop.yml +515 -0
  15. data/lib/potassium/assets/.stylelintrc.json +46 -0
  16. data/lib/potassium/assets/Dockerfile.ci +1 -1
  17. data/lib/potassium/assets/Makefile.erb +35 -0
  18. data/lib/potassium/assets/Procfile +1 -0
  19. data/lib/potassium/assets/README.yml +60 -11
  20. data/lib/potassium/assets/active_admin/admin-component.vue +35 -0
  21. data/lib/potassium/assets/active_admin/admin_application.js +14 -0
  22. data/lib/potassium/assets/active_admin/init_activeadmin_vue.rb +10 -0
  23. data/lib/potassium/assets/app/graphql/graphql_controller.rb +55 -0
  24. data/lib/potassium/assets/app/graphql/mutations/login_mutation.rb +23 -0
  25. data/lib/potassium/assets/app/graphql/queries/base_query.rb +4 -0
  26. data/lib/potassium/assets/app/graphql/types/base/base_argument.rb +4 -0
  27. data/lib/potassium/assets/app/graphql/types/base/base_enum.rb +4 -0
  28. data/lib/potassium/assets/app/graphql/types/base/base_field.rb +5 -0
  29. data/lib/potassium/assets/app/graphql/types/base/base_input_object.rb +5 -0
  30. data/lib/potassium/assets/app/graphql/types/base/base_interface.rb +7 -0
  31. data/lib/potassium/assets/app/graphql/types/base/base_object.rb +5 -0
  32. data/lib/potassium/assets/app/graphql/types/base/base_scalar.rb +4 -0
  33. data/lib/potassium/assets/app/graphql/types/base/base_union.rb +4 -0
  34. data/lib/potassium/assets/app/graphql/types/mutation_type.rb +10 -0
  35. data/lib/potassium/assets/app/graphql/types/query_type.rb +13 -0
  36. data/lib/potassium/assets/app/javascript/app.spec.js +14 -0
  37. data/lib/potassium/assets/app/uploaders/base_uploader.rb +11 -0
  38. data/lib/potassium/assets/app/uploaders/image_uploader.rb +5 -0
  39. data/lib/potassium/assets/bin/cibuild.erb +24 -7
  40. data/lib/potassium/assets/bin/release +9 -0
  41. data/lib/potassium/assets/bin/setup.erb +3 -0
  42. data/lib/potassium/assets/config/database_mysql.yml.erb +5 -5
  43. data/lib/potassium/assets/config/database_postgresql.yml.erb +5 -5
  44. data/lib/potassium/assets/config/graphql_playground.rb +20 -0
  45. data/lib/potassium/assets/config/puma.rb +5 -1
  46. data/lib/potassium/assets/config/shrine.rb +36 -0
  47. data/lib/potassium/assets/docker-compose.ci.yml +2 -1
  48. data/lib/potassium/assets/docker-compose.yml +6 -0
  49. data/lib/potassium/assets/lib/tasks/auto_annotate_models.rake +34 -33
  50. data/lib/potassium/assets/package.json +4 -1
  51. data/lib/potassium/assets/redis.yml +1 -2
  52. data/lib/potassium/assets/sidekiq_scheduler.yml +1 -1
  53. data/lib/potassium/assets/testing/rails_helper.rb +4 -2
  54. data/lib/potassium/cli/commands/create.rb +12 -19
  55. data/lib/potassium/cli_options.rb +67 -24
  56. data/lib/potassium/helpers/docker-helpers.rb +14 -5
  57. data/lib/potassium/helpers/gem-helpers.rb +1 -1
  58. data/lib/potassium/helpers/template-helpers.rb +4 -0
  59. data/lib/potassium/newest_version_ensurer.rb +19 -36
  60. data/lib/potassium/node_version_ensurer.rb +30 -0
  61. data/lib/potassium/recipes/admin.rb +3 -3
  62. data/lib/potassium/recipes/annotate.rb +1 -1
  63. data/lib/potassium/recipes/api.rb +93 -21
  64. data/lib/potassium/recipes/background_processor.rb +38 -2
  65. data/lib/potassium/recipes/ci.rb +8 -6
  66. data/lib/potassium/recipes/data_migrate.rb +44 -0
  67. data/lib/potassium/recipes/database.rb +6 -2
  68. data/lib/potassium/recipes/database_container.rb +90 -0
  69. data/lib/potassium/recipes/draper.rb +1 -10
  70. data/lib/potassium/recipes/file_storage.rb +66 -0
  71. data/lib/potassium/recipes/front_end.rb +219 -9
  72. data/lib/potassium/recipes/github.rb +93 -15
  73. data/lib/potassium/recipes/heroku.rb +2 -1
  74. data/lib/potassium/recipes/listen.rb +7 -0
  75. data/lib/potassium/recipes/mailer.rb +14 -4
  76. data/lib/potassium/recipes/node.rb +21 -0
  77. data/lib/potassium/recipes/puma.rb +0 -3
  78. data/lib/potassium/recipes/rack_cors.rb +18 -15
  79. data/lib/potassium/recipes/rails.rb +6 -0
  80. data/lib/potassium/recipes/schedule.rb +1 -1
  81. data/lib/potassium/recipes/style.rb +21 -3
  82. data/lib/potassium/recipes/vue_admin.rb +124 -0
  83. data/lib/potassium/templates/application.rb +10 -9
  84. data/lib/potassium/version.rb +7 -4
  85. data/potassium.gemspec +9 -6
  86. data/spec/features/api_spec.rb +25 -0
  87. data/spec/features/background_processor_spec.rb +12 -1
  88. data/spec/features/data_migrate_spec.rb +14 -0
  89. data/spec/features/database_container_spec.rb +35 -0
  90. data/spec/features/draper_spec.rb +1 -6
  91. data/spec/features/file_storage_spec.rb +75 -0
  92. data/spec/features/front_end_spec.rb +88 -0
  93. data/spec/features/github_spec.rb +53 -8
  94. data/spec/features/graphql_spec.rb +71 -0
  95. data/spec/features/heroku_spec.rb +8 -5
  96. data/spec/features/mailer_spec.rb +42 -0
  97. data/spec/features/new_project_spec.rb +6 -14
  98. data/spec/features/node_spec.rb +28 -0
  99. data/spec/features/power_types_spec.rb +5 -16
  100. data/spec/features/vue_admin_spec.rb +47 -0
  101. data/spec/spec_helper.rb +5 -0
  102. data/spec/support/fake_octokit.rb +31 -0
  103. data/spec/support/potassium_test_helpers.rb +26 -8
  104. metadata +126 -45
  105. data/circle.yml +0 -12
  106. data/lib/potassium/assets/.circleci/config.yml +0 -20
  107. data/lib/potassium/assets/active_admin/active_admin.js.coffee +0 -4
  108. data/lib/potassium/assets/active_admin/init_activeadmin_angular.rb +0 -8
  109. data/lib/potassium/assets/api/api_error_concern.rb +0 -32
  110. data/lib/potassium/assets/api/base_controller.rb +0 -9
  111. data/lib/potassium/assets/api/draper_responder.rb +0 -62
  112. data/lib/potassium/assets/api/responder.rb +0 -41
  113. data/lib/potassium/assets/aws.rb +0 -1
  114. data/lib/potassium/assets/testing/paperclip.rb +0 -59
  115. data/lib/potassium/recipes/active_storage.rb +0 -40
  116. data/lib/potassium/recipes/angular_admin.rb +0 -56
  117. data/lib/potassium/recipes/aws_sdk.rb +0 -7
  118. data/lib/potassium/recipes/paperclip.rb +0 -47
  119. data/spec/features/active_storage_spec.rb +0 -30
  120. data/spec/features/front_end.rb +0 -30
@@ -1,29 +1,107 @@
1
+ require 'octokit'
2
+
1
3
  class Recipes::Github < Rails::AppBuilder
2
4
  def ask
3
- repo_name = "platanus/#{get(:dasherized_app_name)}"
4
5
  github_repo_create = answer(:github) do
5
- q = "Do you want to create the Github repository (https://github.com/#{repo_name}) " +
6
- "for this project?"
7
- Ask.confirm(q)
8
- end
9
- if github_repo_create
10
- github_repo_private = answer(:"github-private") do
11
- Ask.confirm("Should the repository be private?")
12
- end
6
+ Ask.confirm('Do you want to create a Github repository?')
13
7
  end
14
- set(:github_repo_name, repo_name)
15
8
  set(:github_repo, github_repo_create)
16
- set(:github_repo_private, github_repo_private)
9
+ setup_repo if github_repo_create
10
+ end
11
+
12
+ def setup_repo
13
+ setup_repo_private
14
+ setup_repo_org
15
+ setup_repo_name
16
+ set(:github_access_token, get_access_token)
17
17
  end
18
18
 
19
19
  def create
20
- github_repo_create(get(:github_repo_name), get(:github_repo_private)) if selected?(:github_repo)
20
+ return unless selected?(:github_repo)
21
+
22
+ create_github_repo
23
+ copy_file '../assets/.github/pull_request_template.md', '.github/pull_request_template.md'
21
24
  end
22
25
 
23
26
  private
24
27
 
25
- def github_repo_create(repo_name, private_repo = false)
26
- flag = private_repo ? "-p" : ""
27
- run "hub create #{flag} #{repo_name}"
28
+ def setup_repo_private
29
+ repo_private = answer(:github_private) do
30
+ Ask.confirm('Should the repository be private?')
31
+ end
32
+ set(:github_repo_private, repo_private)
33
+ end
34
+
35
+ def setup_repo_org
36
+ has_organization = answer(:github_has_org) do
37
+ Ask.confirm('Is this repo for a Github organization?')
38
+ end
39
+ set(:github_has_org, has_organization)
40
+ if has_organization
41
+ repo_organization = answer(:github_org) do
42
+ Ask.input('What is the organization for this repository?', default: 'platanus')
43
+ end
44
+ set(:github_org, repo_organization)
45
+ end
46
+ end
47
+
48
+ def setup_repo_name
49
+ repo_name = answer(:github_name) do
50
+ Ask.input('What is the name for this repository?', default: get(:dasherized_app_name))
51
+ end
52
+ set(:github_repo_name, repo_name)
53
+ end
54
+
55
+ def create_github_repo
56
+ options = { private: get(:github_repo_private) }
57
+ options[:organization] = get(:github_org) if get(:github_has_org)
58
+ repo_name = get(:github_repo_name)
59
+
60
+ is_retry = false
61
+ begin
62
+ github_client(is_retry).create_repository(repo_name, options)
63
+ rescue Octokit::Unauthorized
64
+ is_retry = true
65
+ retry if retry_create_repo
66
+ end
67
+ end
68
+
69
+ def retry_create_repo
70
+ puts "Bad credentials, information on Personal Access Tokens here:"
71
+ puts "https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token"
72
+ puts "Make sure to give repo access to the personal access token"
73
+ Ask.confirm("Do you want to retry?")
74
+ end
75
+
76
+ def github_client(is_retry = false)
77
+ access_token = is_retry ? set_access_token : get(:github_access_token)
78
+ octokit_client.new(access_token: access_token)
79
+ end
80
+
81
+ def octokit_client
82
+ if answer(:test)
83
+ require_relative '../../../spec/support/fake_octokit'
84
+ FakeOctokit
85
+ else
86
+ Octokit::Client
87
+ end
88
+ end
89
+
90
+ def get_access_token
91
+ return File.open(config_filename, 'r').read if File.exists?(config_filename)
92
+
93
+ set_access_token
94
+ end
95
+
96
+ def set_access_token
97
+ access_token = answer(:github_access_token) do
98
+ Ask.input('Enter a GitHub personal access token', password: true)
99
+ end
100
+ File.open(config_filename, 'w') { |f| f.write(access_token) }
101
+ access_token
102
+ end
103
+
104
+ def config_filename
105
+ @config_filename ||= File.expand_path('~/.potassium')
28
106
  end
29
107
  end
@@ -41,6 +41,8 @@ class Recipes::Heroku < Rails::AppBuilder
41
41
 
42
42
  copy_file '../assets/Procfile', 'Procfile'
43
43
  copy_file '../assets/.buildpacks', '.buildpacks'
44
+ copy_file '../assets/bin/release', 'bin/release'
45
+ run 'chmod a+x bin/release'
44
46
 
45
47
  template "../assets/bin/setup_heroku.erb", "bin/setup_heroku", force: true
46
48
  run "chmod a+x bin/setup_heroku"
@@ -88,7 +90,6 @@ class Recipes::Heroku < Rails::AppBuilder
88
90
  run_toolbelt_command "labs:enable runtime-dyno-metadata", staged_app_name
89
91
  run_toolbelt_command "config:add HEROKU_APP_NAME=#{staged_app_name}", staged_app_name
90
92
  run_toolbelt_command "config:add #{rack_env}", staged_app_name
91
- run_toolbelt_command "config:add DEPLOY_TASKS=db:migrate", staged_app_name
92
93
 
93
94
  set_rails_secrets(environment)
94
95
  set_app_multi_buildpack(environment)
@@ -0,0 +1,7 @@
1
+ class Recipes::Listen < Rails::AppBuilder
2
+ def create
3
+ gather_gems(:development) do
4
+ gather_gem('listen')
5
+ end
6
+ end
7
+ end
@@ -24,8 +24,11 @@ class Recipes::Mailer < Rails::AppBuilder
24
24
  dependencies(email_service)
25
25
  config(email_service)
26
26
 
27
- background_processor = load_recipe(:background_processor)
28
- background_processor.add_sidekiq unless background_processor.installed?
27
+ background_processor_recipe = load_recipe(:background_processor)
28
+ background_processor_answer = get(:background_processor)
29
+
30
+ background_processor_recipe.add_sidekiq unless background_processor_recipe.installed? ||
31
+ background_processor_answer
29
32
  end
30
33
 
31
34
  def install
@@ -68,7 +71,7 @@ class Recipes::Mailer < Rails::AppBuilder
68
71
 
69
72
  mailer_config =
70
73
  <<~RUBY
71
- require Rails.root.join("config/mailer")
74
+ require Rails.root.join("config", "mailer")
72
75
  RUBY
73
76
 
74
77
  prepend_file "config/environments/production.rb", mailer_config
@@ -81,11 +84,18 @@ class Recipes::Mailer < Rails::AppBuilder
81
84
  append_to_file '.env.development', "SENDGRID_API_KEY=\n"
82
85
  sendgrid_settings = <<~RUBY
83
86
  Rails.application.config.action_mailer.sendgrid_settings = {
84
- api_key: ENV['SENDGRID_API']
87
+ api_key: ENV['SENDGRID_API_KEY']
85
88
  }
86
89
  RUBY
87
90
  inject_into_file 'config/mailer.rb', sendgrid_settings,
88
91
  after: "Rails.application.config.action_mailer.delivery_method = :sendgrid\n"
92
+ sendgrid_dev_settings = <<~RUBY
93
+ Rails.application.config.action_mailer.sendgrid_dev_settings = {
94
+ api_key: ENV['SENDGRID_API_KEY']
95
+ }
96
+ RUBY
97
+ application sendgrid_dev_settings, env: "development"
98
+ application "config.action_mailer.delivery_method = :sendgrid_dev", env: "development"
89
99
  end
90
100
 
91
101
  def aws_ses
@@ -0,0 +1,21 @@
1
+ require 'net/http'
2
+ require 'semantic'
3
+ require 'json'
4
+
5
+ class Recipes::Node < Rails::AppBuilder
6
+ def create
7
+ info "Using node version LTS #{version}"
8
+ create_file '.node-version', version, force: true
9
+ json_file = File.read(Pathname.new("package.json"))
10
+ js_package = JSON.parse(json_file)
11
+ js_package["engines"] = { "node" => "#{version}.x" }
12
+ json_string = JSON.pretty_generate(js_package)
13
+ create_file 'package.json', json_string, force: true
14
+ end
15
+
16
+ private
17
+
18
+ def version
19
+ Potassium::NODE_VERSION
20
+ end
21
+ end
@@ -5,8 +5,5 @@ class Recipes::Puma < Rails::AppBuilder
5
5
  end
6
6
 
7
7
  copy_file '../assets/config/puma.rb', 'config/puma.rb', force: true
8
-
9
- # Configure rack-timout
10
- application "Rack::Timeout.timeout = (ENV[\"RACK_TIMEOUT\"] || 10).to_i", env: "production"
11
8
  end
12
9
  end
@@ -4,23 +4,26 @@ class Recipes::RackCors < Rails::AppBuilder
4
4
  end
5
5
 
6
6
  def create
7
- gather_gem('rack-cors', '~> 0.4.0')
7
+ gather_gem('rack-cors', '~> 1.1')
8
+ recipe = self
8
9
  after(:gem_install) do
9
- rack_cors_config =
10
- <<~RUBY
11
- config.middleware.insert_before 0, Rack::Cors do
12
- allow do
13
- origins '*'
14
- resource '*',
15
- headers: :any,
16
- expose: ['X-Page', 'X-PageTotal'],
17
- methods: [:get, :post, :delete, :put, :options]
18
- end
19
- end
10
+ application recipe.rack_cors_config
11
+ end
12
+ end
20
13
 
21
- RUBY
14
+ def rack_cors_config
15
+ <<~RUBY
16
+ config.middleware.insert_before 0, Rack::Cors do
17
+ allow do
18
+ origins '*'
19
+ resource '/public/*', headers: :any, methods: :get
20
+ resource '/api/*',
21
+ headers: :any,
22
+ expose: ['X-Page', 'X-PageTotal'],
23
+ methods: [:get, :post, :patch, :put, :delete, :options]
24
+ end
25
+ end
22
26
 
23
- application rack_cors_config
24
- end
27
+ RUBY
25
28
  end
26
29
  end
@@ -1,9 +1,15 @@
1
1
  class Recipes::Rails < Rails::AppBuilder
2
2
  def create
3
3
  gather_gem("bootsnap", require: false)
4
+ gather_gems(:development) do
5
+ gather_gem("spring")
6
+ end
4
7
 
5
8
  environment 'config.force_ssl = true', env: 'production'
9
+ disable_automatic_nonce_generation
10
+ end
6
11
 
12
+ def disable_automatic_nonce_generation
7
13
  line = "Rails.application.config.content_security_policy_nonce_generator = \
8
14
  -> request { SecureRandom.base64(16) }"
9
15
  initializer = "config/initializers/content_security_policy.rb"
@@ -8,7 +8,7 @@ class Recipes::Schedule < Rails::AppBuilder
8
8
 
9
9
  def create
10
10
  if selected?(:schedule)
11
- gather_gem 'sidekiq-scheduler'
11
+ gather_gem 'sidekiq-scheduler', '>= 3.0.1'
12
12
  add_readme_section :internal_dependencies, :sidekiq_scheduler
13
13
  end
14
14
  template '../assets/sidekiq_scheduler.yml', 'config/sidekiq.yml', force: true
@@ -1,12 +1,30 @@
1
1
  class Recipes::Style < Rails::AppBuilder
2
2
  def create
3
- append_to_file ".gitignore", ".rubocop.yml\n"
4
- append_to_file ".gitignore", ".eslintrc.json\n"
5
- append_to_file ".gitignore", ".sscs-lint.yml\n"
3
+ add_linters
4
+ add_config_files
6
5
  add_readme_header :style_guide
7
6
  end
8
7
 
9
8
  def install
10
9
  create
11
10
  end
11
+
12
+ private
13
+
14
+ def add_linters
15
+ gather_gems(:development, :test) do
16
+ gather_gem 'rubocop', '~> 0.82.0'
17
+ gather_gem 'rubocop-performance'
18
+ gather_gem 'rubocop-rails'
19
+ gather_gem 'rubocop-rspec'
20
+ end
21
+ run 'bin/yarn add --dev stylelint eslint eslint-plugin-import'
22
+ run 'bin/yarn add --dev eslint-plugin-vue' if selected?(:front_end, :vue)
23
+ end
24
+
25
+ def add_config_files
26
+ copy_file '../assets/.rubocop.yml', '.rubocop.yml'
27
+ copy_file '../assets/.eslintrc.json', '.eslintrc.json'
28
+ copy_file '../assets/.stylelintrc.json', '.stylelintrc.json'
29
+ end
12
30
  end
@@ -0,0 +1,124 @@
1
+ class Recipes::VueAdmin < Rails::AppBuilder
2
+ def ask
3
+ if selected?(:admin_mode)
4
+ vue_admin = answer(:vue_admin) do
5
+ Ask.confirm "Do you want Vue support for ActiveAdmin?"
6
+ end
7
+ set(:vue_admin, vue_admin)
8
+ set(:front_end, :vue) if vue_admin
9
+ end
10
+ end
11
+
12
+ def create
13
+ recipe = self
14
+ if selected?(:vue_admin)
15
+ after(:admin_install) do
16
+ recipe.add_vue_admin
17
+ end
18
+ end
19
+ end
20
+
21
+ def install
22
+ active_admin = load_recipe(:admin)
23
+ if active_admin.installed?
24
+ add_vue_admin
25
+ info "VueAdmin installed"
26
+ else
27
+ info "VueAdmin can't be installed because Active Admin isn't installed."
28
+ end
29
+ end
30
+
31
+ def installed?
32
+ dir_exist?("app/assets/javascripts/admin")
33
+ end
34
+
35
+ def add_vue_admin
36
+ add_vue_component_library
37
+ add_component_integration
38
+ copy_file '../assets/active_admin/init_activeadmin_vue.rb',
39
+ 'config/initializers/init_activeadmin_vue.rb'
40
+ copy_file '../assets/active_admin/admin_application.js',
41
+ 'app/javascript/packs/admin_application.js',
42
+ force: true
43
+ empty_directory 'app/javascript/components'
44
+ copy_file '../assets/active_admin/admin-component.vue',
45
+ 'app/javascript/components/admin-component.vue',
46
+ force: true
47
+ end
48
+
49
+ def add_component_integration
50
+ line = "class CustomFooter < ActiveAdmin::Component"
51
+ initializer = "config/initializers/active_admin.rb"
52
+ gsub_file initializer, /(#{Regexp.escape(line)})/mi do |_match|
53
+ <<~HERE
54
+ require "vue_component.rb"
55
+ AUTO_BUILD_ELEMENTS=[:admin_component, :template, :slot]
56
+ component_creator(AUTO_BUILD_ELEMENTS)
57
+
58
+ #{line}
59
+ HERE
60
+ end
61
+ end
62
+
63
+ def add_vue_component_library
64
+ lib_component_path = "lib/vue_component.rb"
65
+ class_definitions =
66
+ <<~HERE
67
+ #{vue_component}
68
+ #{component_builder}
69
+ HERE
70
+ File.open(lib_component_path, "w") { |file| file.write(class_definitions) }
71
+ end
72
+
73
+ def vue_component
74
+ <<~HERE
75
+ class VueComponent < Arbre::Component
76
+ builder_method :root
77
+ def tag_name
78
+ :root
79
+ end
80
+
81
+ def initialize(*)
82
+ super
83
+ end
84
+
85
+ def build(attributes = {})
86
+ super(process_attributes(attributes))
87
+ end
88
+
89
+ def process_attributes(attributes)
90
+ vue_attributes = {}
91
+ attributes.each do |key, value|
92
+ dasherized_key = key.to_s.dasherize
93
+ if value.is_a?(String)
94
+ vue_attributes[dasherized_key] = value
95
+ elsif !dasherized_key.index(':').nil? && dasherized_key.index(':').zero?
96
+ vue_attributes[dasherized_key] = value.to_json
97
+ else
98
+ vue_attributes[":" + dasherized_key] = value.to_json
99
+ end
100
+ end
101
+ vue_attributes
102
+ end
103
+ end
104
+ HERE
105
+ end
106
+
107
+ def component_builder
108
+ <<~HERE
109
+ def component_creator(auto_build_elements)
110
+ auto_build_elements.each do |element|
111
+ as_string=element.to_s
112
+ camelized_element = as_string.camelize
113
+ Object.const_set(camelized_element,Class.new(VueComponent))
114
+ Object.const_get(camelized_element).class_eval do
115
+ builder_method as_string.to_sym
116
+ def tag_name
117
+ self.class.to_s.underscore
118
+ end
119
+ end
120
+ end
121
+ end
122
+ HERE
123
+ end
124
+ end