contentful-database-importer 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 98d3aacd7022ac369a2b9b81cd21048d52d1da11
4
+ data.tar.gz: 063b897d2875b9ac709da9f930035f108b41c135
5
+ SHA512:
6
+ metadata.gz: 9b3a9767b6a65e6e5fc9ad2efb26e68190411b358f35e9fa25e8075cbfbae1943e6851f759447d76a819dd9f1baa91e610e86af3277c970e274e914c2309f585
7
+ data.tar.gz: 99db145e30c2adf4bfb8b3e07c49d5626f6576f331aaccd7ed41a142eacbe0a336ecd074671667cea63ffb5cb25ee7407d4908275b9ab26ebad12fc6ebedacca
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.5
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at david.litvakb@gmail.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,43 @@
1
+ # Contributing
2
+ In the spirit of [free software][free-sw], **everyone** is encouraged to help
3
+ improve this project.
4
+
5
+ [free-sw]: http://www.fsf.org/licensing/essays/free-sw.html
6
+
7
+ Here are some ways *you* can contribute:
8
+
9
+ * by using alpha, beta, and prerelease versions
10
+ * by reporting bugs
11
+ * by suggesting new features
12
+ * by writing or editing documentation
13
+ * by writing specifications
14
+ * by writing code ( **no patch is too small** : fix typos, add comments, clean up inconsistent whitespace )
15
+ * by refactoring code
16
+ * by closing [issues][]
17
+ * by reviewing patches
18
+
19
+ [issues]: https://github.com/contentful/contentful-database-importer.rb/issues
20
+
21
+ ## Submitting an Issue
22
+ We use the [GitHub issue tracker][issues] to track bugs and features. Before
23
+ submitting a bug report or feature request, check to make sure it hasn't
24
+ already been submitted. When submitting a bug report, please include a [Gist][]
25
+ that includes a stack trace and any details that may be necessary to reproduce
26
+ the bug, including your gem version, Ruby version, and operating system.
27
+ Ideally, a bug report should include a pull request with failing specs.
28
+
29
+ [gist]: https://gist.github.com/
30
+
31
+ ## Submitting a Pull Request
32
+ 1. [Fork the repository.][fork]
33
+ 2. [Create a topic branch.][branch]
34
+ 3. Add specs for your unimplemented feature or bug fix.
35
+ 4. Run `bundle exec rake test`. If your specs pass, return to step 3.
36
+ 5. Implement your feature or bug fix.
37
+ 6. Run `bundle exec rake test`. If your specs fail, return to step 5.
38
+ 7. Add, commit, and push your changes.
39
+ 8. [Submit a pull request.][pr]
40
+
41
+ [fork]: http://help.github.com/fork-a-repo/
42
+ [branch]: http://learn.github.com/p/branching.html
43
+ [pr]: http://help.github.com/send-pull-requests/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bootstrap-database.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,14 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ # RSpec files
6
+ rspec = dsl.rspec
7
+ watch(rspec.spec_helper) { rspec.spec_dir }
8
+ watch(rspec.spec_support) { rspec.spec_dir }
9
+ watch(rspec.spec_files)
10
+
11
+ # Ruby files
12
+ ruby = dsl.ruby
13
+ dsl.watch_spec_files_for(ruby.lib_files)
14
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Contentful GmbH - David Litvak Bruno
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,253 @@
1
+ # Contentful Database Importer
2
+
3
+ ![TravisCI](https://travis-ci.org/contentful/contentful-database-importer.rb.svg?branch=master)
4
+
5
+ A simple DSL to define your database schemas, their relation to Contentful and import them to Contentful.
6
+
7
+ This gem is intended to be a replacement to [`database_exporter`](https://github.com/contentful/database-exporter.rb). _Warning_: Both gems are incompatible.
8
+
9
+ ## Contentful
10
+
11
+ [Contentful](http://www.contentful.com) is a content management platform for web applications, mobile apps and connected devices.
12
+ It allows you to create, edit and manage content in the cloud and publish it anywhere via a powerful API.
13
+ Contentful offers tools for managing editorial teams and enabling cooperation between organizations.
14
+
15
+ ## What does `contentful-database-importer` do?
16
+
17
+ `contentful-database-importer` let's you define mapping classes between your database and Contentful and allows
18
+ you to generate a JSON file that's a valid [`contentful_bootstrap`](https://github.com/contentful/contentful-bootstrap.rb) JSON Template,
19
+ or directly import to Contentful, creating a new space and using your data to populate the content.
20
+
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ gem install contentful-database-importer
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ * Create a new directory for your import configuration:
31
+
32
+ ```bash
33
+ mkdir my_importer_dir && cd my_importer_dir
34
+ ```
35
+
36
+ * Create a _`Gemfile`_ with the gem:
37
+
38
+ ```ruby
39
+ source 'https://rubygems.org'
40
+
41
+ gem 'contentful-database-importer'
42
+ ```
43
+
44
+ * Create your importer file, for example _`import.rb`_:
45
+
46
+ ```ruby
47
+ require 'contentful/database_importer'
48
+
49
+ class MyTable
50
+ include Contentful::DatabaseImporter::Resource
51
+
52
+ # ... your schema definition ... (explained in next section)
53
+ end
54
+
55
+ # ... more table definitions ...
56
+
57
+ Contentful::DatabaseImporter.setup do |config|
58
+ config.space_name = 'My Cool New Space'
59
+ config.database_connection = 'postgres://user:pass@host:port'
60
+ end
61
+
62
+ Contentful::DatabaseImporter.run!
63
+ ```
64
+
65
+ * Run your file:
66
+
67
+ ```bash
68
+ bundle exec ruby import.rb
69
+ ```
70
+
71
+ ### Defining your Schema
72
+
73
+ ```ruby
74
+ class MyTable
75
+ include Contentful::DatabaseImporter::Resource
76
+
77
+ self.table_name = 'overrides_table_name' # Optional - By default it's the class name in snake case. E.g. 'my_table'
78
+ self.content_type_id = 'overrides_content_type_id' # Optional - By default it's the class name in snake case. E.g 'my_table'
79
+ self.content_type_name = 'Overrides Name' # Optional - By default it's the class name
80
+
81
+ id Contentful::DatabaseImporter::IdGenerator::Base, template: '{{content_type_id}}_{{foo}}_{{index}}' # Optional - By default it's the IdGenerator::Base(template: '{{content_type_id}}_{{index}}')
82
+
83
+ field :foo, type: :string
84
+ field :bar, maps_to: :not_bar, type: :string
85
+ field :image, type: :asset
86
+ end
87
+ ```
88
+
89
+ #### Overriding Table and Content Type ID
90
+
91
+ The methods `::table_name=` and `::content_type_id=` allow you to override the IDs for either the table or content type.
92
+ By default, they are a `snake_cased` version of the class name.
93
+
94
+ #### Defining the ID generator
95
+
96
+ You can define the ID generation strategy, there are 2 classes currently provided:
97
+
98
+ - `Contentful::DatabaseImporter::IdGenerator::Base`: Provides a very basic template engine for generating IDs, this is the default strategy.
99
+ - `Contentful::DatabaseImporter::IdGenerator::ContentfulLike`: Provides a Base62 encode that produces IDs similar to the Contentful provided ones,
100
+ uses the `Base` strategy, then pads it to a minimum length and then Base62 encode it.
101
+
102
+ ##### ID Templates
103
+
104
+ Theres a minimal template engine provided for the ID Generators.
105
+ A single template looks like `{{foo}}_{{bar}}` and works by replacing the values enclosed between `{{}}` with the corresponding value for each entry.
106
+
107
+ There are a few variables globally provided for every class (and will be looked up before the object fields):
108
+
109
+ - `class_name`: The name of the mapping class
110
+ - `table_name`: The defined table name (or the default)
111
+ - `content_type_id`: The defined content type ID (or the default)
112
+ - `index`: The position of the entry (0-based) on the database table
113
+
114
+ After those globally provided values, you can use the value for any field on the mapping class (using the `:maps_to` value if present).
115
+
116
+ For example, using the example template above, if the DB record looks like:
117
+
118
+ ```ruby
119
+ {foo: 'something', bar: 'else', image: 'https://example.com/happycat.jpg'}
120
+ ```
121
+
122
+ Then the resulting template will be `something_else`
123
+
124
+ **Note**: With relationships, it's useful to use a unique identifier value as part of the ID template.
125
+
126
+ #### Defining Fields
127
+
128
+ For defining the field you have the `::field(name, options = {})` method. It defines how to retrieve and later serialize the field.
129
+
130
+ The options are:
131
+
132
+ - `type: type_name`: **Required** for coercions. Types defined below.
133
+ - `maps_to: name`: **Optional**. Defaults to field name, and defines the field name in Contentful
134
+ - `pre_process: lambda_or_symbol`: **Optional**. Described below.
135
+ - `exclude_from_output: boolean`: **Optional**. Defaults to false. Defines if the field will not be uploaded to Contentful. Useful for ID generation.
136
+
137
+ ##### Regular Field Types
138
+
139
+ - `:symbol`, `:string`: Short text field (255 characters maximum) in Contentful.
140
+ - `:text`: Long text field.
141
+ - `:number`: Floating point precision number.
142
+ - `:integer`: Integer number.
143
+ - `:boolean`: Boolean.
144
+ - `:location`: Geographical Location (can be coerced from a String, Hash or Array.)
145
+ - `:date`: An ISO8601 Date (can be coerced from a Date/DateTime object or String).
146
+ - `:object`: A JSON Object. _Currently not supported_
147
+ - `:asset`: A File description. A String containing the file URL needs to be provided.
148
+ - `:array`: An Array of elements.
149
+
150
+ In the case of using `:array`, an extra parameter `item_type: type` must be provided.
151
+
152
+ ##### Relationship Field Types
153
+
154
+ If your data has a relationship field, the `type:` value will be the related class, and will require additional parameters specifying
155
+ the relationship type and keys for retrieving the appropiate data.
156
+
157
+ For example:
158
+
159
+ ```ruby
160
+ class Foo
161
+ field :bars, type: Bar, relationship: :many, id_field: :id, key: :foo_id
162
+ field :baz, type: Baz, relationship: :one, id_field: :id, key: :baz_id
163
+ field :quxs, type: Qux, relationship: :many_through, through: :foo_qux, primary_id_field: :id, primary_key: :foo_id, foreign_key: :qux_id, foreign_id_field: :id
164
+ end
165
+ ```
166
+
167
+ In Contentful, relationships are unidirectional, and if you want bidirectional relationships, you need to declare them in both classes.
168
+
169
+ Relationship fields have the particularity that they don't require the `:maps_to` property, as Contentful will always use
170
+ the field name for the property in Contentful. You define the name of the field in the database with relationship specific parameters.
171
+
172
+ Relationship Types:
173
+
174
+ - `:many`: One to Many relationship, looks for all related objects of the associated class that match the value of the `:id_field` via the `:key`.
175
+ In the example above, it will look for all `Bar` entries which have a `:foo_id` that match the value of `:id` for the current `Foo` entry.
176
+ - `:one`: One to One relationship, looks for the related object of the associated class that matches the value of the `:key` field in the current entry, with the value of `:id_field` in the related entry.
177
+ In the example above, it will look for the `Baz` entry which has an ID that matches the value of `:baz_id` in the current entry.
178
+ - `:many_through`: Many to Many relationship, looks for the related object through an intermediate lookup table, after this it behaves like `:many`.
179
+ In the example above, it will look for all `Qux` entries found in the intermediate table that match the current entry `:id` and looks it up via the `Qux`s `:id`.
180
+
181
+ **Note**: If you're using relationships, use a custom ID Generator template which includes a unique field for each entry,
182
+ that way, creating the links in Contentful will be successful. This requires including the field in the class definition.
183
+
184
+ For example: `'{{content_type_id}}_{{id}}'`
185
+
186
+ **Note**: Ruby requires a class to be defined before using it as a parameter, therefore, you should declare all classes that
187
+ are contained within others, before the one in which you use them. If you want to have circular relationships, you need to define a Merge Class pointing to the same table and content type as the desired class (defined below).
188
+
189
+ ##### Pre-processing
190
+
191
+ If you want to transform your data before uploading to Contentful,
192
+ you can use the `:pre_process` parameter in the `::field` definition.
193
+
194
+ The pre-process value can be a lambda function (E.g. `-> (value) { value + 1 }`) or a symbol (E.g. `:pre_process_foo`).
195
+
196
+ If you use a lambda function, it must receive a single parameter and return a single value.
197
+
198
+ If you use a Symbol, it must match the name of a method defined within the class you're calling it from.
199
+ This method must receive a single parameter and return a single value.
200
+
201
+ ### Merging Tables
202
+
203
+ You might want to merge the content of multiple tables into a single content type.
204
+
205
+ This is supported by default, but ensure that the classes have the same `content_type_id` defined and if you need to
206
+ merge the entries as well, that the ID generator template is set in a way that can match the values from the different classes.
207
+
208
+ In the case you want to create multiple content types from a single table, the same concepts apply.
209
+
210
+ In the case of circular references, you will have to create 2 or more classes pointing to the same table and content type, the same concepts apply.
211
+
212
+ **Note**: Merge classes require at least 1 field declared, even if it's excluded from output.
213
+
214
+ ### Configuration
215
+
216
+ ```ruby
217
+ Contentful::DatabaseImporter.setup do |config|
218
+ config.space_name = 'My Cool New Space' # Required - the destination space name
219
+ config.database_connection = 'postgres://user:pass@host:port' # Required - the DB Connection string
220
+ end
221
+ ```
222
+
223
+ `database_connection` allows the following Database URI Strings:
224
+
225
+ - **[Postgres (Section 31.1.1.2)](https://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING)**: E.g. `'postgres://user:password@host:port/database_name'`.
226
+ - **SQlite**: E.g. `'sqlite://file_path.db'`.
227
+ - **MySQL**: E.g. `'mysql://user:password@host:post/database_name'`.
228
+
229
+ ### Running the Import Tool
230
+
231
+ You can do any of the following operations:
232
+
233
+ * Generate a JSON Template as a Ruby Hash for reuse within the script:
234
+
235
+ ```ruby
236
+ Contentful::DatabaseImporter.generate_json
237
+ ```
238
+
239
+ * Generate JSON Template as a prettyfied JSON string:
240
+
241
+ ```ruby
242
+ Contentful::DatabaseImporter.generate_json!
243
+ ```
244
+
245
+ * Generate the JSON and Import it to Contentful (creates a Space with all the content):
246
+
247
+ ```ruby
248
+ Contentful::DatabaseImporter.run!
249
+ ```
250
+
251
+ ## Contributing
252
+
253
+ Feel free to improve this tool by submitting a Pull Request. For more information, please read [CONTRIBUTING.md](./CONTRIBUTING.md)
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'contentful/database_importer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "contentful-database-importer"
8
+ spec.version = Contentful::DatabaseImporter::VERSION
9
+ spec.authors = ["Contentful GmbH (David Litvak Bruno)"]
10
+ spec.email = ["david.litvak@contentful.com"]
11
+
12
+ spec.summary = %q{Tool to import content from a Database to Contentful}
13
+ spec.description = %q{Tool to import content from a Database to Contentful}
14
+ spec.homepage = "https://contentful.com"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'contentful_bootstrap', '~> 3.0'
23
+ spec.add_dependency 'contentful-management', '~> 0.9'
24
+ spec.add_dependency 'sequel', '~> 4.39'
25
+ spec.add_dependency 'base62-rb'
26
+ spec.add_dependency 'mimemagic'
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.12"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+ spec.add_development_dependency 'rubocop'
32
+ spec.add_development_dependency 'simplecov'
33
+ spec.add_development_dependency 'guard'
34
+ spec.add_development_dependency 'guard-rspec'
35
+ spec.add_development_dependency 'pry'
36
+ end
@@ -0,0 +1,12 @@
1
+ module Contentful
2
+ module DatabaseImporter
3
+ # Configuration for Importer
4
+ class Config
5
+ attr_accessor :space_name, :database_connection
6
+
7
+ def complete?
8
+ !space_name.nil? && !database_connection.nil?
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,37 @@
1
+ module Contentful
2
+ module DatabaseImporter
3
+ module IdGenerator
4
+ # Base Id Generator
5
+ class Base
6
+ attr_reader :options
7
+
8
+ def initialize(options = {})
9
+ @options = options
10
+ end
11
+
12
+ def run(entry_data, index)
13
+ options.fetch(:template, '').gsub(/\{\{([\w|\.]+)\}\}/) do |match|
14
+ find(entry_data, index, match.gsub('{{', '').gsub('}}', '').to_sym)
15
+ end
16
+ end
17
+
18
+ def find(entry_data, index, match)
19
+ return options[match] if options.key?(match)
20
+ return index.to_s if match == :index
21
+
22
+ find_on_entry(entry_data, match)
23
+ end
24
+
25
+ def find_on_entry(entry_data, match)
26
+ if entry_data.excluded_fields.key?(match)
27
+ return entry_data.excluded_fields[match]
28
+ elsif entry_data.bootstrap_fields.key?(match)
29
+ return entry_data.bootstrap_fields[match]
30
+ end
31
+
32
+ raise "Template could not be resolved, #{match} not found."
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ require 'contentful/database_importer/id_generator/base'
2
+ require 'base62-rb'
3
+
4
+ module Contentful
5
+ module DatabaseImporter
6
+ module IdGenerator
7
+ # Base62 Encoded Id Generator
8
+ class ContentfulLike < Base
9
+ def run(entry_data, index)
10
+ result = ''
11
+ id = super(entry_data, index)
12
+ id.each_char do |c|
13
+ result << c.ord.to_s
14
+ end
15
+
16
+ result << '9' while result.size < 40
17
+
18
+ Base62.encode(result.to_i)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,2 @@
1
+ require 'contentful/database_importer/id_generator/base'
2
+ require 'contentful/database_importer/id_generator/contentful_like'
@@ -0,0 +1,82 @@
1
+ require 'json'
2
+
3
+ module Contentful
4
+ module DatabaseImporter
5
+ # Json File Generator
6
+ module JsonGenerator
7
+ def self.generate_json
8
+ result = { version: 3, contentTypes: [],
9
+ assets: [], entries: {} }
10
+
11
+ resources.each do |resource|
12
+ content_type_definition = resource.content_type_definition
13
+ create_content_type(content_type_definition, result)
14
+ create_entries(resource, content_type_definition, result)
15
+ end
16
+
17
+ result
18
+ end
19
+
20
+ def self.resources
21
+ ObjectSpace.each_object(Class).select do |c|
22
+ c.included_modules.include? Resource
23
+ end
24
+ end
25
+
26
+ def self.create_content_type(content_type_definition, result)
27
+ prev_ct_definition = result[:contentTypes].find do |ct|
28
+ ct[:id] == content_type_definition[:id]
29
+ end
30
+
31
+ if prev_ct_definition
32
+ result[:contentTypes].delete(prev_ct_definition)
33
+ content_type_definition.merge!(prev_ct_definition)
34
+ end
35
+
36
+ result[:contentTypes] << content_type_definition
37
+ end
38
+
39
+ def self.create_entries(resource, content_type_definition, result)
40
+ result[:entries][content_type_definition[:id]] ||= []
41
+ resource.all.each do |entry|
42
+ create_entry(entry, content_type_definition, result)
43
+ end
44
+ end
45
+
46
+ def self.previous_entry(entry_definition, content_type_definition, result)
47
+ result[:entries][content_type_definition[:id]].find do |e|
48
+ e[:sys][:id] == entry_definition[:sys][:id]
49
+ end
50
+ end
51
+
52
+ def self.merge_entries(entry_definition, content_type_definition, result)
53
+ prev_entry = previous_entry(
54
+ entry_definition,
55
+ content_type_definition,
56
+ result
57
+ )
58
+
59
+ return unless prev_entry
60
+
61
+ result[:entries][content_type_definition[:id]].delete(prev_entry)
62
+ entry_definition.merge!(prev_entry)
63
+ end
64
+
65
+ def self.create_entry(entry, content_type_definition, result)
66
+ entry_definition = entry.to_bootstrap
67
+
68
+ merge_entries(entry_definition, content_type_definition, result)
69
+
70
+ result[:assets].concat(
71
+ entry.associated_assets
72
+ ) unless entry.associated_assets.empty?
73
+
74
+ result[:entries][content_type_definition[:id]] << entry_definition
75
+ end
76
+
77
+ def self.generate_json!
78
+ JSON.pretty_generate(generate_json)
79
+ end
80
+ end
81
+ end
82
+ end