moblues 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 (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +44 -0
  7. data/Rakefile +4 -0
  8. data/bin/moblues +5 -0
  9. data/lib/extensions/hash.rb +9 -0
  10. data/lib/moblues/cli.rb +26 -0
  11. data/lib/moblues/data_model/attribute.rb +15 -0
  12. data/lib/moblues/data_model/entity.rb +17 -0
  13. data/lib/moblues/data_model/relationship.rb +17 -0
  14. data/lib/moblues/data_model.rb +3 -0
  15. data/lib/moblues/generator/base.rb +65 -0
  16. data/lib/moblues/generator/human.rb +25 -0
  17. data/lib/moblues/generator/human_header.h.erb +4 -0
  18. data/lib/moblues/generator/human_implementation.m.erb +4 -0
  19. data/lib/moblues/generator/machine.rb +36 -0
  20. data/lib/moblues/generator/machine_header.h.erb +43 -0
  21. data/lib/moblues/generator/machine_implementation.m.erb +7 -0
  22. data/lib/moblues/generator/model.rb +23 -0
  23. data/lib/moblues/generator.rb +4 -0
  24. data/lib/moblues/reader/attribute.rb +23 -0
  25. data/lib/moblues/reader/entity.rb +34 -0
  26. data/lib/moblues/reader/model.rb +31 -0
  27. data/lib/moblues/reader/relationship.rb +22 -0
  28. data/lib/moblues/reader/type.rb +58 -0
  29. data/lib/moblues/reader.rb +5 -0
  30. data/lib/moblues/utils/model_resolver.rb +19 -0
  31. data/lib/moblues/version.rb +3 -0
  32. data/lib/moblues.rb +4 -0
  33. data/moblues.gemspec +33 -0
  34. data/spec/features/moblues_spec.rb +36 -0
  35. data/spec/moblues/cli_spec.rb +50 -0
  36. data/spec/moblues/data_model/attribute_spec.rb +30 -0
  37. data/spec/moblues/data_model/entity_spec.rb +48 -0
  38. data/spec/moblues/data_model/relationship_spec.rb +46 -0
  39. data/spec/moblues/generator/human_spec.rb +74 -0
  40. data/spec/moblues/generator/machine_spec.rb +86 -0
  41. data/spec/moblues/generator/model_spec.rb +27 -0
  42. data/spec/moblues/reader/attribute_spec.rb +22 -0
  43. data/spec/moblues/reader/entity_spec.rb +57 -0
  44. data/spec/moblues/reader/model_spec.rb +66 -0
  45. data/spec/moblues/reader/relationship_spec.rb +22 -0
  46. data/spec/moblues/reader/type_spec.rb +79 -0
  47. data/spec/moblues/utils/model_resolver_spec.rb +28 -0
  48. data/spec/resources/Model.xcdatamodeld/.xccurrentversion +8 -0
  49. data/spec/resources/Model.xcdatamodeld/Model 2.xcdatamodel/contents +28 -0
  50. data/spec/resources/Model.xcdatamodeld/Model.xcdatamodel/contents +18 -0
  51. data/spec/resources/expected/Author.h +4 -0
  52. data/spec/resources/expected/Author.m +4 -0
  53. data/spec/resources/expected/_Author.h +36 -0
  54. data/spec/resources/expected/_Author.m +9 -0
  55. data/spec/resources/expected/_Book.h +19 -0
  56. data/spec/resources/expected/_Book.m +6 -0
  57. data/spec/resources/expected/_Person.h +9 -0
  58. data/spec/resources/expected/_Person.m +6 -0
  59. data/spec/resources/expected/_Team.h +18 -0
  60. data/spec/resources/expected/_Team.m +5 -0
  61. data/spec/resources/expected/human/Playable.h +4 -0
  62. data/spec/resources/expected/human/Playable.m +4 -0
  63. data/spec/resources/expected/human/Playlist.h +5 -0
  64. data/spec/resources/expected/human/Playlist.m +4 -0
  65. data/spec/resources/expected/human/Track.h +4 -0
  66. data/spec/resources/expected/human/Track.m +4 -0
  67. data/spec/resources/expected/human/User.h +4 -0
  68. data/spec/resources/expected/human/User.m +4 -0
  69. data/spec/resources/expected/machine/_Playable.h +9 -0
  70. data/spec/resources/expected/machine/_Playable.m +6 -0
  71. data/spec/resources/expected/machine/_Playlist.h +28 -0
  72. data/spec/resources/expected/machine/_Playlist.m +6 -0
  73. data/spec/resources/expected/machine/_Track.h +14 -0
  74. data/spec/resources/expected/machine/_Track.m +7 -0
  75. data/spec/resources/expected/machine/_User.h +27 -0
  76. data/spec/resources/expected/machine/_User.m +8 -0
  77. data/spec/resources/factories/data_model/attribute_factory.rb +12 -0
  78. data/spec/resources/factories/data_model/entity_factory.rb +13 -0
  79. data/spec/resources/factories/data_model/relationship_factory.rb +14 -0
  80. data/spec/resources/fixtures.rb +34 -0
  81. data/spec/resources/tmp/.gitkeep +0 -0
  82. data/spec/resources/tmp/_Team.h +18 -0
  83. data/spec/resources/tmp/_Team.m +5 -0
  84. data/spec/resources/tmp/human/Playlist.h +5 -0
  85. data/spec/resources/tmp/human/Playlist.m +4 -0
  86. data/spec/spec_helper.rb +15 -0
  87. metadata +285 -0
data/moblues.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'moblues/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'moblues'
8
+ spec.version = Moblues::VERSION
9
+ spec.authors = ['Vincent Garrigues']
10
+ spec.email = ['vincent.garrigues@gmail.com']
11
+ spec.summary = 'Generates Objective-C files for entities defined in a Core Data model'
12
+ spec.description = %q{
13
+ Generates files for entities defined in a Core Data model. Inspired by mogenerator.
14
+
15
+ For each entity in the Core Data model, moblues will create two files: a machine file and a human file. The machine file will be overwritten each time and should not contain any modifications. Moblues will only create the human file if it doesn't exist.
16
+ }
17
+ spec.homepage = 'http://garriguv.io'
18
+ spec.license = 'MIT'
19
+
20
+ spec.files = `git ls-files -z`.split("\x0")
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_runtime_dependency 'thor', '~> 0.19'
26
+ spec.add_runtime_dependency 'plist', '~> 3.1'
27
+
28
+ spec.add_development_dependency 'bundler', '~> 1.7'
29
+ spec.add_development_dependency 'rake', '~> 10.3'
30
+ spec.add_development_dependency 'rspec', '~> 3.1'
31
+ spec.add_development_dependency 'factory_girl', '~> 4.5'
32
+ spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.1'
33
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'moblues/cli'
3
+
4
+ describe 'moblues' do
5
+ subject { Moblues::CLI }
6
+ let(:perform) { subject.start([command] + options) }
7
+
8
+ describe 'generate' do
9
+ let(:command) { 'generate' }
10
+ let(:options) { %w{--model=spec/resources/Model.xcdatamodeld --human=spec/resources/tmp/human --machine=spec/resources/tmp/machine} }
11
+
12
+ after do
13
+ Fixtures.delete_tmp_files(machine_files + human_files.select { |f| !f.include?('Playlist') } )
14
+ end
15
+
16
+ it 'generates human and machine files' do
17
+ perform
18
+
19
+ (machine_files + human_files).each do |file|
20
+ expect(Fixtures.read_file(Fixtures.generated_dir, file)).to eq(Fixtures.read_file(Fixtures.expected_dir, file))
21
+ end
22
+ end
23
+ end
24
+
25
+ def machine_files
26
+ entities.map{|f| "machine/_#{f}" }.map{ |f| %W(#{f}.h #{f}.m) }.flatten
27
+ end
28
+
29
+ def human_files
30
+ entities.map{|f| "human/#{f}" }.map{ |f| %W(#{f}.h #{f}.m) }.flatten
31
+ end
32
+
33
+ def entities
34
+ %w{User Playable Track Playlist}
35
+ end
36
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+ require 'moblues/cli'
3
+
4
+ describe Moblues::CLI do
5
+ subject { described_class }
6
+ let(:perform) { subject.start([command] + options, debug: true) }
7
+
8
+ context 'when command is invalid' do
9
+ let(:command) { 'invalid' }
10
+ let(:options) { [] }
11
+
12
+ it 'fails' do
13
+ expect { perform }.to raise_error
14
+ end
15
+ end
16
+
17
+ context 'generate' do
18
+ let(:command) { 'generate' }
19
+
20
+ context 'when options missing' do
21
+ let(:options) { [] }
22
+
23
+ it 'fails' do
24
+ expect { perform }.to raise_error
25
+ end
26
+ end
27
+
28
+ context 'when options given' do
29
+ let(:options) { %w{--model=model/path --human=human/path --machine=machine/path} }
30
+
31
+ let(:model_reader) { double(Moblues::Reader::Model) }
32
+ let(:model_generator) { double(Moblues::Generator::Model) }
33
+ let(:entities) { [build(:entity)] }
34
+
35
+ before do
36
+ allow(Moblues::Reader::Model).to receive(:new) { model_reader }
37
+ allow(Moblues::Generator::Model).to receive(:new) { model_generator }
38
+ end
39
+
40
+ it 'created the directories reads the model and generates the files' do
41
+ expect(Dir).to receive(:mkdir).with('human/path').once
42
+ expect(Dir).to receive(:mkdir).with('machine/path').once
43
+ expect(model_reader).to receive(:model).with('model/path').once { entities }
44
+ expect(model_generator).to receive(:generate).with(entities).once
45
+
46
+ perform
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'moblues/data_model/attribute'
3
+
4
+ module Moblues
5
+ module DataModel
6
+ describe Attribute do
7
+ describe '#initialize' do
8
+ context 'when name and type provided' do
9
+ subject { described_class.new(name: 'attribute', type: 'type') }
10
+
11
+ it 'returns an Attribute object' do
12
+ expect(subject).to eq(Attribute.new(name: 'attribute', type: 'type'))
13
+ end
14
+ end
15
+
16
+ context 'when name missing' do
17
+ it 'raises an assertion' do
18
+ expect { Attribute.new(type: 'type') }.to raise_exception(KeyError)
19
+ end
20
+ end
21
+
22
+ context 'when type missing' do
23
+ it 'raises an assertion' do
24
+ expect { Attribute.new(name: 'attribute') }.to raise_exception(KeyError)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'moblues/data_model/entity'
3
+
4
+ module Moblues
5
+ module DataModel
6
+ describe Entity do
7
+ describe '#initialize' do
8
+ context 'when name, parent_entity, attributes and relationship provided' do
9
+ subject { described_class.new(name: 'AudioBook', parent_entity: 'Book', attributes: ['title'], relationships: ['author']) }
10
+
11
+ it 'returns an Entity object' do
12
+ expect(subject).to eq(Entity.new(name: 'AudioBook', parent_entity: 'Book', attributes: ['title'], relationships: ['author']))
13
+ end
14
+ end
15
+
16
+ context 'when name missing' do
17
+ it 'raises an exception' do
18
+ expect { Entity.new(parent_entity: 'Book', attributes: ['title'], relationships: ['author']) }.to raise_exception(KeyError)
19
+ end
20
+ end
21
+
22
+ context 'when parent_entity missing' do
23
+ subject { described_class.new(name: 'AudioBook', attributes: ['title'], relationships: ['author']) }
24
+
25
+ it 'returns an Entity with NSManagedObject parent_entity' do
26
+ expect(subject).to eq(Entity.new(name: 'AudioBook', parent_entity: 'NSManagedObject', attributes: ['title'], relationships: ['author']))
27
+ end
28
+ end
29
+
30
+ context 'when attributes missing' do
31
+ subject { described_class.new(name: 'AudioBook', parent_entity: 'Book', relationships: ['author']) }
32
+
33
+ it 'returns an Entity with empty attributes' do
34
+ expect(subject).to eq(Entity.new(name: 'AudioBook', parent_entity: 'Book', attributes: [], relationships: ['author']))
35
+ end
36
+ end
37
+
38
+ context 'when relationships missing' do
39
+ subject { described_class.new(name: 'AudioBook', parent_entity: 'Book', attributes: ['title']) }
40
+
41
+ it 'returns an Entity with empty relationships' do
42
+ expect(subject).to eq(Entity.new(name: 'AudioBook', parent_entity: 'Book', attributes: ['title'], relationships: []))
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'moblues/data_model/relationship'
3
+
4
+ module Moblues
5
+ module DataModel
6
+ describe Relationship do
7
+ describe '#initialize' do
8
+ context 'when name, destination_entity, to_many and ordered provided' do
9
+ subject { described_class.new(name: 'relationship', destination_entity: 'entity', to_many: true, ordered: true) }
10
+
11
+ it 'returns a Relationship object' do
12
+ expect(subject).to eq(Relationship.new(name: 'relationship', destination_entity: 'entity', to_many: true, ordered: true))
13
+ end
14
+ end
15
+
16
+ context 'when name missing' do
17
+ it 'raises an assertion' do
18
+ expect { Relationship.new(destination_entity: 'entity', to_many: true, ordered: true) }.to raise_exception(KeyError)
19
+ end
20
+ end
21
+
22
+ context 'when destination_entity missing' do
23
+ it 'raises an assertion' do
24
+ expect { Relationship.new(name: 'relationship', to_many: true, ordered: true) }.to raise_exception(KeyError)
25
+ end
26
+ end
27
+
28
+ context 'when to_many missing' do
29
+ subject { described_class.new(name: 'relationship', destination_entity: 'entity', ordered: true) }
30
+
31
+ it 'returns a Relationship with to_many false' do
32
+ expect(subject).to eq(Relationship.new(name: 'relationship', destination_entity: 'entity', to_many: false, ordered: true))
33
+ end
34
+ end
35
+
36
+ context 'when ordered missing' do
37
+ subject { described_class.new(name: 'relationship', destination_entity: 'entity', to_many: true) }
38
+
39
+ it 'returns a Relationship with ordered false' do
40
+ expect(subject).to eq(Relationship.new(name: 'relationship', destination_entity: 'entity', to_many: true, ordered: false))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'moblues/generator/human'
3
+ require 'moblues/data_model'
4
+
5
+ describe Moblues::Generator::Human do
6
+ subject { described_class.new(output_dir: Fixtures.generated_dir) }
7
+
8
+ after do
9
+ Fixtures.delete_tmp_files(%w{Author.h Author.m})
10
+ end
11
+
12
+ describe '#generate' do
13
+ it 'generates a human header' do
14
+ subject.generate(entity)
15
+
16
+ expect(Fixtures.generated_file_content('Author.h')).to eq(Fixtures.expected_content('Author.h'))
17
+ end
18
+
19
+ it 'generates a human implementation' do
20
+ subject.generate(entity)
21
+
22
+ expect(Fixtures.generated_file_content('Author.m')).to eq(Fixtures.expected_content('Author.m'))
23
+ end
24
+
25
+ context 'if the header already exists' do
26
+ before do
27
+ File.open(File.join(Fixtures.generated_dir, 'Author.h'), 'w+') do |f|
28
+ f.write('do nothing')
29
+ end
30
+ end
31
+
32
+ it 'does not overwrite the header file' do
33
+ subject.generate(entity)
34
+
35
+ expect(Fixtures.generated_file_content('Author.h')).to eq('do nothing')
36
+ end
37
+ end
38
+
39
+ context 'if the implementation already exists' do
40
+ before do
41
+ File.open(File.join(Fixtures.generated_dir, 'Author.m'), 'w+') do |f|
42
+ f.write('do nothing')
43
+ end
44
+ end
45
+
46
+ it 'does not overwrite the implementation file' do
47
+ subject.generate(entity)
48
+
49
+ expect(Fixtures.generated_file_content('Author.m')).to eq('do nothing')
50
+ end
51
+ end
52
+ end
53
+
54
+ def entity
55
+ Moblues::DataModel::Entity.new(name: 'Author',
56
+ attributes: attributes,
57
+ relationships: relationships)
58
+ end
59
+
60
+ def attributes
61
+ [
62
+ Moblues::DataModel::Attribute.new(name: 'dob', type: :date),
63
+ Moblues::DataModel::Attribute.new(name: 'name', type: :string)
64
+ ]
65
+ end
66
+
67
+ def relationships
68
+ [
69
+ Moblues::DataModel::Relationship.new(name: 'books', destination_entity: 'Book', to_many: true, ordered: true),
70
+ Moblues::DataModel::Relationship.new(name: 'essays', destination_entity: 'Book', to_many: true),
71
+ Moblues::DataModel::Relationship.new(name: 'publisher', destination_entity: 'Publisher')
72
+ ]
73
+ end
74
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+ require 'moblues/generator/machine'
3
+ require 'moblues/data_model'
4
+
5
+ describe Moblues::Generator::Machine do
6
+ subject { described_class.new(output_dir: Fixtures.generated_dir) }
7
+
8
+ after do
9
+ Fixtures.delete_tmp_files(%w{_Author.h _Author.m _Person.h _Person.m _Book.h _Book.m})
10
+ end
11
+
12
+ describe '#generate' do
13
+ shared_examples_for 'machine_generator' do |name|
14
+ it 'generates a header' do
15
+ subject.generate(entity)
16
+
17
+ expect(Fixtures.generated_file_content(header(name))).to eq(Fixtures.expected_content(header(name)))
18
+ end
19
+
20
+ it 'generates an implementation' do
21
+ subject.generate(entity)
22
+
23
+ expect(Fixtures.generated_file_content(implementation(name))).to eq(Fixtures.expected_content(implementation(name)))
24
+ end
25
+ end
26
+
27
+ context 'with a complex entity' do
28
+ let(:entity) { Moblues::DataModel::Entity.new(name: 'Author',
29
+ parent_entity: 'Person',
30
+ attributes: attributes,
31
+ relationships: relationships) }
32
+ let(:attributes) {[
33
+ Moblues::DataModel::Attribute.new(name: 'dob', type: :date),
34
+ Moblues::DataModel::Attribute.new(name: 'name', type: :string)
35
+ ]}
36
+ let(:relationships) {[
37
+ Moblues::DataModel::Relationship.new(name: 'books', destination_entity: 'Book', to_many: true, ordered: true),
38
+ Moblues::DataModel::Relationship.new(name: 'essays', destination_entity: 'Book', to_many: true),
39
+ Moblues::DataModel::Relationship.new(name: 'publisher', destination_entity: 'Publisher')
40
+ ]}
41
+
42
+ it_behaves_like 'machine_generator', 'Author'
43
+ end
44
+
45
+ context 'with an entity without relationships' do
46
+ let(:entity) { Moblues::DataModel::Entity.new(name: 'Person',
47
+ attributes: [Moblues::DataModel::Attribute.new(name: 'name', type: :string),
48
+ Moblues::DataModel::Attribute.new(name: 'dob', type: :date)]) }
49
+
50
+ it_behaves_like 'machine_generator', 'Person'
51
+ end
52
+
53
+ context 'with an entity with attributes, relationships but NO forward declaration' do
54
+ let(:entity) { Moblues::DataModel::Entity.new(name: 'Book',
55
+ attributes: attributes,
56
+ relationships: relationships) }
57
+ let(:attributes) {[
58
+ Moblues::DataModel::Attribute.new(name: 'name', type: :string)
59
+ ]}
60
+ let(:relationships) {[
61
+ Moblues::DataModel::Relationship.new(name: 'editions', destination_entity: 'Edition', to_many: true)
62
+ ]}
63
+
64
+ it_behaves_like 'machine_generator', 'Book'
65
+ end
66
+
67
+ context 'with an entity that has a to-many relationship an a variable name that contains capitals' do
68
+ let(:entity) { Moblues::DataModel::Entity.new(name: 'Team',
69
+ attributes: [],
70
+ relationships: relationships) }
71
+ let(:relationships) {[
72
+ Moblues::DataModel::Relationship.new(name: 'teamMembers', destination_entity: 'Person', to_many: true)
73
+ ]}
74
+
75
+ it_behaves_like 'machine_generator', 'Team'
76
+ end
77
+ end
78
+
79
+ def header(name)
80
+ "_#{name}.h"
81
+ end
82
+
83
+ def implementation(name)
84
+ "_#{name}.h"
85
+ end
86
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Moblues::Generator::Model do
4
+ describe '#generate' do
5
+ subject { described_class.new(human: human, machine: machine, human_dir: human_dir, machine_dir: machine_dir) }
6
+
7
+ let(:entities) { [build(:entity)] }
8
+ let(:entity) { entities.first }
9
+ let(:human_dir) { 'human/dir'}
10
+ let(:machine_dir) { 'machine/dir'}
11
+
12
+ let(:human) { double(Moblues::Generator::Human) }
13
+ let(:machine) { double(Moblues::Generator::Machine) }
14
+
15
+ before do
16
+ allow(Moblues::Generator::Human).to receive(:new).with(output_dir: human_dir) { human }
17
+ allow(Moblues::Generator::Machine).to receive(:new).with(output_dir: machine_dir) { machine }
18
+ end
19
+
20
+ it 'generates the human and machine files' do
21
+ expect(human).to receive(:generate).with(entity).once
22
+ expect(machine).to receive(:generate).with(entity).once
23
+
24
+ subject.generate(entities)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'moblues/reader/attribute'
3
+
4
+ describe Moblues::Reader::Attribute do
5
+ describe '#attribute' do
6
+ context 'when XML is valid' do
7
+ let(:xml) { REXML::Document.new('<attribute name="testName" attributeType="String"/>').elements.first }
8
+
9
+ it 'returns an Attribute object' do
10
+ expect(subject.attribute(xml)).to eq(Moblues::DataModel::Attribute.new(name: 'testName', type: :string))
11
+ end
12
+ end
13
+
14
+ context 'when XML is invalid' do
15
+ let(:xml) { REXML::Document.new('<notAnAttribute/>').elements.first }
16
+
17
+ it 'raises an assertion' do
18
+ expect { subject.attribute(xml) }.to raise_exception
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'moblues/reader/entity'
3
+
4
+ describe Moblues::Reader::Entity do
5
+ describe '#entity' do
6
+ context 'when XML is valid' do
7
+ subject { described_class.new(attribute_reader: attribute_reader, relationship_reader: relationship_reader) }
8
+
9
+ let(:attribute_reader) { double(Moblues::Reader::Attribute) }
10
+ let(:relationship_reader) { double(Moblues::Reader::Relationship) }
11
+ let(:xml) {
12
+ xml_str = <<EOF
13
+ <entity name="Author" parentEntity="Person" syncable="YES">
14
+ <attribute name="dob" attributeType="Date" syncable="YES"/>
15
+ <attribute name="name" attributeType="String" syncable="YES"/>
16
+ <relationship name="books" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Book" inverseName="author" inverseEntity="Book" syncable="YES"/>
17
+ </entity>
18
+ EOF
19
+ REXML::Document.new(xml_str).elements.first
20
+ }
21
+
22
+ before(:each) do
23
+ allow(attribute_reader).to receive(:attribute) { build(:attribute) }
24
+ allow(relationship_reader).to receive(:relationship) { build(:relationship) }
25
+ end
26
+
27
+ it 'maps attributes' do
28
+ expect(attribute_reader).to receive(:attribute).twice
29
+
30
+ subject.entity(xml)
31
+ end
32
+
33
+ it 'maps relationships' do
34
+ expect(relationship_reader).to receive(:relationship).once
35
+
36
+ subject.entity(xml)
37
+ end
38
+
39
+ it 'returns an Entity' do
40
+ entity = Moblues::DataModel::Entity.new(name: 'Author',
41
+ parent_entity: 'Person',
42
+ attributes: [build(:attribute), build(:attribute)],
43
+ relationships: [build(:relationship)])
44
+
45
+ expect(subject.entity(xml)).to eq(entity)
46
+ end
47
+ end
48
+
49
+ context 'when XML is invalid' do
50
+ let(:xml) { REXML::Document.new('<notAnEntity/>').elements.first }
51
+
52
+ it 'raises an exception' do
53
+ expect { subject.entity(xml) }.to raise_exception
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require 'moblues/reader/model'
3
+
4
+ describe Moblues::Reader::Model do
5
+ let(:model) { build(:model) }
6
+ let(:model_resolver) { double(Moblues::Utils::ModelResolver) }
7
+ let(:entity_reader) { double(Moblues::Reader::Entity) }
8
+ let(:file_str) {
9
+ <<EOF
10
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
11
+ <model userDefinedModelVersionIdentifier="2.0" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6252" systemVersion="14A389" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
12
+ <entity name="Author" syncable="YES">
13
+ <attribute name="dob" attributeType="Date" syncable="YES"/>
14
+ <attribute name="name" attributeType="String" syncable="YES"/>
15
+ <relationship name="books" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Book" inverseName="author" inverseEntity="Book" syncable="YES"/>
16
+ </entity>
17
+ <entity name="Book" syncable="YES">
18
+ <attribute name="numberOfPages" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
19
+ <attribute name="title" attributeType="String" syncable="YES"/>
20
+ <relationship name="author" maxCount="1" deletionRule="Nullify" destinationEntity="Author" inverseName="books" inverseEntity="Author" syncable="YES"/>
21
+ <relationship name="publisher" maxCount="1" deletionRule="Nullify" destinationEntity="Publisher" inverseName="books" inverseEntity="Publisher" syncable="YES"/>
22
+ </entity>
23
+ <entity name="Publisher" syncable="YES">
24
+ <attribute name="name" attributeType="String" syncable="YES"/>
25
+ <attribute name="website" optional="YES" attributeType="String" syncable="YES"/>
26
+ <relationship name="books" maxCount="1" deletionRule="Nullify" destinationEntity="Book" inverseName="publisher" inverseEntity="Book" syncable="YES"/>
27
+ </entity>
28
+ <elements>
29
+ <element name="Author" positionX="-281" positionY="-39" width="128" height="88"/>
30
+ <element name="Book" positionX="-54" positionY="-9" width="128" height="103"/>
31
+ <element name="Publisher" positionX="153" positionY="6" width="128" height="88"/>
32
+ </elements>
33
+ </model>
34
+ EOF
35
+ }
36
+
37
+ subject { described_class.new(resolver: model_resolver, reader: entity_reader) }
38
+
39
+ before do
40
+ allow(model_resolver).to receive(:resolve_model)
41
+ allow(entity_reader).to receive(:entity) { build(:entity) }
42
+ allow(File).to receive(:read) { file_str }
43
+ end
44
+
45
+ describe '#parse_model' do
46
+ it 'resolves the model contents' do
47
+ expect(model_resolver).to receive(:resolve_model).with('path')
48
+
49
+ subject.model('path')
50
+ end
51
+
52
+ it 'maps the model' do
53
+ expect(entity_reader).to receive(:entity).exactly(3).times
54
+
55
+ subject.model('path')
56
+ end
57
+
58
+ it 'returns an array of entities' do
59
+ expect(subject.model('path')).to eq([build(:entity), build(:entity), build(:entity)])
60
+ end
61
+
62
+ it 'raises an assertion if path is nil' do
63
+ expect { subject.model(nil) }.to raise_exception
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'moblues/reader/relationship'
3
+
4
+ describe Moblues::Reader::Relationship do
5
+ describe '#relationship' do
6
+ context 'when XML is valid' do
7
+ let(:xml) { REXML::Document.new('<relationship name="testName" toMany="YES" destinationEntity="TestEntity" ordered="YES"/>').elements.first }
8
+
9
+ it 'returns a Relationship' do
10
+ expect(subject.relationship(xml)).to eq(Moblues::DataModel::Relationship.new(name: 'testName', destination_entity: 'TestEntity', to_many: true, ordered: true))
11
+ end
12
+ end
13
+
14
+ context 'when XML is invalid' do
15
+ let(:xml) { REXML::Document.new('<notARelationship/>').elements.first }
16
+
17
+ it 'raises an exception' do
18
+ expect { subject.relationship(xml) }.to raise_exception
19
+ end
20
+ end
21
+ end
22
+ end