mumuki-domain 6.1.5 → 6.2.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.
- checksums.yaml +5 -5
- data/Rakefile +35 -0
- data/app/models/assignment.rb +2 -5
- data/app/models/book.rb +6 -2
- data/app/models/concerns/syncable.rb +94 -0
- data/app/models/concerns/with_slug.rb +7 -7
- data/app/models/content.rb +1 -0
- data/app/models/course.rb +33 -4
- data/app/models/exam.rb +5 -1
- data/app/models/exercise.rb +8 -1
- data/app/models/guide.rb +3 -3
- data/app/models/invitation.rb +36 -11
- data/app/models/language.rb +5 -4
- data/app/models/message.rb +1 -1
- data/app/models/organization.rb +10 -5
- data/app/models/topic.rb +6 -1
- data/app/models/user.rb +17 -12
- data/db/migrate/20160701195105_add_login_methods_to_organization.rb +1 -1
- data/db/migrate/20181210131824_convert_course_invitation_into_fk.rb +6 -0
- data/lib/mumuki/domain/factories.rb +17 -0
- data/lib/mumuki/domain/factories/api_client_factory.rb +18 -0
- data/lib/mumuki/domain/factories/assignments_factory.rb +7 -0
- data/lib/mumuki/domain/factories/book_factory.rb +7 -0
- data/lib/mumuki/domain/factories/chapter_factory.rb +16 -0
- data/lib/mumuki/domain/factories/complement_factory.rb +6 -0
- data/lib/mumuki/domain/factories/course_factory.rb +9 -0
- data/lib/mumuki/domain/factories/discussion_factory.rb +8 -0
- data/lib/mumuki/domain/factories/exam_factory.rb +9 -0
- data/lib/mumuki/domain/factories/exercise_factory.rb +73 -0
- data/lib/mumuki/domain/factories/guide_factory.rb +30 -0
- data/lib/mumuki/domain/factories/invitation_factory.rb +7 -0
- data/lib/mumuki/domain/factories/lesson_factory.rb +7 -0
- data/lib/mumuki/domain/factories/login_settings_factory.rb +5 -0
- data/lib/mumuki/domain/factories/message_factory.rb +11 -0
- data/lib/mumuki/domain/factories/organization_factory.rb +26 -0
- data/lib/mumuki/domain/factories/topic_factory.rb +9 -0
- data/lib/mumuki/domain/factories/user_factory.rb +8 -0
- data/lib/mumuki/domain/locales/submission.en.yml +4 -0
- data/lib/mumuki/domain/locales/submission.es.yml +4 -0
- data/lib/mumuki/domain/locales/submission.pt.yml +4 -0
- data/lib/mumuki/domain/submission/console_submission.rb +1 -1
- data/lib/mumuki/domain/version.rb +1 -1
- metadata +61 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ee36809b582014389c467d826cf8b882328db5ca64148dc86c61934de774cbb2
|
|
4
|
+
data.tar.gz: a015be5b71a6cb6be0b3b7f40704e5c7c0ff8391fc8e45d08db51420c71d9522
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9281ce11cfab651b7a7c2be4f4d528a7ac29278c19eff71116adcb376c21ec6059bb874412599398da47cbe4702f2ec0a8a55865cbe80decf21df85402ca442b
|
|
7
|
+
data.tar.gz: 1183e2aeed8d70f425f1cc9751f95f987bfd7a8d88d0268bc6fe83983725f4f45b8ef03095b1d666b773e8964ad234355b7de4ef11eb4ca4d229764028b20164
|
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'Mumuki::Domain'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
|
18
|
+
|
|
19
|
+
load 'rails/tasks/engine.rake'
|
|
20
|
+
load 'rails/tasks/statistics.rake'
|
|
21
|
+
|
|
22
|
+
require 'bundler/gem_tasks'
|
|
23
|
+
|
|
24
|
+
require 'rspec/core'
|
|
25
|
+
require 'rspec/core/rake_task'
|
|
26
|
+
|
|
27
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
|
28
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
|
29
|
+
|
|
30
|
+
task default: :spec
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
data/app/models/assignment.rb
CHANGED
|
@@ -49,7 +49,7 @@ class Assignment < ApplicationRecord
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def notify!
|
|
52
|
-
Mumukit::Nuntius.notify! 'submissions', to_resource_h unless Organization.
|
|
52
|
+
Mumukit::Nuntius.notify! 'submissions', to_resource_h unless Organization.silenced?
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def notify_to_accessible_organizations!
|
|
@@ -177,10 +177,7 @@ class Assignment < ApplicationRecord
|
|
|
177
177
|
end
|
|
178
178
|
|
|
179
179
|
def files
|
|
180
|
-
|
|
181
|
-
.directives_sections
|
|
182
|
-
.split_sections(current_content)
|
|
183
|
-
.map { |name, content| Mumuki::Domain::File.new name, content }
|
|
180
|
+
exercise.files_for(current_content)
|
|
184
181
|
end
|
|
185
182
|
|
|
186
183
|
private
|
data/app/models/book.rb
CHANGED
|
@@ -22,14 +22,14 @@ class Book < Content
|
|
|
22
22
|
user.try(:last_lesson)|| first_lesson
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
after_save :reindex_usages!
|
|
26
|
+
|
|
25
27
|
def import_from_resource_h!(resource_h)
|
|
26
28
|
self.assign_attributes resource_h.except(:chapters, :complements, :id, :description)
|
|
27
29
|
self.description = resource_h[:description]&.squeeze(' ')
|
|
28
30
|
|
|
29
31
|
rebuild! resource_h[:chapters].map { |it| Topic.find_by!(slug: it).as_chapter_of(self) }
|
|
30
32
|
rebuild_complements! resource_h[:complements].to_a.map { |it| Guide.find_by(slug: it)&.as_complement_of(self) }.compact
|
|
31
|
-
|
|
32
|
-
Organization.where(book: self).each { |org| org.reindex_usages! }
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def to_resource_h
|
|
@@ -51,6 +51,10 @@ class Book < Content
|
|
|
51
51
|
[chapters, complements].flatten.each { |item| item.index_usage! organization }
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
def reindex_usages!
|
|
55
|
+
Organization.where(book: self).each &:reindex_usages!
|
|
56
|
+
end
|
|
57
|
+
|
|
54
58
|
## Forking
|
|
55
59
|
|
|
56
60
|
def fork_to!(organization, syncer)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Mixin for objects that may
|
|
2
|
+
# be treated as `Mumukit::Sync` resources
|
|
3
|
+
#
|
|
4
|
+
# `Syncable` objects also get `Mumukit::Platform::Notifiable` by free
|
|
5
|
+
module Syncable
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
include Mumukit::Platform::Notifiable
|
|
8
|
+
|
|
9
|
+
# Generates a sync key
|
|
10
|
+
#
|
|
11
|
+
# :warning: This method is required by `Mumukit::Sync::Syncer#import!`
|
|
12
|
+
def sync_key
|
|
13
|
+
Mumukit::Sync.key sync_key_kind, sync_key_id
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Updates this object with the provided
|
|
17
|
+
# resource-hash. Fields not present on it are not modified
|
|
18
|
+
#
|
|
19
|
+
# :warning: This method is required by
|
|
20
|
+
#
|
|
21
|
+
# * `Mumukit::Sync::Syncer#import!`
|
|
22
|
+
# * `Mumukit::Sync::Syncer#import_all!`
|
|
23
|
+
# * `Mumukit::Sync::Syncer#locate_and_import!`
|
|
24
|
+
required :import_from_resource_h!
|
|
25
|
+
|
|
26
|
+
# Generates a resource-hash representation
|
|
27
|
+
#
|
|
28
|
+
# :warning: This method is required by
|
|
29
|
+
#
|
|
30
|
+
# * `Mumukit::Sync::Syncer#export!`
|
|
31
|
+
# * `Mumukit::Sync::Syncer#locate_and_export!`
|
|
32
|
+
required :to_resource_h
|
|
33
|
+
|
|
34
|
+
# :warning: This method is required by `Mumukit::Platform::Notifiable#notify!`
|
|
35
|
+
def platform_class_name
|
|
36
|
+
sync_key_kind.as_module_name
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def sync_key_kind
|
|
42
|
+
self.class
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def sync_key_id
|
|
46
|
+
self[self.class.sync_key_id_field]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class_methods do
|
|
50
|
+
# The name of the field used as id with `Mumukit::Sync#key`
|
|
51
|
+
required :sync_key_id_field
|
|
52
|
+
|
|
53
|
+
# :warning: This method is required by
|
|
54
|
+
#
|
|
55
|
+
# * `Mumukit::Sync::Syncer#import_all!`
|
|
56
|
+
# * `Mumukit::Sync::Syncer#locate_and_import!`
|
|
57
|
+
# * `Mumukit::Sync::Syncer#locate_and_export!`
|
|
58
|
+
def locate_resource(sync_key_id)
|
|
59
|
+
find_or_initialize_by sync_key_id_field => sync_key_id
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# `locate_resource` is a helpful method that can be used
|
|
63
|
+
# outside the `Mumukit::Sync` context, thus this mixin provide
|
|
64
|
+
# a shorter alias
|
|
65
|
+
alias_method :locate, :locate_resource
|
|
66
|
+
|
|
67
|
+
# `locate!` is similar to `locate`, but fails instead of creating a
|
|
68
|
+
# a new object when not found
|
|
69
|
+
#
|
|
70
|
+
# This method is not required by `Mumukit::Sync`
|
|
71
|
+
def locate!(sync_key_id)
|
|
72
|
+
find_by! sync_key_id_field => sync_key_id
|
|
73
|
+
rescue ActiveRecord::RecordNotFound
|
|
74
|
+
raise ActiveRecord::RecordNotFound, "Coudn't find #{self.name} with #{sync_key_id_field}: #{sync_key_id}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Locates and imports a resource, extracting
|
|
78
|
+
# its key directly from the resource-hash.
|
|
79
|
+
#
|
|
80
|
+
# This class-message is not required by `Mumukit::Sync` but it is exposed
|
|
81
|
+
# for convenience when needing to import resources for whom we already
|
|
82
|
+
# have a resource-hash representation and we don't need to fetch
|
|
83
|
+
# them from a `Mumukit::Sync::Store`.
|
|
84
|
+
def import_from_resource_h!(resource_h)
|
|
85
|
+
locate_resource(extract_sync_key_id(resource_h)).tap do |it|
|
|
86
|
+
it.import_from_resource_h! resource_h
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def extract_sync_key_id(resource_h)
|
|
91
|
+
resource_h[sync_key_id_field]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -12,12 +12,6 @@ module WithSlug
|
|
|
12
12
|
{organization: org, repository: repo}
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
## Resource Protocol
|
|
16
|
-
|
|
17
|
-
def sync_key
|
|
18
|
-
Mumukit::Sync.key self.class.name.underscore.to_sym, slug
|
|
19
|
-
end
|
|
20
|
-
|
|
21
15
|
## Copy and Rebase
|
|
22
16
|
|
|
23
17
|
def rebase!(organization)
|
|
@@ -30,7 +24,7 @@ module WithSlug
|
|
|
30
24
|
|
|
31
25
|
## Filtering
|
|
32
26
|
|
|
33
|
-
|
|
27
|
+
class_methods do
|
|
34
28
|
|
|
35
29
|
def allowed(permissions)
|
|
36
30
|
all.select { |it| permissions&.writer?(it.slug) }
|
|
@@ -45,6 +39,12 @@ module WithSlug
|
|
|
45
39
|
find_by!(slug: "#{args[:organization]}/#{args[:repository]}")
|
|
46
40
|
end
|
|
47
41
|
|
|
42
|
+
## Resource Protocol
|
|
43
|
+
|
|
44
|
+
def sync_key_id_field
|
|
45
|
+
:slug
|
|
46
|
+
end
|
|
47
|
+
|
|
48
48
|
def locate_resource(key)
|
|
49
49
|
find_or_initialize_by(slug: key)
|
|
50
50
|
end
|
data/app/models/content.rb
CHANGED
data/app/models/course.rb
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
class Course < ApplicationRecord
|
|
2
|
+
include Syncable
|
|
2
3
|
include Mumukit::Platform::Course::Helpers
|
|
3
4
|
|
|
4
5
|
validates_presence_of :slug, :shifts, :code, :days, :period, :description, :organization_id
|
|
5
6
|
validates_uniqueness_of :slug
|
|
6
7
|
belongs_to :organization
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
has_many :invitations
|
|
10
|
+
|
|
11
|
+
alias_attribute :name, :code
|
|
12
|
+
|
|
13
|
+
def current_invitation
|
|
14
|
+
invitations.where('expiration_date > ?', Time.now).take
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def import_from_resource_h!(resource_h)
|
|
18
|
+
update! Mumukit::Platform::Course::Helpers.slice_platform_json(resource_h)
|
|
11
19
|
end
|
|
12
20
|
|
|
13
21
|
def slug=(slug)
|
|
@@ -15,6 +23,27 @@ class Course < ApplicationRecord
|
|
|
15
23
|
|
|
16
24
|
self[:slug] = slug
|
|
17
25
|
self[:code] = s.course
|
|
18
|
-
self[:organization_id] = Organization.
|
|
26
|
+
self[:organization_id] = Organization.locate!(s.organization).id
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def invite!(expiration_date)
|
|
30
|
+
if closed?
|
|
31
|
+
generate_invitation! expiration_date
|
|
32
|
+
else
|
|
33
|
+
current_invitation
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def closed?
|
|
38
|
+
current_invitation.blank? || current_invitation.expired?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def generate_invitation!(expiration_date)
|
|
42
|
+
invitation = invitations.build expiration_date: expiration_date, course: self
|
|
43
|
+
invitation.save_and_notify!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.sync_key_id_field
|
|
47
|
+
:slug
|
|
19
48
|
end
|
|
20
49
|
end
|
data/app/models/exam.rb
CHANGED
|
@@ -11,6 +11,7 @@ class Exam < ApplicationRecord
|
|
|
11
11
|
has_many :users, through: :authorizations
|
|
12
12
|
|
|
13
13
|
after_destroy { |record| Usage.destroy_usages_for record }
|
|
14
|
+
after_save :reindex_usages!
|
|
14
15
|
|
|
15
16
|
include TerminalNavigation
|
|
16
17
|
|
|
@@ -100,6 +101,10 @@ class Exam < ApplicationRecord
|
|
|
100
101
|
authorizations.all_except(authorizations_for(users)).destroy_all
|
|
101
102
|
end
|
|
102
103
|
|
|
104
|
+
def reindex_usages!
|
|
105
|
+
index_usage! organization
|
|
106
|
+
end
|
|
107
|
+
|
|
103
108
|
def self.import_from_resource_h!(json)
|
|
104
109
|
exam_data = json.with_indifferent_access
|
|
105
110
|
organization = Organization.find_by!(name: exam_data[:organization])
|
|
@@ -108,7 +113,6 @@ class Exam < ApplicationRecord
|
|
|
108
113
|
remove_previous_version exam_data[:eid], exam_data[:guide_id]
|
|
109
114
|
exam = where(classroom_id: exam_data[:eid]).update_or_create!(whitelist_attributes(exam_data))
|
|
110
115
|
exam.process_users exam_data[:users]
|
|
111
|
-
exam.index_usage! organization
|
|
112
116
|
exam
|
|
113
117
|
end
|
|
114
118
|
|
data/app/models/exercise.rb
CHANGED
|
@@ -132,7 +132,7 @@ class Exercise < ApplicationRecord
|
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
def reclassify!(type)
|
|
135
|
-
update!(type:
|
|
135
|
+
update!(type: type)
|
|
136
136
|
Exercise.find(id)
|
|
137
137
|
end
|
|
138
138
|
|
|
@@ -183,6 +183,13 @@ class Exercise < ApplicationRecord
|
|
|
183
183
|
navigable_parent.limited_for?(self)
|
|
184
184
|
end
|
|
185
185
|
|
|
186
|
+
def files_for(current_content)
|
|
187
|
+
language
|
|
188
|
+
.directives_sections
|
|
189
|
+
.split_sections(current_content)
|
|
190
|
+
.map { |name, content| Mumuki::Domain::File.new name, content }
|
|
191
|
+
end
|
|
192
|
+
|
|
186
193
|
private
|
|
187
194
|
|
|
188
195
|
def evaluation_class
|
data/app/models/guide.rb
CHANGED
|
@@ -58,7 +58,7 @@ class Guide < Content
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def import_from_resource_h!(resource_h)
|
|
61
|
-
self.assign_attributes whitelist_attributes(resource_h
|
|
61
|
+
self.assign_attributes whitelist_attributes(resource_h)
|
|
62
62
|
self.language = Language.for_name(resource_h.dig(:language, :name))
|
|
63
63
|
self.save!
|
|
64
64
|
|
|
@@ -67,8 +67,8 @@ class Guide < Content
|
|
|
67
67
|
exercise_type = e[:type] || 'problem'
|
|
68
68
|
|
|
69
69
|
exercise = exercise ?
|
|
70
|
-
exercise.ensure_type!(exercise_type) :
|
|
71
|
-
|
|
70
|
+
exercise.ensure_type!(exercise_type.as_module_name) :
|
|
71
|
+
exercise_type.as_module.new(guide_id: self.id, bibliotheca_id: e[:id])
|
|
72
72
|
|
|
73
73
|
exercise.import_from_resource_h! (i+1), e
|
|
74
74
|
end
|
data/app/models/invitation.rb
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
class Invitation < ApplicationRecord
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
include Syncable
|
|
3
|
+
|
|
4
|
+
belongs_to :course
|
|
5
|
+
validates_uniqueness_of :code
|
|
6
|
+
|
|
7
|
+
defaults do
|
|
8
|
+
self.code ||= self.class.generate_code
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def
|
|
12
|
-
|
|
11
|
+
def import_from_resource_h!(json)
|
|
12
|
+
update! json.merge(course: Course.locate!(json[:course]))
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def organization
|
|
16
|
-
|
|
16
|
+
course.organization
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def course_slug
|
|
20
|
+
course.slug
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
def navigable_name
|
|
20
24
|
I18n.t(:invitation_for, course: course_name)
|
|
21
25
|
end
|
|
22
26
|
|
|
27
|
+
def to_resource_h
|
|
28
|
+
{ code: code, course: course_slug, expiration_date: expiration_date }
|
|
29
|
+
end
|
|
30
|
+
|
|
23
31
|
def navigation_end?
|
|
24
32
|
true
|
|
25
33
|
end
|
|
@@ -28,9 +36,26 @@ class Invitation < ApplicationRecord
|
|
|
28
36
|
code
|
|
29
37
|
end
|
|
30
38
|
|
|
39
|
+
def expired?
|
|
40
|
+
Time.now > expiration_date
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def unexpired
|
|
44
|
+
raise RecordNotFound, "This invitation has already expired" if expired?
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.generate_code
|
|
49
|
+
SecureRandom.urlsafe_base64 4
|
|
50
|
+
end
|
|
51
|
+
|
|
31
52
|
private
|
|
32
53
|
|
|
33
54
|
def course_name
|
|
34
|
-
course.
|
|
55
|
+
course.name
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.sync_key_id_field
|
|
59
|
+
:code
|
|
35
60
|
end
|
|
36
61
|
end
|
data/app/models/language.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
class Language < ApplicationRecord
|
|
2
2
|
include WithCaseInsensitiveSearch
|
|
3
|
+
include Syncable
|
|
3
4
|
|
|
4
5
|
enum output_content_type: [:plain, :html, :markdown]
|
|
5
6
|
|
|
@@ -59,10 +60,6 @@ class Language < ApplicationRecord
|
|
|
59
60
|
interpolate(field, assignment.submitter.interpolations, lambda { |content| replace_content_reference(assignment, content) })
|
|
60
61
|
end
|
|
61
62
|
|
|
62
|
-
def self.locate_resource(runner_url)
|
|
63
|
-
find_or_initialize_by(runner_url: runner_url).tap { |it| it.save(validate: false) }
|
|
64
|
-
end
|
|
65
|
-
|
|
66
63
|
def to_embedded_resource_h
|
|
67
64
|
as_json(only: [:name, :extension, :test_extension]).symbolize_keys
|
|
68
65
|
end
|
|
@@ -101,4 +98,8 @@ class Language < ApplicationRecord
|
|
|
101
98
|
def directives_comment_type
|
|
102
99
|
Mumukit::Directives::CommentType.parse comment_type
|
|
103
100
|
end
|
|
101
|
+
|
|
102
|
+
def self.sync_key_id_field
|
|
103
|
+
:runner_url
|
|
104
|
+
end
|
|
104
105
|
end
|
data/app/models/message.rb
CHANGED
data/app/models/organization.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
class Organization < ApplicationRecord
|
|
2
|
+
include Syncable
|
|
2
3
|
include Mumukit::Platform::Organization::Helpers
|
|
3
4
|
|
|
4
5
|
serialize :profile, Mumukit::Platform::Organization::Profile
|
|
@@ -96,13 +97,9 @@ class Organization < ApplicationRecord
|
|
|
96
97
|
report_issue_enabled? || community_link.present? || forum_enabled?
|
|
97
98
|
end
|
|
98
99
|
|
|
99
|
-
def self.import_from_resource_h!(resource_h)
|
|
100
|
-
find_or_initialize_by(name: resource_h[:name]).tap { |it| it.import_from_resource_h! resource_h }
|
|
101
|
-
end
|
|
102
|
-
|
|
103
100
|
def import_from_resource_h!(resource_h)
|
|
104
101
|
attrs = Mumukit::Platform::Organization::Helpers.slice_platform_json resource_h
|
|
105
|
-
attrs[:book] = Book.
|
|
102
|
+
attrs[:book] = Book.locate! attrs[:book]
|
|
106
103
|
update! attrs
|
|
107
104
|
end
|
|
108
105
|
|
|
@@ -124,5 +121,13 @@ class Organization < ApplicationRecord
|
|
|
124
121
|
def base
|
|
125
122
|
find_by name: 'base'
|
|
126
123
|
end
|
|
124
|
+
|
|
125
|
+
def silenced?
|
|
126
|
+
!Mumukit::Platform::Organization.current? || current.silent?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def sync_key_id_field
|
|
130
|
+
:name
|
|
131
|
+
end
|
|
127
132
|
end
|
|
128
133
|
end
|
data/app/models/topic.rb
CHANGED
|
@@ -9,6 +9,8 @@ class Topic < Content
|
|
|
9
9
|
|
|
10
10
|
markdown_on :appendix
|
|
11
11
|
|
|
12
|
+
after_save :reindex_usages!
|
|
13
|
+
|
|
12
14
|
def pending_lessons(user)
|
|
13
15
|
guides.
|
|
14
16
|
joins('left join public.exercises exercises
|
|
@@ -29,7 +31,6 @@ class Topic < Content
|
|
|
29
31
|
self.assign_attributes resource_h.except(:lessons, :description)
|
|
30
32
|
self.description = resource_h[:description].squeeze(' ')
|
|
31
33
|
rebuild! resource_h[:lessons].to_a.map { |it| lesson_for(it) }
|
|
32
|
-
Organization.all.each { |org| org.reindex_usages! }
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def to_resource_h
|
|
@@ -40,6 +41,10 @@ class Topic < Content
|
|
|
40
41
|
book.chapters.find_by(topic_id: id) || Chapter.new(topic: self, book: book)
|
|
41
42
|
end
|
|
42
43
|
|
|
44
|
+
def reindex_usages!
|
|
45
|
+
Chapter.where(topic: self).map(&:book).each(&:reindex_usages!)
|
|
46
|
+
end
|
|
47
|
+
|
|
43
48
|
## Forking
|
|
44
49
|
|
|
45
50
|
def fork_to!(organization, syncer)
|
data/app/models/user.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
class User < ApplicationRecord
|
|
2
|
+
include Syncable
|
|
2
3
|
include WithProfile,
|
|
3
4
|
WithUserNavigation,
|
|
4
5
|
WithReminders,
|
|
@@ -80,7 +81,7 @@ class User < ApplicationRecord
|
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
def accept_invitation!(invitation)
|
|
83
|
-
make_student_of! invitation.
|
|
84
|
+
make_student_of! invitation.course_slug
|
|
84
85
|
end
|
|
85
86
|
|
|
86
87
|
def copy_progress_to!(another)
|
|
@@ -95,18 +96,14 @@ class User < ApplicationRecord
|
|
|
95
96
|
reload
|
|
96
97
|
end
|
|
97
98
|
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
User.where(uid: json[:uid]).update_or_create!(json)
|
|
99
|
+
def import_from_resource_h!(json)
|
|
100
|
+
update! Mumukit::Platform::User::Helpers.slice_platform_json json
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def unsubscribe_from_reminders!
|
|
104
104
|
update! accepts_reminders: false
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
-
def self.unsubscription_verifier
|
|
108
|
-
Rails.application.message_verifier(:unsubscribe)
|
|
109
|
-
end
|
|
110
107
|
|
|
111
108
|
def attach!(role, course)
|
|
112
109
|
add_permission! role, course.slug
|
|
@@ -118,11 +115,6 @@ class User < ApplicationRecord
|
|
|
118
115
|
save_and_notify!
|
|
119
116
|
end
|
|
120
117
|
|
|
121
|
-
def self.create_if_necessary(user)
|
|
122
|
-
user[:uid] ||= user[:email]
|
|
123
|
-
where(uid: user[:uid]).first_or_create(user)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
118
|
def interpolations
|
|
127
119
|
{
|
|
128
120
|
'user_email' => email,
|
|
@@ -150,4 +142,17 @@ class User < ApplicationRecord
|
|
|
150
142
|
def init
|
|
151
143
|
self.image_url ||= "user_shape.png"
|
|
152
144
|
end
|
|
145
|
+
|
|
146
|
+
def self.sync_key_id_field
|
|
147
|
+
:uid
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def self.unsubscription_verifier
|
|
151
|
+
Rails.application.message_verifier(:unsubscribe)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.create_if_necessary(user)
|
|
155
|
+
user[:uid] ||= user[:email]
|
|
156
|
+
where(uid: user[:uid]).first_or_create(user)
|
|
157
|
+
end
|
|
153
158
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
class AddLoginMethodsToOrganization < ActiveRecord::Migration[4.2]
|
|
2
2
|
def change
|
|
3
|
-
add_column :organizations, :login_methods, :string, array: true, default:
|
|
3
|
+
add_column :organizations, :login_methods, :string, array: true, default: [], null: false
|
|
4
4
|
end
|
|
5
5
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require_relative './factories/api_client_factory'
|
|
2
|
+
require_relative './factories/assignments_factory'
|
|
3
|
+
require_relative './factories/book_factory'
|
|
4
|
+
require_relative './factories/chapter_factory'
|
|
5
|
+
require_relative './factories/complement_factory'
|
|
6
|
+
require_relative './factories/course_factory'
|
|
7
|
+
require_relative './factories/discussion_factory'
|
|
8
|
+
require_relative './factories/exam_factory'
|
|
9
|
+
require_relative './factories/exercise_factory'
|
|
10
|
+
require_relative './factories/guide_factory'
|
|
11
|
+
require_relative './factories/invitation_factory'
|
|
12
|
+
require_relative './factories/lesson_factory'
|
|
13
|
+
require_relative './factories/login_settings_factory'
|
|
14
|
+
require_relative './factories/message_factory'
|
|
15
|
+
require_relative './factories/organization_factory'
|
|
16
|
+
require_relative './factories/topic_factory'
|
|
17
|
+
require_relative './factories/user_factory'
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
factory :api_client do |t|
|
|
3
|
+
transient do
|
|
4
|
+
role { :janitor }
|
|
5
|
+
grant { 'test/*' }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
description { "foo" }
|
|
9
|
+
user {
|
|
10
|
+
create :user,
|
|
11
|
+
first_name: 'foo',
|
|
12
|
+
last_name: 'bar',
|
|
13
|
+
email: 'foo+1@bar.com',
|
|
14
|
+
uid: 'foo+1@bar.com',
|
|
15
|
+
permissions: { role => grant }
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
|
|
3
|
+
factory :chapter do
|
|
4
|
+
number { Faker::Number.between(1, 40) }
|
|
5
|
+
book { Organization.current.book rescue nil }
|
|
6
|
+
|
|
7
|
+
transient do
|
|
8
|
+
lessons { [] }
|
|
9
|
+
name { Faker::Lorem.sentence(3) }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
after(:build) do |chapter, evaluator|
|
|
13
|
+
chapter.topic = build(:topic, name: evaluator.name, lessons: evaluator.lessons) unless evaluator.topic
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
|
|
3
|
+
factory :language do
|
|
4
|
+
sequence(:name) { |n| "lang#{n}" }
|
|
5
|
+
|
|
6
|
+
runner_url { Faker::Internet.url }
|
|
7
|
+
queriable { true }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
factory :haskell, parent: :language do
|
|
11
|
+
name { 'haskell' }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
factory :bash, parent: :language do
|
|
15
|
+
name { 'bash' }
|
|
16
|
+
triable { true }
|
|
17
|
+
stateful_console { true }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
factory :text_language, parent: :language do
|
|
21
|
+
name { 'text' }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
factory :gobstones, parent: :language do
|
|
25
|
+
name { 'gobstones' }
|
|
26
|
+
extension { 'gbs' }
|
|
27
|
+
queriable { false }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
factory :exercise_base do
|
|
31
|
+
language { guide ? guide.language : create(:language) }
|
|
32
|
+
sequence(:bibliotheca_id) { |n| n }
|
|
33
|
+
sequence(:number) { |n| n }
|
|
34
|
+
|
|
35
|
+
locale { :en }
|
|
36
|
+
guide
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
factory :challenge, parent: :exercise_base do
|
|
40
|
+
layout { 'input_right' }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
factory :reading, class: Reading, parent: :exercise_base do
|
|
44
|
+
name { 'A reading' }
|
|
45
|
+
description { 'Simple reading' }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
factory :problem, class: Problem, parent: :challenge do
|
|
49
|
+
name { 'A problem' }
|
|
50
|
+
description { 'Simple problem' }
|
|
51
|
+
test { 'dont care' }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
factory :interactive, class: Interactive, parent: :challenge do
|
|
55
|
+
name { 'An interactive problem' }
|
|
56
|
+
description { 'Simple interactive problem' }
|
|
57
|
+
goal { :query_passes }
|
|
58
|
+
language { create(:bash) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
factory :playground, class: Playground, parent: :challenge do
|
|
62
|
+
name { 'A Playground' }
|
|
63
|
+
description { 'Simple playground' }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
factory :exercise, parent: :problem
|
|
67
|
+
|
|
68
|
+
factory :x_equal_5_exercise, parent: :exercise do
|
|
69
|
+
test { 'describe "x" $ do
|
|
70
|
+
it "should be equal 5" $ do
|
|
71
|
+
x `shouldBe` 5' }
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
|
|
3
|
+
factory :guide do
|
|
4
|
+
sequence(:name) { |n| "guide#{n}" }
|
|
5
|
+
locale { 'en' }
|
|
6
|
+
description { 'A Guide' }
|
|
7
|
+
slug { "flbulgarelli/mumuki-sample-guide-#{SecureRandom.uuid}" }
|
|
8
|
+
language
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
trait :guide_container do
|
|
12
|
+
transient do
|
|
13
|
+
exercises { [] }
|
|
14
|
+
name { Faker::Lorem.sentence(3) }
|
|
15
|
+
description { Faker::Lorem.sentence(10) }
|
|
16
|
+
language { create(:language) }
|
|
17
|
+
slug { "mumuki/mumuki-test-lesson-#{SecureRandom.uuid}" }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
after(:build) do |lesson, evaluator|
|
|
21
|
+
lesson.guide = build(:guide,
|
|
22
|
+
name: evaluator.name,
|
|
23
|
+
slug: evaluator.slug,
|
|
24
|
+
exercises: evaluator.exercises,
|
|
25
|
+
description: evaluator.description,
|
|
26
|
+
language: evaluator.language) unless evaluator.guide
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
|
|
3
|
+
factory :organization do
|
|
4
|
+
contact_email { Faker::Internet.email }
|
|
5
|
+
description { 'a great org' }
|
|
6
|
+
locale { 'en' }
|
|
7
|
+
settings {}
|
|
8
|
+
book
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
factory :base, parent: :organization do
|
|
12
|
+
public { true }
|
|
13
|
+
name { 'base' }
|
|
14
|
+
login_methods { Mumukit::Login::Settings.login_methods }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
factory :public_organization, parent: :organization do
|
|
18
|
+
public { true }
|
|
19
|
+
name { 'the-public-org' }
|
|
20
|
+
login_methods { Mumukit::Login::Settings.login_methods }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
factory :private_organization, parent: :organization do
|
|
24
|
+
name { 'the-private-org' }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -6,7 +6,7 @@ class Mumuki::Domain::Submission::ConsoleSubmission < Mumuki::Domain::Submission
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def format_query_result!(results)
|
|
9
|
-
results[:result] = I18n.t(
|
|
9
|
+
results[:result] = I18n.t('submission.try_again') if results[:status].aborted?
|
|
10
10
|
results[:status] = results[:status].to_submission_status
|
|
11
11
|
results
|
|
12
12
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mumuki-domain
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Franco Leonardo Bulgarelli
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-
|
|
11
|
+
date: 2018-12-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -86,14 +86,14 @@ dependencies:
|
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '1.
|
|
89
|
+
version: '1.13'
|
|
90
90
|
type: :runtime
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '1.
|
|
96
|
+
version: '1.13'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: mumukit-directives
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -114,14 +114,14 @@ dependencies:
|
|
|
114
114
|
requirements:
|
|
115
115
|
- - "~>"
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '3.
|
|
117
|
+
version: '3.6'
|
|
118
118
|
type: :runtime
|
|
119
119
|
prerelease: false
|
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
121
|
requirements:
|
|
122
122
|
- - "~>"
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '3.
|
|
124
|
+
version: '3.6'
|
|
125
125
|
- !ruby/object:Gem::Dependency
|
|
126
126
|
name: mumukit-randomizer
|
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -156,14 +156,42 @@ dependencies:
|
|
|
156
156
|
requirements:
|
|
157
157
|
- - "~>"
|
|
158
158
|
- !ruby/object:Gem::Version
|
|
159
|
-
version: '0.
|
|
159
|
+
version: '0.3'
|
|
160
160
|
type: :runtime
|
|
161
161
|
prerelease: false
|
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
|
163
163
|
requirements:
|
|
164
164
|
- - "~>"
|
|
165
165
|
- !ruby/object:Gem::Version
|
|
166
|
-
version: '0.
|
|
166
|
+
version: '0.3'
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: pg
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - "~>"
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: 0.18.0
|
|
174
|
+
type: :development
|
|
175
|
+
prerelease: false
|
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - "~>"
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: 0.18.0
|
|
181
|
+
- !ruby/object:Gem::Dependency
|
|
182
|
+
name: mumukit-login
|
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - "~>"
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '6.1'
|
|
188
|
+
type: :development
|
|
189
|
+
prerelease: false
|
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
+
requirements:
|
|
192
|
+
- - "~>"
|
|
193
|
+
- !ruby/object:Gem::Version
|
|
194
|
+
version: '6.1'
|
|
167
195
|
description: Mumuki Platform's Domain Model
|
|
168
196
|
email:
|
|
169
197
|
- franco@mumuki.org
|
|
@@ -173,6 +201,7 @@ extra_rdoc_files: []
|
|
|
173
201
|
files:
|
|
174
202
|
- LICENSE
|
|
175
203
|
- README.md
|
|
204
|
+
- Rakefile
|
|
176
205
|
- app/models/api_client.rb
|
|
177
206
|
- app/models/application_record.rb
|
|
178
207
|
- app/models/assignment.rb
|
|
@@ -192,6 +221,7 @@ files:
|
|
|
192
221
|
- app/models/concerns/submittable/solvable.rb
|
|
193
222
|
- app/models/concerns/submittable/submittable.rb
|
|
194
223
|
- app/models/concerns/submittable/triable.rb
|
|
224
|
+
- app/models/concerns/syncable.rb
|
|
195
225
|
- app/models/concerns/with_assignments.rb
|
|
196
226
|
- app/models/concerns/with_case_insensitive_search.rb
|
|
197
227
|
- app/models/concerns/with_description.rb
|
|
@@ -501,6 +531,7 @@ files:
|
|
|
501
531
|
- db/migrate/20181114201620_add_test_template_to_languages.rb
|
|
502
532
|
- db/migrate/20181117190241_add_feedback_to_language.rb
|
|
503
533
|
- db/migrate/20181121165956_rename_choices_column.rb
|
|
534
|
+
- db/migrate/20181210131824_convert_course_invitation_into_fk.rb
|
|
504
535
|
- lib/mumuki/domain.rb
|
|
505
536
|
- lib/mumuki/domain/engine.rb
|
|
506
537
|
- lib/mumuki/domain/evaluation.rb
|
|
@@ -515,12 +546,33 @@ files:
|
|
|
515
546
|
- lib/mumuki/domain/extensions.rb
|
|
516
547
|
- lib/mumuki/domain/extensions/array.rb
|
|
517
548
|
- lib/mumuki/domain/extensions/string.rb
|
|
549
|
+
- lib/mumuki/domain/factories.rb
|
|
550
|
+
- lib/mumuki/domain/factories/api_client_factory.rb
|
|
551
|
+
- lib/mumuki/domain/factories/assignments_factory.rb
|
|
552
|
+
- lib/mumuki/domain/factories/book_factory.rb
|
|
553
|
+
- lib/mumuki/domain/factories/chapter_factory.rb
|
|
554
|
+
- lib/mumuki/domain/factories/complement_factory.rb
|
|
555
|
+
- lib/mumuki/domain/factories/course_factory.rb
|
|
556
|
+
- lib/mumuki/domain/factories/discussion_factory.rb
|
|
557
|
+
- lib/mumuki/domain/factories/exam_factory.rb
|
|
558
|
+
- lib/mumuki/domain/factories/exercise_factory.rb
|
|
559
|
+
- lib/mumuki/domain/factories/guide_factory.rb
|
|
560
|
+
- lib/mumuki/domain/factories/invitation_factory.rb
|
|
561
|
+
- lib/mumuki/domain/factories/lesson_factory.rb
|
|
562
|
+
- lib/mumuki/domain/factories/login_settings_factory.rb
|
|
563
|
+
- lib/mumuki/domain/factories/message_factory.rb
|
|
564
|
+
- lib/mumuki/domain/factories/organization_factory.rb
|
|
565
|
+
- lib/mumuki/domain/factories/topic_factory.rb
|
|
566
|
+
- lib/mumuki/domain/factories/user_factory.rb
|
|
518
567
|
- lib/mumuki/domain/file.rb
|
|
519
568
|
- lib/mumuki/domain/locales/activerecord.en.yml
|
|
520
569
|
- lib/mumuki/domain/locales/activerecord.es.yml
|
|
521
570
|
- lib/mumuki/domain/locales/narrator.en.yml
|
|
522
571
|
- lib/mumuki/domain/locales/narrator.es.yml
|
|
523
572
|
- lib/mumuki/domain/locales/narrator.pt.yml
|
|
573
|
+
- lib/mumuki/domain/locales/submission.en.yml
|
|
574
|
+
- lib/mumuki/domain/locales/submission.es.yml
|
|
575
|
+
- lib/mumuki/domain/locales/submission.pt.yml
|
|
524
576
|
- lib/mumuki/domain/seed.rb
|
|
525
577
|
- lib/mumuki/domain/status.rb
|
|
526
578
|
- lib/mumuki/domain/status/discussion/closed.rb
|
|
@@ -566,8 +618,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
566
618
|
- !ruby/object:Gem::Version
|
|
567
619
|
version: '0'
|
|
568
620
|
requirements: []
|
|
569
|
-
|
|
570
|
-
rubygems_version: 2.5.1
|
|
621
|
+
rubygems_version: 3.0.0
|
|
571
622
|
signing_key:
|
|
572
623
|
specification_version: 4
|
|
573
624
|
summary: Mumuki Platform's Domain Model
|