moodle2cc 0.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 (95) hide show
  1. data/.gitignore +20 -0
  2. data/Gemfile +4 -0
  3. data/Guardfile +14 -0
  4. data/LICENSE +661 -0
  5. data/README.md +38 -0
  6. data/Rakefile +12 -0
  7. data/bin/moodle2cc +6 -0
  8. data/lib/moodle2cc.rb +60 -0
  9. data/lib/moodle2cc/canvas/.converter.rb.swo +0 -0
  10. data/lib/moodle2cc/canvas/.wiki.rb.swo +0 -0
  11. data/lib/moodle2cc/canvas/assessment.rb +109 -0
  12. data/lib/moodle2cc/canvas/assignment.rb +108 -0
  13. data/lib/moodle2cc/canvas/converter.rb +28 -0
  14. data/lib/moodle2cc/canvas/course.rb +145 -0
  15. data/lib/moodle2cc/canvas/discussion_topic.rb +64 -0
  16. data/lib/moodle2cc/canvas/label.rb +8 -0
  17. data/lib/moodle2cc/canvas/question.rb +468 -0
  18. data/lib/moodle2cc/canvas/question_bank.rb +54 -0
  19. data/lib/moodle2cc/canvas/resource.rb +16 -0
  20. data/lib/moodle2cc/canvas/templates/assignment.html.erb +9 -0
  21. data/lib/moodle2cc/canvas/templates/syllabus.html.erb +9 -0
  22. data/lib/moodle2cc/canvas/templates/wiki_content.html.erb +10 -0
  23. data/lib/moodle2cc/canvas/web_content.rb +10 -0
  24. data/lib/moodle2cc/canvas/web_link.rb +17 -0
  25. data/lib/moodle2cc/canvas/wiki.rb +25 -0
  26. data/lib/moodle2cc/cc/assessment.rb +25 -0
  27. data/lib/moodle2cc/cc/assignment.rb +66 -0
  28. data/lib/moodle2cc/cc/cc_helper.rb +282 -0
  29. data/lib/moodle2cc/cc/converter.rb +136 -0
  30. data/lib/moodle2cc/cc/course.rb +16 -0
  31. data/lib/moodle2cc/cc/discussion_topic.rb +53 -0
  32. data/lib/moodle2cc/cc/label.rb +6 -0
  33. data/lib/moodle2cc/cc/question.rb +23 -0
  34. data/lib/moodle2cc/cc/resource.rb +30 -0
  35. data/lib/moodle2cc/cc/templates/assignment.html.erb +9 -0
  36. data/lib/moodle2cc/cc/templates/syllabus.html.erb +9 -0
  37. data/lib/moodle2cc/cc/templates/wiki_content.html.erb +10 -0
  38. data/lib/moodle2cc/cc/web_content.rb +40 -0
  39. data/lib/moodle2cc/cc/web_link.rb +53 -0
  40. data/lib/moodle2cc/cc/wiki.rb +72 -0
  41. data/lib/moodle2cc/cli.rb +14 -0
  42. data/lib/moodle2cc/error.rb +3 -0
  43. data/lib/moodle2cc/migrator.rb +23 -0
  44. data/lib/moodle2cc/moodle/backup.rb +38 -0
  45. data/lib/moodle2cc/moodle/course.rb +24 -0
  46. data/lib/moodle2cc/moodle/grade_item.rb +24 -0
  47. data/lib/moodle2cc/moodle/info.rb +8 -0
  48. data/lib/moodle2cc/moodle/mod.rb +127 -0
  49. data/lib/moodle2cc/moodle/question.rb +98 -0
  50. data/lib/moodle2cc/moodle/question_category.rb +17 -0
  51. data/lib/moodle2cc/moodle/section.rb +37 -0
  52. data/lib/moodle2cc/resource_factory.rb +32 -0
  53. data/lib/moodle2cc/version.rb +3 -0
  54. data/moodle2cc.gemspec +29 -0
  55. data/test/acceptance/migrator_test.rb +23 -0
  56. data/test/fixtures/cc.imscc +0 -0
  57. data/test/fixtures/moodle_backup/course_files/folder/test.txt +1 -0
  58. data/test/fixtures/moodle_backup/course_files/test.txt +1 -0
  59. data/test/fixtures/moodle_backup/moodle.xml +916 -0
  60. data/test/test_helper.rb +43 -0
  61. data/test/test_question_helper.rb +107 -0
  62. data/test/test_wiki_helper.rb +21 -0
  63. data/test/unit/canvas/assessment_test.rb +218 -0
  64. data/test/unit/canvas/assignment_test.rb +220 -0
  65. data/test/unit/canvas/converter_test.rb +159 -0
  66. data/test/unit/canvas/course_test.rb +176 -0
  67. data/test/unit/canvas/discussion_topic_test.rb +129 -0
  68. data/test/unit/canvas/label_test.rb +32 -0
  69. data/test/unit/canvas/question_bank_test.rb +73 -0
  70. data/test/unit/canvas/question_test.rb +824 -0
  71. data/test/unit/canvas/web_content_test.rb +37 -0
  72. data/test/unit/canvas/web_link_test.rb +48 -0
  73. data/test/unit/canvas/wiki_test.rb +74 -0
  74. data/test/unit/cc/assessment_test.rb +48 -0
  75. data/test/unit/cc/assignment_test.rb +67 -0
  76. data/test/unit/cc/cc_helper_test.rb +19 -0
  77. data/test/unit/cc/converter_test.rb +159 -0
  78. data/test/unit/cc/course_test.rb +36 -0
  79. data/test/unit/cc/discussion_topic_test.rb +83 -0
  80. data/test/unit/cc/label_test.rb +29 -0
  81. data/test/unit/cc/question_test.rb +49 -0
  82. data/test/unit/cc/web_content_test.rb +76 -0
  83. data/test/unit/cc/web_link_test.rb +79 -0
  84. data/test/unit/cc/wiki_test.rb +139 -0
  85. data/test/unit/migrator_test.rb +56 -0
  86. data/test/unit/moodle/backup_test.rb +30 -0
  87. data/test/unit/moodle/course_test.rb +57 -0
  88. data/test/unit/moodle/grade_item_test.rb +72 -0
  89. data/test/unit/moodle/info_test.rb +21 -0
  90. data/test/unit/moodle/mod_test.rb +301 -0
  91. data/test/unit/moodle/question_category_test.rb +34 -0
  92. data/test/unit/moodle/question_test.rb +185 -0
  93. data/test/unit/moodle/section_test.rb +75 -0
  94. data/test/unit/resource_factory_test.rb +119 -0
  95. metadata +342 -0
@@ -0,0 +1,14 @@
1
+ require 'thor'
2
+
3
+ module Moodle2CC
4
+ class CLI < Thor
5
+ desc "migrate MOODLE_BACKUP_ZIP EXPORT_DIR", "Migrates Moodle backup ZIP to IMS Common Cartridge package"
6
+ method_option :format, :default => 'cc'
7
+ method_options :format => :string
8
+ def migrate(source, destination)
9
+ migrator = Moodle2CC::Migrator.new source, destination, options
10
+ migrator.migrate
11
+ puts "#{source} converted to #{migrator.imscc_path}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Moodle2CC
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,23 @@
1
+ module Moodle2CC
2
+ class Migrator
3
+ def initialize(source, destination, options={})
4
+ @source = source
5
+ @destination = destination
6
+ @format = options['format'] || 'cc'
7
+ raise(Moodle2CC::Error, "'#{@source}' does not exist") unless File.exists?(@source)
8
+ raise(Moodle2CC::Error, "'#{@destination}' is not a directory") unless File.directory?(@destination)
9
+ raise(Moodle2CC::Error, "'#{@format}' is not a valid format. Please use 'cc' or 'canvas'.") unless ['cc', 'canvas'].include?(@format)
10
+ @converter_class = @format == 'cc' ? Moodle2CC::CC::Converter : Moodle2CC::Canvas::Converter
11
+ end
12
+
13
+ def migrate
14
+ backup = Moodle2CC::Moodle::Backup.read @source
15
+ @converter = @converter_class.new backup, @destination
16
+ @converter.convert
17
+ end
18
+
19
+ def imscc_path
20
+ @converter.imscc_path
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ require 'zip/zipfilesystem'
2
+
3
+ module Moodle2CC::Moodle
4
+ class Backup
5
+ include HappyMapper
6
+
7
+ attr_accessor :backup_file, :files
8
+
9
+ tag 'MOODLE_BACKUP'
10
+ has_one :info, Info
11
+ has_one :course, Course
12
+
13
+ def self.read(backup_file)
14
+ Zip::ZipFile.open(backup_file) do |zipfile|
15
+ xml = zipfile.file.read("moodle.xml")
16
+ backup = parse xml
17
+ backup.backup_file = backup_file
18
+ backup.files = zipfile.entries.select { |e| e.name =~ /^course_files/ && !e.directory? }.
19
+ map { |e| e.name.sub('course_files/', '') }.sort
20
+ backup
21
+ end
22
+ end
23
+
24
+ def copy_files_to(dir)
25
+ Zip::ZipFile.open(@backup_file) do |zipfile|
26
+ @files.each do |file|
27
+ zipfile.file.open("course_files/#{file}") do |zip|
28
+ destination_file = File.join(dir, file)
29
+ FileUtils.mkdir_p(File.dirname(destination_file))
30
+ File.open(destination_file, 'w') do |f|
31
+ f.write zip.read
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ module Moodle2CC::Moodle
2
+ class Course
3
+ include HappyMapper
4
+
5
+ tag 'COURSE'
6
+ element :id, Integer, :tag => 'HEADER/ID'
7
+ element :fullname, String, :tag => 'HEADER/FULLNAME'
8
+ element :shortname, String, :tag => 'HEADER/SHORTNAME'
9
+ element :startdate, Integer, :tag => 'HEADER/STARTDATE'
10
+ element :format, String, :tag => 'HEADER/FORMAT'
11
+ element :visible, Boolean, :tag => 'HEADER/VISIBLE'
12
+ has_many :sections, Section
13
+ has_many :mods, Mod
14
+ has_many :grade_items, GradeItem
15
+ has_many :question_categories, QuestionCategory
16
+
17
+ after_parse do |course|
18
+ course.sections.each { |section| section.course = course }
19
+ course.mods.each { |mod| mod.course = course }
20
+ course.grade_items.each { |grade_item| grade_item.course = course }
21
+ course.question_categories.each { |question_category| question_category.course = course }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Moodle2CC::Moodle
2
+ class GradeItem
3
+ include HappyMapper
4
+
5
+ attr_accessor :course
6
+
7
+ tag 'GRADEBOOK/GRADE_ITEMS/GRADE_ITEM'
8
+ element :id, Integer, :tag => 'ID'
9
+ element :category_id, Integer, :tag => 'CATEGORYID'
10
+ element :item_name, String, :tag => 'ITEMNAME'
11
+ element :item_type, String, :tag => 'ITEMTYPE'
12
+ element :item_module, String, :tag => 'ITEMMODULE'
13
+ element :item_instance, Integer, :tag => 'ITEMINSTANCE'
14
+ element :item_number, Integer, :tag => 'ITEMNUMBER'
15
+ element :grade_type, Integer, :tag => 'GRADETYPE'
16
+ element :grade_max, Float, :tag => 'GRADEMAX'
17
+ element :grade_min, Float, :tag => 'GRADEMIN'
18
+ element :scale_id, Integer, :tag => 'SCALEID'
19
+
20
+ def instance
21
+ course.mods.find { |mod| mod.id == item_instance }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ module Moodle2CC::Moodle
2
+ class Info
3
+ include HappyMapper
4
+
5
+ tag 'INFO'
6
+ element :name, String, :tag => 'NAME'
7
+ end
8
+ end
@@ -0,0 +1,127 @@
1
+ module Moodle2CC::Moodle
2
+ class Mod
3
+ include HappyMapper
4
+
5
+ attr_accessor :course
6
+
7
+ class QuestionInstance
8
+ include HappyMapper
9
+
10
+ attr_accessor :mod
11
+
12
+ tag 'QUESTION_INSTANCES/QUESTION_INSTANCE'
13
+ element :id, Integer, :tag => 'ID'
14
+ element :question_id, Integer, :tag => 'QUESTION'
15
+ element :grade, Integer, :tag => 'GRADE'
16
+
17
+ def question
18
+ mod.course.question_categories.map do |qc|
19
+ qc.questions.find { |q| q.id == question_id }
20
+ end.compact.first
21
+ end
22
+ end
23
+
24
+ class Page
25
+ include HappyMapper
26
+
27
+ tag 'ENTRIES/ENTRY/PAGES/PAGE'
28
+ element :page_name, String, :tag => 'PAGENAME'
29
+ element :version, Integer, :tag => 'VERSION'
30
+ element :content, String, :tag => 'CONTENT'
31
+ end
32
+
33
+ class Option
34
+ include HappyMapper
35
+
36
+ tag 'OPTIONS/OPTION'
37
+ element :id, Integer, :tag => 'ID'
38
+ element :text, String, :tag => 'TEXT'
39
+ end
40
+
41
+ tag 'MODULES/MOD'
42
+ element :id, Integer, :tag => 'ID'
43
+ element :var1, Integer, :tag => 'VAR1'
44
+ element :var2, Integer, :tag => 'VAR2'
45
+ element :var3, Integer, :tag => 'VAR3'
46
+ element :var4, Integer, :tag => 'VAR4'
47
+ element :var5, Integer, :tag => 'VAR5'
48
+ element :mod_type, String, :tag => 'MODTYPE'
49
+ element :type, String, :tag => 'TYPE'
50
+ element :name, String, :tag => 'NAME'
51
+ element :description, String, :tag => 'DESCRIPTION'
52
+ element :summary, String, :tag => 'SUMMARY'
53
+ element :alltext, String, :tag => 'ALLTEXT'
54
+ element :text, String, :tag => 'TEXT'
55
+ element :content, String, :tag => 'CONTENT'
56
+ element :assignment_type, String, :tag => 'ASSIGNMENTTYPE'
57
+ element :reference, String, :tag => 'REFERENCE'
58
+ element :intro, String, :tag => 'INTRO'
59
+ element :resubmit, Boolean, :tag => 'RESUBMIT'
60
+ element :prevent_late, Boolean, :tag => 'PREVENTLATE'
61
+ element :grade, Integer, :tag => 'GRADE'
62
+ element :number_of_attachments, Integer, :tag => 'NATTACHMENTS'
63
+ element :number_of_student_assessments, Integer, :tag => 'NSASSESSMENTS'
64
+ element :anonymous, Boolean, :tag => 'ANONYMOUS'
65
+ element :submission_start, Integer, :tag => 'SUBMISSIONSTART'
66
+ element :submission_end, Integer, :tag => 'SUBMISSIONEND'
67
+ element :assessment_start, Integer, :tag => 'ASSESSMENTSTART'
68
+ element :assessment_end, Integer, :tag => 'ASSESSMENTEND'
69
+ element :time_due, Integer, :tag => 'TIMEDUE'
70
+ element :time_available, Integer, :tag => 'TIMEAVAILABLE'
71
+ element :time_open, Integer, :tag => 'TIMEOPEN'
72
+ element :time_close, Integer, :tag => 'TIMECLOSE'
73
+ element :time_limit, Integer, :tag => 'TIMELIMIT'
74
+ element :attempts_number, Integer, :tag => 'ATTEMPTS_NUMBER'
75
+ element :grade_method, Integer, :tag => 'GRADEMETHOD'
76
+ element :password, String, :tag => 'PASSWORD'
77
+ element :subnet, String, :tag => 'SUBNET'
78
+ element :shuffle_answers, Boolean, :tag => 'SHUFFLEANSWERS'
79
+ element :page_name, String, :tag => 'PAGENAME'
80
+ has_many :question_instances, QuestionInstance
81
+ has_many :questions, Question
82
+ has_many :pages, Page
83
+ has_many :options, Option
84
+
85
+ after_parse do |mod|
86
+ mod.question_instances.each { |question_instance| question_instance.mod = mod }
87
+ end
88
+
89
+ def section_mod
90
+ course.sections.map { |section| section.mods.find { |mod| mod.instance_id == id && mod.mod_type == mod_type } }.compact.first
91
+ end
92
+
93
+ def grade_item
94
+ course.grade_items.find { |grade_item| grade_item.item_instance == id }
95
+ end
96
+
97
+ def questions
98
+ if mod_type == 'questionnaire'
99
+ @questions.sort! { |a,b| a.position <=> b.position }
100
+ return @questions
101
+ end
102
+ if mod_type == 'choice'
103
+ question = Question.new
104
+ question.id = "choice_question_#{@id}"
105
+ question.name = @name
106
+ question.text = @text
107
+ question.type = @mod_type
108
+ question.grade = 1
109
+ question.answers = []
110
+ @options.each do |option|
111
+ answer = Question::Answer.new
112
+ answer.id = option.id
113
+ answer.text = option.text
114
+ question.answers << answer
115
+ end
116
+ @questions = [question]
117
+ else
118
+ @questions = question_instances.map do |qi|
119
+ question = qi.question
120
+ question.grade = qi.grade
121
+ question.instance_id = qi.id
122
+ question
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,98 @@
1
+ module Moodle2CC::Moodle
2
+ class Question
3
+ include HappyMapper
4
+
5
+ attr_accessor :question_category, :grade, :instance_id
6
+
7
+ class Answer
8
+ include HappyMapper
9
+
10
+ tag 'ANSWERS/ANSWER'
11
+ element :id, Integer, :tag => 'ID'
12
+ element :text, String, :tag => 'ANSWER_TEXT'
13
+ element :fraction, Float, :tag => 'FRACTION'
14
+ element :feedback, String, :tag => 'FEEDBACK'
15
+ end
16
+
17
+ class Calculation
18
+ include HappyMapper
19
+
20
+ class DatasetDefinition
21
+ include HappyMapper
22
+
23
+ class DatasetItem
24
+ include HappyMapper
25
+
26
+ tag 'DATASET_ITEMS/DATASET_ITEM'
27
+ element :number, Integer, :tag => 'NUMBER'
28
+ element :value, Float, :tag => 'VALUE'
29
+ end
30
+
31
+ tag 'DATASET_DEFINITIONS/DATASET_DEFINITION'
32
+ element :name, String, :tag => 'NAME'
33
+ element :options, String, :tag => 'OPTIONS'
34
+ has_many :dataset_items, DatasetItem
35
+ end
36
+
37
+ tag 'CALCULATED'
38
+ element :answer_id, Integer, :tag => 'ANSWER'
39
+ element :tolerance, Float, :tag => 'TOLERANCE'
40
+ element :correct_answer_length, Integer, :tag => 'CORRECTANSWERLENGTH'
41
+ element :correct_answer_format, Integer, :tag => 'CORRECTANSWERFORMAT'
42
+ has_many :dataset_definitions, DatasetDefinition
43
+ end
44
+
45
+ class Match
46
+ include HappyMapper
47
+
48
+ tag 'MATCHS/MATCH'
49
+ element :id, Integer, :tag => 'ID'
50
+ element :code, Integer, :tag => 'CODE'
51
+ element :question_text, String, :tag => 'QUESTIONTEXT'
52
+ element :answer_text, String, :tag => 'ANSWERTEXT'
53
+ end
54
+
55
+ class Numerical
56
+ include HappyMapper
57
+
58
+ tag 'NUMERICAL'
59
+ element :answer_id, Integer, :tag => 'ANSWER'
60
+ element :tolerance, Integer, :tag => 'TOLERANCE'
61
+ end
62
+
63
+ class Choice
64
+ include HappyMapper
65
+
66
+ tag 'QUESTION_CHOICE'
67
+ element :id, Integer, :tag => 'ID'
68
+ element :content, String, :tag => 'CONTENT'
69
+ end
70
+
71
+ tag 'QUESTION'
72
+ element :id, Integer, :tag => 'ID'
73
+ element :name, String, :tag => 'NAME'
74
+ element :text, String, :tag => 'QUESTIONTEXT'
75
+ element :content, String, :tag => 'CONTENT'
76
+ element :length, Integer, :tag => 'LENGTH'
77
+ element :general_feedback, String, :tag => 'GENERALFEEDBACK'
78
+ element :default_grade, Integer, :tag => 'DEFAULTGRADE'
79
+ element :position, Integer, :tag => 'POSITION'
80
+ element :type, String, :tag => 'QTYPE'
81
+ element :type_id, Integer, :tag => 'TYPE_ID'
82
+ has_many :numericals, Numerical
83
+ has_many :answers, Answer
84
+ has_many :calculations, Calculation
85
+ has_many :matches, Match
86
+ has_many :choices, Choice
87
+
88
+ def instance
89
+ question_category.course.mods.select do |mod|
90
+ mod.mod_type == 'quiz'
91
+ end.map do |mod|
92
+ mod.question_instances.find do |qi|
93
+ qi.question_id == id
94
+ end
95
+ end.compact.first
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,17 @@
1
+ module Moodle2CC::Moodle
2
+ class QuestionCategory
3
+ include HappyMapper
4
+
5
+ attr_accessor :course
6
+
7
+ tag 'QUESTION_CATEGORIES/QUESTION_CATEGORY'
8
+ element :id, Integer, :tag => 'ID'
9
+ element :name, String, :tag => 'NAME'
10
+ element :info, String, :tag => 'INFO'
11
+ has_many :questions, Question
12
+
13
+ after_parse do |qc|
14
+ qc.questions.each { |q| q.question_category = qc }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ module Moodle2CC::Moodle
2
+ class Section
3
+ include HappyMapper
4
+
5
+ attr_accessor :course
6
+
7
+ class Mod
8
+ include HappyMapper
9
+
10
+ attr_accessor :section
11
+
12
+ tag 'MODS/MOD'
13
+
14
+ element :id, Integer, :tag => 'ID'
15
+ element :mod_type, String, :tag => 'TYPE'
16
+ element :instance_id, Integer, :tag => 'INSTANCE'
17
+ element :added, Integer, :tag => 'ADDED'
18
+ element :indent, Integer, :tag => 'INDENT'
19
+ element :visible, Boolean, :tag => 'VISIBLE'
20
+
21
+ def instance
22
+ section.course.mods.find { |mod| mod.id == instance_id && mod.mod_type == mod_type }
23
+ end
24
+ end
25
+
26
+ tag 'SECTIONS/SECTION'
27
+ element :id, Integer, :tag => 'ID'
28
+ element :number, Integer, :tag => 'NUMBER'
29
+ element :summary, String, :tag => 'SUMMARY'
30
+ element :visible, Boolean, :tag => 'VISIBLE'
31
+ has_many :mods, Mod
32
+
33
+ after_parse do |section|
34
+ section.mods.each { |mod| mod.section = section }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ class Moodle2CC::ResourceFactory
2
+
3
+ def initialize(namespace)
4
+ @namespace = namespace
5
+ end
6
+
7
+ def get_resource_from_mod(mod, position=0)
8
+ case mod.mod_type
9
+ when 'assignment', 'workshop'
10
+ @namespace.const_get(:Assignment).new(mod, position)
11
+ when 'resource'
12
+ if mod.type == 'file'
13
+ @namespace.const_get(:WebLink).new(mod)
14
+ elsif mod.type == 'html'
15
+ @namespace.const_get(:WebContent).new(mod)
16
+ end
17
+ when 'forum'
18
+ @namespace.const_get(:DiscussionTopic).new(mod, position)
19
+ when 'quiz', 'questionnaire', 'choice'
20
+ @namespace.const_get(:Assessment).new(mod, position)
21
+ when 'wiki'
22
+ @namespace.const_get(:Wiki).new(mod)
23
+ when 'label'
24
+ html = Nokogiri::HTML(mod.content)
25
+ if html.text == mod.content && mod.content.length < 50 # label doesn't contain HTML
26
+ @namespace.const_get(:Label).new(mod)
27
+ else
28
+ @namespace.const_get(:WebContent).new(mod)
29
+ end
30
+ end
31
+ end
32
+ end