diff --git a/.rspec b/.rspec
index 83e16f8..e3dd828 100644
--- a/.rspec
+++ b/.rspec
@@ -1,2 +1,3 @@
--color
--require spec_helper
+--format d
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000..3513cd3
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,11 @@
+# If you use `rubocop --auto-gen-config, you might want to then uncomment the following line
+# inherit_from: .rubocop_todo.yml
+
+Metrics/ClassLength:
+ Max: 150
+
+Metrics/LineLength:
+ Max: 120
+
+Metrics/BlockLength:
+ Max: 80
diff --git a/Gemfile b/Gemfile
index 06f5d64..c5dc06d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,7 +1,8 @@
source 'https://rubygems.org'
-gem 'nokogiri'
+gemspec
group :test do
gem 'rspec'
+ gem 'rubocop', '~> 0.47'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 692a6c4..b410013 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,30 +1,56 @@
+PATH
+ remote: .
+ specs:
+ gyro (1.0.0)
+ liquid (~> 3.0)
+ nokogiri (~> 1.6.8)
+
GEM
remote: https://rubygems.org/
specs:
- diff-lcs (1.2.5)
- mini_portile (0.6.2)
- nokogiri (1.6.6.2)
- mini_portile (~> 0.6.0)
- rspec (3.3.0)
- rspec-core (~> 3.3.0)
- rspec-expectations (~> 3.3.0)
- rspec-mocks (~> 3.3.0)
- rspec-core (3.3.2)
- rspec-support (~> 3.3.0)
- rspec-expectations (3.3.1)
+ ast (2.3.0)
+ diff-lcs (1.3)
+ liquid (3.0.6)
+ mini_portile2 (2.1.0)
+ nokogiri (1.6.8.1)
+ mini_portile2 (~> 2.1.0)
+ parallel (1.11.2)
+ parser (2.4.0.0)
+ ast (~> 2.2)
+ powerpack (0.1.1)
+ rainbow (2.2.2)
+ rake
+ rake (12.0.0)
+ rspec (3.6.0)
+ rspec-core (~> 3.6.0)
+ rspec-expectations (~> 3.6.0)
+ rspec-mocks (~> 3.6.0)
+ rspec-core (3.6.0)
+ rspec-support (~> 3.6.0)
+ rspec-expectations (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.3.0)
- rspec-mocks (3.3.2)
+ rspec-support (~> 3.6.0)
+ rspec-mocks (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.3.0)
- rspec-support (3.3.0)
+ rspec-support (~> 3.6.0)
+ rspec-support (3.6.0)
+ rubocop (0.49.1)
+ parallel (~> 1.10)
+ parser (>= 2.3.3.1, < 3.0)
+ powerpack (~> 0.1)
+ rainbow (>= 1.99.1, < 3.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (~> 1.0, >= 1.0.1)
+ ruby-progressbar (1.8.1)
+ unicode-display_width (1.2.1)
PLATFORMS
ruby
DEPENDENCIES
- nokogiri
+ gyro!
rspec
+ rubocop (~> 0.47)
BUNDLED WITH
- 1.14.5
+ 1.13.7
diff --git a/README.md b/README.md
index 4ce7f1e..65826e5 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,11 @@
+
# Gyro
-Gyro is a tool to generate [Realm](https://realm.io) model classes, for both Android (Java) and iOS/macOS (ObjC & Swift), from an `.xcdatamodel` file.
+[](https://rubygems.org/gems/gyro)
+[](https://circleci.com/gh/NijiDigital/gyro)
+[](http://twitter.com/Niji_Digital)
+
+Gyro is a tool to generate [Realm](https://realm.io) model classes, for both Android (Java) and iOS/macOS (Swift), from an `.xcdatamodel` file.
---
@@ -25,14 +30,6 @@ This will allow you to design your model in a visual way (rather than by code),
The `.xcdatamodel` file is the input of the script.
-
-## License
-
-This tool is under [the Apache 2 License](LICENSE).
-
-It has been initially developed by [Niji](http://www.niji.fr) and is in no way affiliated to the [Realm](https://realm.io) company.
-
-
## Installation
Gyro is on RubyGems, so this means you can simply install it by using this command in your terminal:
@@ -41,37 +38,33 @@ Gyro is on RubyGems, so this means you can simply install it by using this comma
gem install gyro
```
-Then invoke it with the appropriate options (see next paragraph), like this:
+_Alternativly, you could also clone this repository anywhere you want on your machine, then build and install the local gem_
```bash
-gyro -m --ios ~/Dev/MyProject/RealmModel --swift
+gem build gyro.gemspec
+gem install gyro-1.0.0.gem
```
-_Alternativly, you could also simply clone this repository anywhere you want on your machine, then run the `bin/gyro` executable directly from where you cloned it_
+## Usage
+Invoke it with the appropriate options like this:
-## Command line arguments
-
-Gyro is a command line tool. The available parameters are as follows. You can also use `-h` do display the usage and available parameters/flags in the Terminal of course.
+```bash
+gyro --model --template --output --param :
+```
+`` is the path to the xcdatamodel file
-| Short flag | Long flag | Description | Android | iOS |
-| ---------- | --------- | ----------- |:-------:|:---:|
-| `-m` | `--model` | Path to the `.xcdatamodel` file. If this parameter is not given, Gyro will look for a `.xcdatamodel` | ✅ | ✅ |
-| `-a` | `--android` | Path to the directory where the generated files for Android will be created (e.g.: home/documents/dev/android/realm_project/com/niji/data) | ✅ | ➖ |
-| `-p` | `--package` | Full name of the Android "data" package (e.g.: com.niji.data) | ✅ | ➖ |
-| `-i` | `--ios` | Path to the directory where the generated files for iOS/macOS will be created | ➖ | ✅ |
-| `-j` | `--json` | Create the Realm-JSON categories (https://github.com/matthewcheok/Realm-JSON) | ➖ | ☑️ |
-| `-f` | `--framework` | Tells whether the project uses CocoaPods Frameworks | ➖ | ☑️ |
-| `-s` | `--swift` | Use Swift for the iOS/macOS generation | ➖ | ☑️ |
-| `-n` | `--nsnumber` | Generate `NSNumber`s instead of Int/BOOL/Float types | ➖ | ☑️ |
-| `-w` | `--wrappers` | Use type wrappers for Java (Integer, Double, …) for optional attributes instead of primitive types (int, double, …) | ☑️ | ➖ |
-| `-x` | `--annotations` | Annotate the getters/setters of the generated classes with `@Nullable` for any optional attribute/relationship, and with `@NonNull` for any non-optional attribute/relationship | ☑️ | ➖ |
-| `-h` | `--help` | Show help | ☑️ | ☑️ |
-| `-v` | `--version` | Show the current version number of Gyro | ☑️ | ☑️ |
+`` is the path to the output directory file
-_Caption: ✅ Mandatory flag for this platform / ☑️ Optional flag usable for this platform / ➖ Not applicable for this platform_
+`` is the name of the template. Below you have the list of templates.
+If you want additional information about templates you can go to the documentation for each :
+- [android](lib/templates/android/README.md)
+- [swift3](lib/templates/swift3/README.md)
+- [decodable](lib/templates/decodable/README.md)
+- [swift3-variant](lib/templates/swift3-variant/README.md)
+- [object-mapper](lib/templates/object-mapper/README.md)
## Annotating your `xcdatamodel`
@@ -79,776 +72,14 @@ The `.xcdatamodel` Xcode editor allows you to add "user infos" to your entities,
_To define a User Info key in Xcode's xcdatamodel editor, select the entity or attribute you want to add a User Info to, then select the 3rd tab in the inspector on the right ("Data Model Inspector", or Cmd-Alt-3), and fill the information you want in the "User Info" section there._
-With the help of these "user infos", you will be able to give Gyro extra information about your model classes. For example, you can tell which attribute is the primary key, the attributes to ignore, the JSON mappings, …
-
-Below are details about how to annotate your `.xcdatamodel` entities and attributes to be able to leverage each Realm features when generating your Realm models with Gyro.
-
-
----
-
-
-### Primary key
-
-To tell which attribute will be used as a primary key, add the following 'user info' to **the entity**:
-
-| Key | Value |
-|-----|-------|
-| `identityAttribute` | `name_of_the_attribute` |
-
-
-__Example__: On the "FidelityCard" entity:
-
-
-
-
-
-📑 Sample of the generated code in Java (Android)
-
-```java
-package com.niji.data;
-
-import io.realm.RealmObject;
-import io.realm.annotations.PrimaryKey;
-
-/* DO NOT EDIT | Generated by gyro */
-
-public class FidelityCard extends RealmObject {
-
- @PrimaryKey
- private short identifier;
- private int points;
- private User user;
- [...]
-}
-```
-
-
-
-📑 Sample of the generated code in Objective-C (iOS)
-
-```objc
-// DO NOT EDIT | Generated by gyro
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Imports
-
-#import "RLMFidelityCard.h"
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Implementation
-
-@implementation RLMFidelityCard
-
-#pragma mark - Superclass Overrides
-
-+ (NSString *)primaryKey
-{
- return @"identifier";
-}
-
-@end
-```
-
-
-
----
-
-
-### Ignore attribute
-
-You can decide to ignore some attributes of the `.xcdatamodel` file. They will not be persisted to Realm. To do so, add the following 'user info' to **the attribute**:
-
-| Key | Value |
-|-----|-------|
-| `realmIgnored` | `value` |
-
-
-__Example__: on the attribute `ignored` of the entity `Shop`:
-
-
-
-
-
-📑 Sample of the generated code in Java (Android)
-
-```java
-package com.niji.data;
-
-import io.realm.RealmList;
-import io.realm.RealmObject;
-import io.realm.annotations.Ignore;
-
-/* DO NOT EDIT | Generated by gyro */
-
-public class Shop extends RealmObject {
-
- private String name;
- private String readOnly;
- private RealmList products;
- @Ignore
- private String ignored;
- [...]
-}
-```
-
-
-
-📑 Sample of the generated code in Objective-C (iOS)
-
-```objc
-// DO NOT EDIT | Generated by gyro
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Imports
-
-#import "RLMShop.h"
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Implementation
-
-@implementation RLMShop
-
-#pragma mark - Superclass Overrides
-
-// Specify properties to ignore (Realm won't persist these)
-+ (NSArray *)ignoredProperties
-{
- return @[@"ignored"];
-}
-@end
-```
-
-
-
----
-
-
-### Read only
-
-On iOS/macOS, you can define attributes which are not persisted and whose value is computed dynamically.
-To do so, add the following 'user info' to **the attribute**
-
-| Key | Value |
-|-----|-------|
-| `realmReadOnly` | `the_code_source_to_generate` |
-
-
-__Example__: On the `readOnly` attribute of the `Shop` entity:
-
-
-
-
-
-📑 Sample of the generated code in Objective-C (iOS)
-
-```objc
-// DO NOT EDIT | Generated by gyro
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Imports
-
-#import "RLMShop.h"
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Implementation
-
-@implementation RLMShop
-
-#pragma mark - Superclass Overrides
-
-- (NSString *)readOnly
-{
- return self.name;
-}
-
-@end
-```
-
-
-
----
-
-
-### Inverse Relationships
-
-In realm, when you have both A -> B and B -> A relationships, you have to choose one of those relationships to be the primary one (e.g. A -> B) — that will be stored in Realm — and the other inverse relationship will then be **computed** by code. [For more information, see the related RealmSwift documentation on Inverse Relationships](https://realm.io/docs/swift/latest/#inverse-relationships).
-
-To mark a relationship as being an inverse relationship (the B -> A relationship and not the primary A -> B one), the convention in `gyro` is to **suffix the name of the relationship with an underscore `_`** .
-
-This will then generate the following code in Swift for that inverse relationship:
-
-```swift
-LinkingObjects(fromType: A.self, property: "b")`
-```
-
-If your inverse relationship is defined to point to a unique object (inverse of a `1-*` relationship for exemple, and not a `*-*` one), the generated code will contain both the plural form of the computed variable and a singular form returning its first element, for convenience:
-
-```swift
-let owners = LinkingObjects(fromType: Person.self, property: "dogs")`
-var owner: Person? { return owners.first }
-```
-
-
----
-
-
-### Optionnals fields and wrapper types
-
-On Android, the `-w`/`--wrappers` flag allows you to use wrapper types (`Double`, `Short`, …) for optional fields instead of primitive types (`double`, `short`, …).
-
-
-📑 Sample of the generated code in Java (Android)
-
-```java
-package com.niji.data;
-
-import io.realm.RealmObject;
-import io.realm.annotations.PrimaryKey;
-
-/* DO NOT EDIT | Generated by gyro */
-
-public class FidelityCard extends RealmObject {
-
- @PrimaryKey
- private short identifier; // "optional" checkbox not checked in the xcdatamodel
- private Integer points; // "optional" checkbox checked in the xcdatamodel
- private User user;
- [...]
-}
-```
-
-
-
----
-
-
-### Support Annotations
-
-On Android, the flag `-x`/`--annotations` allows you to annotate class attributes' getters & setters with `@Nullable` (if the attribute is optional) or `@NonNull` (if it isn't) attributes.
-This option can be combined with the `-w` wrapper flag to generate a safer and more secure code in Android Studio, generating proper warnings if misused.
-
-
-📑 Sample of the generated code in Java (Android)
-
-```java
-package com.niji.data;
-
-import io.realm.RealmObject;
-import io.realm.annotations.PrimaryKey;
-
-/* DO NOT EDIT | Generated by gyro */
-
-public class FidelityCard extends RealmObject {
-
- @PrimaryKey
- private short identifier; // "optional" checkbox not checked in the xcdatamodel
- private Integer points; // "optional" checkbox checked in the xcdatamodel
- private User user;
- [...]
-
- @android.support.annotation.Nullable
- public Integer getPoints() {
- return points;
- }
-
- public void setPoints(@android.support.annotation.Nullable final Integer points) {
- this.points = points;
- }
-
-}
-```
-
-
-Furthermore, it's possible to add custom annotations to your fields.
-To do that, simply add the key/value pair to the UserInfos of the attribute to annotate:
-
-| Key | Value |
-|-----|-------|
-| `supportAnnotation` | `AnnotationToAdd` |
-
-
-__Example__: If you wish to add the `IntRange(from=0,to=255)` annotation to an attribute, use the following:
-
-
-
-
-
-📑 Sample of the generated code in Java (Android)
-
-```java
-package com.gyro.tests;
-
-import io.realm.RealmObject;
-
-/* DO NOT EDIT | Generated by gyro */
-
-public class FidelityCard extends RealmObject {
-
- public interface Attributes {
- String IDENTIFIER = "identifier";
- String POINTS = "points";
- }
-
- private short identifier;
- @android.support.annotation.IntRange(from=0,to=255)
- private int points;
-
- public short getIdentifier() {
- return identifier;
- }
-
- public void setIdentifier(final short identifier) {
- this.identifier = identifier;
- }
-
- @android.support.annotation.IntRange(from=0,to=255)
- public int getPoints() {
- return points;
- }
-
- public void setPoints(@android.support.annotation.IntRange(from=0,to=255) final int points) {
- this.points = points;
- }
-}
-```
-
-
-
----
-
-
-### Handling enums
-
-Sometimes, an `Int` attribute in the model actually represents an `enum` member in your model. To deal with this case, you can add the following two key/value pairs to this **attribute**:
-
-| Key | Value |
-|-----|-------|
-| `enumType` | `my_type` |
-| `enumValues` | `my_value_1, my_value_2, my_value_3` |
-
-> _Note: If you also add the `JSONKeyPath` User Info key to your attribute in addition to enums, you'll have to add the `JSONValues` to also tell the mapping between the `enumValues` and the matching possible values found in the JSON. See the [JSON Mapping](#json-mapping) below for more details._
-
-__Example__: On the attribute `type` of the `Shop` entity.
-
-
-
-
-
-📑 Sample of the generated code in Java (Android)
-
-`Shop.java`:
-
-```java
-package com.niji.data;
-
-import io.realm.RealmObject;
-
-/* DO NOT EDIT | Generated by gyro */
-
-public class Shop extends RealmObject {
- private String name;
- private Type type;
- [...]
-}
-```
-
-`Type.java`:
-
-```java
-package com.niji.data;
-
-/* DO NOT EDIT | Generated by gyro */
-
-public enum Type {
- TYPE_ONE,
- TYPE_TWO,
- TYPE_THREE
-}
-```
-
-
-
-📑 Sample of the generated code in Objective-C (iOS)
-
-`RLMShop.h`:
-
-```objc
-// DO NOT EDIT | Generated by gyro
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Imports
-
-#import
-#import "RLMTypes.h"
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Interface
-
-@interface RLMShop : RLMObject
-
-#pragma mark - Properties
-
-@property NSString *name;
-@property RLMType type;
-
-@end
-```
-
-`RLMTypes.h`:
-
-```objc
-// DO NOT EDIT | Generated by gyro
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Types
-
-typedef NS_ENUM(int, RLMType) {
- RLMTypeOne = 0,
- RLMTypeTwo,
- RLMTypeThree
-};
-```
-
-
-
-📑 Sample of the generated code in Swift (iOS)
-
-`Shop.swift`:
-
-```swift
-/* DO NOT EDIT | Generated by gyro */
-
-import RealmSwift
-
-final class Shop: Object {
-
- enum Attributes: String {
- case Name = "name"
- case OptionalValue = "optionalValue"
- case Type = "type"
- }
-
- dynamic var name: String = ""
- dynamic var optionalValue: String? = nil
- var optionalValueEnum: OptValue? {
- get {
- guard let optionalValue = optionalValue,
- let enumValue = OptValue(rawValue: optionalValue)
- else { return nil }
- return enumValue
- }
- set { optionalValue = newValue?.rawValue ?? nil }
- }
-
- dynamic var type: String = ""
- var typeEnum: Type? {
- get {
- guard let enumValue = Type(rawValue: type) else { return nil }
- return enumValue
- }
- set { type = newValue?.rawValue ?? "" }
- }
-}
-```
-
-`Type.swift`:
-
-```swift
-/* DO NOT EDIT | Generated by gyro */
-
-enum Type: String {
- case TypeOne = "type_one"
- case TypeTwo = "type_two"
- case TypeThree = "type_three"
-}
-```
-
-`OptValue.swift`
-
-```swift
-/* DO NOT EDIT | Generated by gyro */
-
-enum OptValue: String {
- case OptValueOne = "opt_value_one"
- case OptValueTwo = "opt_value_two"
- case OptValueThree = "opt_value_three"
-}
-```
-
-
-> **Note**: For Android and Swift, each enum is created in a separate file. For ObjC, all the enums are created in the file RLMTypes.h
-
-
----
-
-
-### Add comments to the generated classes
-
-To make the generated code more readable, it's possible to add comments on an entity — e.g. to provide a short description of what this entity is supposed to represent.
-
-To do so, simply add the following key/value pair to your **entity** in your `.xcdatamodel`:
-
-| Key | Value |
-|-----|-------|
-| `comment` | `the_comment_text_here` |
-
-A code commend (`/** … */`) will then be generated (in the `.h` (ObjC), `.swift` (Swift) or `.java` (Android)) just before the class declaration, e.g. to help the developer understand what this class is for.
-
-
----
-
-
-### JSON Mapping
-
-You can also add the json mapping for each **attribute** or **relationship** with the following key/value pair:
-
-| Key | Value |
-|-----|-------|
-| `JSONKeyPath` | `json_field_name` |
-
-This key is only used when using the `--json` flag.
-
-Currently, this will then generate:
-
- * Code for `ObjectMapper` on iOS (in the future we plan to generate `Sourcery` annotations instead so that people can choose whatever JSON library they prefer).
- * `GSON` annotations (`@SerializedName(…)`) for Android
-
-__Example__: On the 'name' attribute of the 'Shop' entity:
-
-
-
-
-
-📑 Sample of the generated code in Java (Android)
-
-Sur Android, nous utilisons la librairie GSON
-
-```java
-package com.niji.data;
-
-import com.google.gson.annotations.SerializedName;
-
-import io.realm.RealmList;
-import io.realm.RealmObject;
-
-/* DO NOT EDIT | Generated by gyro */
-
-public class Shop extends RealmObject {
-
- @SerializedName("json_name")
- private String name;
- private RealmList products;
- [...]
-}
-```
-
-
-
-📑 Sample of the generated code in Objective-C (iOS)
-
-On iOS, we use the Realm-JSON library and generate them in `MyEntity+JSON.m` category files.
-
-`RLMShop+JSON.m`:
-
-```objc
-// DO NOT EDIT | Generated by gyro
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Imports
-
-#import "RLMShop+JSON.h"
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Implementation
-
-@implementation RLMShop (JSON)
-
-+ (NSDictionary *)JSONInboundMappingDictionary
-{
- return @{
- @"json_name" : @"name"
- };
-}
-
-+ (NSDictionary *)JSONOutboundMappingDictionary
-{
- return @{
- @"name" : @"json_name"
- };
-}
-
-@end
-```
-
-
-#### Combine JSONKeyPath and enums
-
-Note that you can **combine that `JSONKeyPath` key with enums** (see [Handling enums](#handling-enums) above). If you declared the User Info keys to make your attribute an enum (`enumType` + `enumValues`) in addition to `JSONKeyPath`, you'll have to also add the `JSONValues` key to list the corresponding values in the JSON for those `enumValues`.
-
-| Key | Value |
-|-----|-------|
-| `JSONValues` | `valeur_json_1,valeur_json_2,valeur_json_3` |
-
-The number of items listed for that `JSONValues` key must be the same as the number of items listed for the `enumValues` keys, obviously.
-
-__Example__:
-

+With the help of these "user infos", you will be able to give Gyro extra information about your model classes. For example, you can tell which attribute is the primary key, the attributes to ignore, the JSON mappings, …
-
-📑 Sample of the generated code in Java (Android)
-
-```java
-package com.niji.data;
-
-import com.google.gson.annotations.SerializedName;
-
-/* DO NOT EDIT | Generated by gyro */
-
-public enum Type {
-
- @SerializedName("json_type_one")TYPE_ONE,
- @SerializedName("json_type_two")TYPE_TWO,
- @SerializedName("json_type_three")TYPE_THREE
-}
-```
-
-
-
-📑 Sample of the generated code in Objective-C (iOS)
-
-`RLMShop+JSON.m`:
-
-```objc
-// DO NOT EDIT | Generated by gyro
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Imports
-
-#import "RLMShop+JSON.h"
-#import "RLMTypes.h"
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Implementation
-
-@implementation RLMShop (JSON)
-
-+ (NSValueTransformer *)typeJSONTransformer
-{
- return [MCJSONValueTransformer valueTransformerWithMappingDictionary:@{
- @"json_type_one" : @(RLMTypeOne),
- @"json_type_two" : @(RLMTypeTwo),
- @"json_type_three" : @(RLMTypeThree)
- }];
-}
-
-@end
-```
-
-
-
----
-
-
-### Custom ValueTransformers
-
-Only available on iOS (as Android uses the GSON library), custom `ValueTransformers` allows you to e.g. convrt a `String` into an `Int` or a `Date` when parsing the JSON. They are only used when using the `--json` flag.
-
-To create a specific `ValueTransformer` for a field:
-
-* Create your `ValueTransformer` custom class inheriting `NSValueTransformer` and add it to your project
-* Select the attribute that will need this transformer, and in the UserInfo field, add a pair for the **transformer** key whose value should be the name of the `ValueTransformer` class to use:
-
-| Key | Value |
-|-----|-------|
-| `transformer` | `NameOfTheTransformerClass` |
-
-__Example__:
-
-
-
-
-
-📑 Sample of the generated code in Objective-C (iOS)
-
-`gyro` will produce the following code. (In this example, attributes `attrDouble` and `attrInteger32` don't have a **transformer** key set in their UserInfo).
-
-```objc
-// DO NOT EDIT | Generated by gyro
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Imports
-
-#import "RLMShop+JSON.h"
-#import "MPDecimalTransformer.h"
-#import "MPIntegerTransformer.h"
-
-////////////////////////////////////////////////////////////////////////////////
-
-#pragma mark - Implementation
-
-@implementation RLMShop (JSON)
-
-+ (NSDictionary *)JSONInboundMappingDictionary
-{
- return @{
- @"attrDecimal" : @"attrDecimal",
- @"attrDouble" : @"attrDouble",
- @"attrFloat" : @"attrFloat",
- @"attrInteger16" : @"attrInteger16",
- @"attrInteger32" : @"attrInteger32",
- @"attrInteger64" : @"attrInteger64"
- };
-}
-
-+ (NSDictionary *)JSONOutboundMappingDictionary
-{
- return @{
- @"attrDecimal" : @"attrDecimal",
- @"attrDouble" : @"attrDouble",
- @"attrFloat" : @"attrFloat",
- @"attrInteger16" : @"attrInteger16",
- @"attrInteger32" : @"attrInteger32",
- @"attrInteger64" : @"attrInteger64"
- };
-}
-
-+ (NSValueTransformer *)attrDecimalJSONTransformer
-{
- return [[MPDecimalTransformer alloc] init];
-}
-
-+ (NSValueTransformer *)attrFloatJSONTransformer
-{
- return [[MPDecimalTransformer alloc] init];
-}
-
-+ (NSValueTransformer *)attrInteger16JSONTransformer
-{
- return [[MPIntegerTransformer alloc] init];
-}
+**For more information about "user infos", you can see the dedicated documentation [here](USER-INFO.md).**
-+ (NSValueTransformer *)attrInteger64JSONTransformer
-{
- return [[MPIntegerTransformer alloc] init];
-}
+## License
-@end
+This tool is under [the Apache 2 License](LICENSE).
-```
-
+It has been initially developed by [Niji](http://www.niji.fr) and is in no way affiliated to the [Realm](https://realm.io) company.
diff --git a/USER-INFO.md b/USER-INFO.md
new file mode 100644
index 0000000..4d7c1f9
--- /dev/null
+++ b/USER-INFO.md
@@ -0,0 +1,703 @@
+
+# User info keys documentation
+
+Below are details about how to annotate your `.xcdatamodel` entities and attributes to be able to leverage each Realm features when generating your Realm models with Gyro.
+
+---
+
+### Summary :
+
+- [Primary key](#primary-key)
+- [Ignore attribute](#ignore-attribute)
+- [Read only](#read-only)
+- [Inverse Relationships](#inverse-relationships)
+- [Optionnals fields and wrapper types](#optionnals-fields-and-wrapper-types)
+- [Support Annotations](#support-annotations)
+- [Handling enums](#handling-enums)
+- [Add comments to the generated classes](#add-comments-to-the-generated-classes)
+- [JSON Mapping](#json-mapping)
+ - [Combine JSONKeyPath and enums](#combine-jsonkeypath-and-enums)
+- [Custom ValueTransformers](#custom-valuetransformers)
+
+---
+
+
+# Primary key
+
+To tell which attribute will be used as a primary key, add the following 'user info' to **the entity**:
+
+| Key | Value |
+|-----|-------|
+| `identityAttribute` | `name_of_the_attribute` |
+
+
+__Example__: On the "FidelityCard" entity:
+
+
+
+
+
+📑 Sample of the generated code in Java (Android)
+
+```java
+package com.gyro.tests;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+
+/* DO NOT EDIT | Generated by gyro */
+
+public class FidelityCard extends RealmObject {
+
+ [...]
+ @PrimaryKey
+ private short identifier;
+ [...]
+}
+```
+
+
+
+📑 Sample of the generated code in Swift (iOS)
+
+```swift
+/* DO NOT EDIT | Generated by gyro */
+
+import RealmSwift
+
+final class FidelityCard: Object {
+
+ [...]
+ dynamic var identifier: Int16 = 0
+
+ override static func primaryKey() -> String? {
+ return "identifier"
+ }
+
+}
+```
+
+
+
+---
+
+
+# Ignore attribute
+
+You can decide to ignore some attributes of the `.xcdatamodel` file. They will not be persisted to Realm. To do so, add the following 'user info' to **the attribute**:
+
+| Key | Value |
+|-----|-------|
+| `realmIgnored` | `value` |
+
+
+__Example__: on the attribute `ignored` of the entity `Shop`:
+
+
+
+
+
+📑 Sample of the generated code in Java (Android)
+
+```java
+package com.gyro.tests;
+
+import io.realm.RealmList;
+import io.realm.RealmObject;
+import io.realm.annotations.Ignore;
+
+/* DO NOT EDIT | Generated by gyro */
+
+public class Shop extends RealmObject {
+
+ [...]
+ @Ignore
+ private String ignored;
+ [...]
+}
+```
+
+
+
+📑 Sample of the generated code in Swift (iOS)
+
+```swift
+/* DO NOT EDIT | Generated by gyro */
+
+import RealmSwift
+
+final class Shop: Object {
+
+ dynamic var ignored: String = ""
+
+ // Specify properties to ignore (Realm won't persist these)
+ override static func ignoredProperties() -> [String] {
+ return ["ignored"]
+ }
+
+}
+
+```
+
+
+
+---
+
+
+# Read only (DEPRECATED)
+
+
+ Information about read only 'user info'
+On iOS/macOS, you can define attributes which are not persisted and whose value is computed dynamically.
+To do so, add the following 'user info' to **the attribute**
+
+| Key | Value |
+|-----|-------|
+| `realmReadOnly` | `the_code_source_to_generate` |
+
+
+__Example__: On the `readOnly` attribute of the `Shop` entity:
+
+
+
+
+
+📑 Sample of the generated code in Objective-C (iOS)
+
+```objc
+// DO NOT EDIT | Generated by gyro
+
+////////////////////////////////////////////////////////////////////////////////
+
+#pragma mark - Imports
+
+#import "RLMShop.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+#pragma mark - Implementation
+
+@implementation RLMShop
+
+#pragma mark - Superclass Overrides
+
+- (NSString *)readOnly
+{
+ return self.name;
+}
+
+@end
+```
+
+
+
+---
+
+
+# Inverse Relationships
+
+In realm, when you have both A -> B and B -> A relationships, you have to choose one of those relationships to be the primary one (e.g. A -> B) — that will be stored in Realm — and the other inverse relationship will then be **computed** by code. [For more information, see the related RealmSwift documentation on Inverse Relationships](https://realm.io/docs/swift/latest/#inverse-relationships).
+
+To mark a relationship as being an inverse relationship (the B -> A relationship and not the primary A -> B one), the convention in `gyro` is to **suffix the name of the relationship with an underscore `_`** .
+
+This will then generate the following code in Swift for that inverse relationship:
+
+```swift
+LinkingObjects(fromType: A.self, property: "b")`
+```
+
+If your inverse relationship is defined to point to a unique object (inverse of a `1-*` relationship for exemple, and not a `*-*` one), the generated code will contain both the plural form of the computed variable and a singular form returning its first element, for convenience:
+
+```swift
+let owners = LinkingObjects(fromType: Person.self, property: "dogs")
+var owner: Person? { return owners.first }
+```
+
+
+---
+
+
+# Optionnals fields and wrapper types
+
+On Android, the `-p use_wrappers` flag allows you to use wrapper types (`Double`, `Short`, …) for optional fields instead of primitive types (`double`, `short`, …).
+
+
+📑 Sample of the generated code in Java (Android)
+
+```java
+package com.gyro.tests;
+
+import io.realm.RealmObject;
+
+/* DO NOT EDIT | Generated by gyro */
+
+public class FidelityCard extends RealmObject {
+
+ @PrimaryKey
+ private short identifier; // "optional" checkbox not checked in the xcdatamodel
+ private Integer points; // "optional" checkbox checked in the xcdatamodel
+
+}
+
+```
+
+
+---
+
+
+# Support Annotations
+
+On Android, the flag `-p support_annotations` allows you to annotate class attributes' getters & setters with `@Nullable` (if the attribute is optional) or `@NonNull` (if it isn't) attributes.
+This option can be combined with the `-p use_wrappers` wrapper flag to generate a safer and more secure code in Android Studio, generating proper warnings if misused.
+
+
+📑 Sample of the generated code in Java (Android)
+
+```java
+package com.gyro.tests;
+
+import io.realm.RealmObject;
+
+/* DO NOT EDIT | Generated by gyro */
+
+public class FidelityCard extends RealmObject {
+
+ private short identifier;
+ @android.support.annotation.IntRange(from=0,to=255)
+ private Integer points;
+ private User user;
+
+ [...]
+
+ @android.support.annotation.Nullable
+ @android.support.annotation.IntRange(from=0,to=255)
+ public Integer getPoints() {
+ return points;
+ }
+
+ public void setPoints(@android.support.annotation.Nullable @android.support.annotation.IntRange(from=0,to=255) final Integer points) {
+ this.points = points;
+ }
+ [...]
+}
+
+```
+
+
+Furthermore, it's possible to add custom annotations to your fields.
+To do that, simply add the key/value pair to the UserInfos of the attribute to annotate:
+
+| Key | Value |
+|-----|-------|
+| `supportAnnotation` | `AnnotationToAdd` |
+
+
+__Example__: If you wish to add the `IntRange(from=0,to=255)` annotation to an attribute, use the following:
+
+
+
+
+
+📑 Sample of the generated code in Java (Android)
+
+```java
+package com.gyro.tests;
+
+import io.realm.RealmObject;
+
+/* DO NOT EDIT | Generated by gyro */
+
+public class FidelityCard extends RealmObject {
+
+ public interface Attributes {
+ String IDENTIFIER = "identifier";
+ String POINTS = "points";
+ }
+
+ private short identifier;
+ @android.support.annotation.IntRange(from=0,to=255)
+ private int points;
+
+ public short getIdentifier() {
+ return identifier;
+ }
+
+ public void setIdentifier(final short identifier) {
+ this.identifier = identifier;
+ }
+
+ @android.support.annotation.IntRange(from=0,to=255)
+ public int getPoints() {
+ return points;
+ }
+
+ public void setPoints(@android.support.annotation.IntRange(from=0,to=255) final int points) {
+ this.points = points;
+ }
+}
+```
+
+
+
+---
+
+
+# Handling enums
+
+Sometimes, an `Int` attribute in the model actually represents an `enum` member in your model. To deal with this case, you can add the following two key/value pairs to this **attribute**:
+
+| Key | Value |
+|-----|-------|
+| `enumType` | `my_type` |
+| `enumValues` | `my_value_1, my_value_2, my_value_3` |
+
+> _Note: If you also add the `JSONKeyPath` User Info key to your attribute in addition to enums, you'll have to add the `JSONValues` to also tell the mapping between the `enumValues` and the matching possible values found in the JSON. See the [JSON Mapping](#json-mapping) below for more details._
+
+__Example__: On the attribute `type` of the `Shop` entity.
+
+
+
+
+
+📑 Sample of the generated code in Java (Android)
+
+`Shop.java`:
+
+```java
+package com.gyro.tests;
+
+/* DO NOT EDIT | Generated by gyro */
+
+import io.realm.RealmObject;
+
+public class Shop extends RealmObject {
+ private String name;
+ private String type;
+
+ public String getType() {
+ return type;
+ }
+
+ public Type getTypeEnum() {
+ return Type.get(getType());
+ }
+
+ public void setTypeEnum(final Type type) {
+ this.type = type.getJsonValue();
+ }
+ [...]
+}
+
+
+```
+
+`Type.java`:
+
+```java
+package com.gyro.tests;
+
+/* DO NOT EDIT | Generated by gyro */
+
+public enum Type {
+
+ TYPE_ONE("TypeOne"),
+ TYPE_TWO("TypeTwo"),
+ TYPE_THREE("TypeThree");
+ [...]
+}
+```
+
+
+
+📑 Sample of the generated code in Swift (iOS)
+
+`Shop.swift`:
+
+```swift
+/* DO NOT EDIT | Generated by gyro */
+
+import RealmSwift
+
+final class Shop: Object {
+ [...]
+ dynamic var type: String = ""
+ var typeEnum: Type? {
+ get {
+ guard let enumValue = Type(rawValue: type) else { return nil }
+ return enumValue
+ }
+ set { type = newValue?.rawValue ?? "" }
+ }
+}
+```
+
+`Type.swift`:
+
+```swift
+/* DO NOT EDIT | Generated by gyro */
+
+enum Type: String {
+ case typeOne = "TypeOne"
+ case typeTwo = "TypeTwo"
+ case typeThree = "TypeThree"
+}
+
+```
+
+
+> **Note**: For Android and Swift, each enum is created in a separate file.
+
+---
+
+
+# Add comments to the generated classes
+
+To make the generated code more readable, it's possible to add comments on an entity — e.g. to provide a short description of what this entity is supposed to represent.
+
+To do so, simply add the following key/value pair to your **entity** in your `.xcdatamodel`:
+
+| Key | Value |
+|-----|-------|
+| `comment` | `the_comment_text_here` |
+
+A code commend (`/** … */`) will then be generated (`.swift` (Swift) or `.java` (Android)) just before the class declaration, e.g. to help the developer understand what this class is for.
+
+
+---
+
+
+# JSON Mapping
+
+You can also add the json mapping for each **attribute** or **relationship** with the following key/value pair:
+
+| Key | Value |
+|-----|-------|
+| `JSONKeyPath` | `json_field_name` |
+
+This key is only used when using the `ObjectMapper` or `Decodable` template.
+
+Currently, this will then generate:
+
+ * Code for `ObjectMapper` or `Decodable` on iOS (in the future we plan to generate `Sourcery` annotations instead so that people can choose whatever JSON library they prefer).
+ * `GSON` annotations (`@SerializedName(…)`) for Android
+
+__Example__: On the 'name' attribute of the 'Shop' entity:
+
+
+
+
+
+📑 Sample of the generated code in Java (Android)
+
+Sur Android, nous utilisons la librairie GSON
+
+```java
+package com.gyro.tests;
+
+/* DO NOT EDIT | Generated by gyro */
+
+import com.google.gson.annotations.SerializedName;
+
+import io.realm.RealmList;
+import io.realm.RealmObject;
+
+public class Shop extends RealmObject {
+
+ @SerializedName("json_name")
+ private String name;
+ private RealmList products;
+ [...]
+}
+```
+
+
+
+📑 Sample of the generated code in Swift (iOS)
+
+On iOS, we support `Decodable` and `Object Mapper` templates for parsing/mapping.
+
+`Shop+Decodable.swift`:
+
+```swift
+/* DO NOT EDIT | Generated by gyro */
+
+import protocol Decodable.Decodable
+import Decodable
+
+extension Shop: Decodable {
+
+ static func decode(_ json: Any) throws -> Shop {
+ let shop = Shop()
+ shop.name = try json => "name"
+ let productsSandbox: [Product] = try json => "products"
+ shop.products.append(objectsIn: productsSandbox)
+ return shop
+ }
+}
+
+```
+
+`ShopMapper.swift`:
+
+```swift
+/* DO NOT EDIT | Generated by gyro */
+
+import ObjectMapper
+
+extension Shop: Mappable {
+
+ // MARK: Initializers
+
+ convenience init?(map: Map) {
+ self.init()
+ }
+
+ // MARK: Mappable
+
+ func mapping(map: Map) {
+
+ // MARK: Attributes
+ self.name <- map["name"]
+
+ // MARK: Relationships
+ self.products <- (map["products"], ListTransform())
+ }
+}
+```
+
+
+
+
+## Combine JSONKeyPath and enums
+
+Note that you can **combine that `JSONKeyPath` key with enums** (see [Handling enums](#handling-enums) above). If you declared the User Info keys to make your attribute an enum (`enumType` + `enumValues`) in addition to `JSONKeyPath`, you'll have to also add the `JSONValues` key to list the corresponding values in the JSON for those `enumValues`.
+
+| Key | Value |
+|-----|-------|
+| `JSONValues` | `valeur_json_1,valeur_json_2,valeur_json_3` |
+
+The number of items listed for that `JSONValues` key must be the same as the number of items listed for the `enumValues` keys, obviously.
+
+__Example__:
+
+
+
+
+
+📑 Sample of the generated code in Java (Android)
+
+`Type.java`:
+
+```java
+package com.gyro.tests;
+
+/* DO NOT EDIT | Generated by gyro */
+
+public enum Type {
+
+ TYPE_ONE("json_type_one"),
+ TYPE_TWO("json_type_two"),
+ TYPE_THREE("json_type_three");
+ [...]
+}
+```
+
+
+
+📑 Sample of the generated code in Swift (iOS)
+
+`Type.swift`:
+
+```swift
+/* DO NOT EDIT | Generated by gyro */
+
+enum Type: String {
+ case typeOne = "json_type_one"
+ case typeTwo = "json_type_two"
+ case typeThree = "json_type_three"
+}
+
+```
+
+
+
+---
+
+
+# Custom ValueTransformers
+
+Only available on iOS (as Android uses the GSON library), custom `ValueTransformers` allows you to e.g. convrt a `String` into an `Int` or a `Date` when parsing the JSON. They are only used when using `ObjectMapper` or `Decodable` template.
+
+To create a specific `ValueTransformer` for a field:
+
+* Create your `ValueTransformer` custom class inheriting `NSValueTransformer` and add it to your project
+* Select the attribute that will need this transformer, and in the UserInfo field, add a pair for the **transformer** key whose value should be the name of the `ValueTransformer` class to use:
+
+| Key | Value |
+|-----|-------|
+| `transformer` | `NameOfTheTransformerClass` |
+
+__Example__:
+
+
+
+
+
+📑 Sample of the generated code in Swift (iOS)
+
+`gyro` will produce the following code. (In this example, attributes `attrDouble` and `attrInteger32` don't have a **transformer** key set in their UserInfo).
+
+`Shop+Decodable.swift`:
+
+```swift
+/* DO NOT EDIT | Generated by gyro */
+
+import protocol Decodable.Decodable
+import Decodable
+
+extension Shop: Decodable {
+
+ static func decode(_ json: Any) throws -> Shop {
+ let shop = Shop()
+ shop.attrDate = try Date.decode(json => "attrDate")
+ shop.attrDateCustom = try Date.decode(json => "attrDateCustom")
+ shop.attrDouble = try json => "attrDouble"
+ shop.attrInteger16 = try Int.decode(json => "attrInteger16")
+ shop.attrInteger32 = try json => "attrInteger32"
+ shop.attrInteger64 = try Int.decode(json => "attrInteger64")
+ return shop
+ }
+}
+```
+
+`ShopMapper.swift`:
+
+```swift
+/* DO NOT EDIT | Generated by gyro */
+
+import ObjectMapper
+
+extension Shop: Mappable {
+
+ // MARK: Initializers
+ convenience init?(_ map: Map) {
+ self.init()
+ }
+
+ // MARK: Mappable
+ func mapping(map: Map) {
+ // MARK: Attributes
+ self.attrDate <- (map["attrDate"], ISO8601DateTransform())
+ self.attrDateCustom <- (map["attrDateCustom"], CustomDateTransformer())
+ self.attrDecimal <- (map["attrDecimal"], MPDecimalTransformer())
+ self.attrDouble <- map["attrDouble"]
+ self.attrFloat <- (map["attrFloat"], MPDecimalTransformer())
+ self.attrInteger16 <- (map["attrInteger16"], MPIntegerTransformer())
+ self.attrInteger32 <- map["attrInteger32"]
+ self.attrInteger64 <- (map["attrInteger64"], MPIntegerTransformer())
+ }
+}
+
+```
+
+
diff --git a/bin/gyro b/bin/gyro
index dfa79d3..f5db5fd 100755
--- a/bin/gyro
+++ b/bin/gyro
@@ -1,118 +1,125 @@
#!/usr/bin/env ruby
-=begin
-Copyright 2016 - Niji
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-if RUBY_VERSION < '2.0.0'
- abort 'error: gyro requires Ruby 2 or higher.'
-end
+abort 'error: gyro requires Ruby 2 or higher.' if RUBY_VERSION < '2.0.0'
require 'gyro'
require 'optparse'
+require 'pathname'
dir = Dir.pwd
options = {
- :model => nil,
- :android_dir => nil,
- :package => nil,
- :ios_dir => nil,
- :ios_json => false,
- :ios_framework => false,
- :ios_swift => false
+ model: nil,
+ template: nil,
+ output: nil,
+ params: {}
}
-OptionParser.new do |opts|
+parser = OptionParser.new do |opts|
opts.banner = 'Usage: gyro [options]'
- opts.on('-m PATH', '--model PATH', %q(Specify Interface Builder .xcdatamodel file)) do |path|
- options[:model] = path
- end
- opts.on('-a PATH', '--android PATH', %q(Specify Android Realm model classes dir)) do |path|
- options[:android_dir] = path
+ opts.on('-m PATH', '--model PATH', 'The .xcdatamodel file to use') do |xcdatamodel_path|
+ options[:model] = xcdatamodel_path
end
- opts.on('-p PATH', '--package PATH', %q(Specify Android Realm model classes package name)) do |path|
- options[:package] = path
+ opts.on('-t DIR', '--template DIR',
+ 'Path to the template directory to use, or name of a template embedded in gyro',
+ '(see --list for available names)') do |path|
+ options[:template] = path
end
- opts.on('-i PATH', '--ios PATH', %q(Specify iOS Realm model classes dir)) do |path|
- options[:ios_dir] = path
+ opts.on('-o DIR', '--output DIR',
+ 'Path to the output directory where to generate the model files') do |path|
+ options[:output] = path
end
- opts.on('-j', '--json', %q(Additionally generate Realm-JSON categories (Objective-C) or ObjectMapper artifacts (Swift))) do
- options[:ios_json] = true
+ opts.on('-p KEY:VALUE', '--param KEY:VALUE',
+ 'Specify a custom parameter that can be used by the template.',
+ 'Can be repeated to add more parameters') do |params|
+ (k, *v) = params.split(':')
+ options[:params] = options[:params].merge(k.to_s => v.join(':').to_s)
end
- opts.on('-f', '--framework', %q(If you use CocoaPods Frameworks instead of static libraries)) do
- options[:ios_framework] = true
+ opts.on('-l', '--list', 'List the names of all available templates provided by gyro') do
+ Gyro::Template.print_list
+ exit
end
- opts.on('-s', '--swift', %q(If you use Swift as iOS language)) do
- options[:ios_swift] = true
+ opts.on('-i', '--info TEMPLATE',
+ 'Show information about the given template',
+ '(path to directory or name of an embedded template)') do |template|
+ Gyro::Template.print_infos(template)
+ exit
end
- opts.on('-n', '--nsnumber', %q(To generate NSNumbers instead of Int/BOOL/Float types)) do
- options[:wrappers] = true
+ opts.on_tail('-h', '--help', 'Show this message') do
+ puts opts
+ exit 1
end
- opts.on('-w', '--wrappers', %q(To generate Java primitive wrappers for optional fields instead of primitive types)) do
- options[:wrappers] = true
+ opts.on_tail('-v', '--version', 'Show version') do
+ puts Gyro::VERSION
+ exit
end
- opts.on('-x', '--annotations', %q(To tag code with Android support annotations according to optional/non optional fields)) do
- options[:annotations] = true
- end
- opts.on_tail('-h', '--help', %q(Show this message)) { puts opts; exit 1 }
- opts.on_tail('-v', '--version', 'Show version') { puts Gyro::VERSION; exit }
- opts.parse!
+end
+
+begin
+ parser.parse!(ARGV)
+rescue OptionParser::ParseError => e
+ Gyro::Log.error(e)
+ puts parser
+ exit 1
end
if options[:model].nil?
- Gyro::Log::info('No model provided, trying to find one in the local directory…')
- options[:model] = Gyro.find_xcdatamodel(dir)
- Gyro::Log::info("Unable to find any .xcdatamodel in #{dir}") if options[:model].nil?
+ Gyro::Log.info('No model provided, trying to find one in the local directory…')
+ options[:model] = Gyro::Parser::XCDataModel.find_in_dir(dir)
+ Gyro::Log.info("Unable to find any .xcdatamodel in #{dir}") if options[:model].nil?
end
if options[:model].nil?
- Gyro::Error::exit_with_error('You need to specify .xcdatamodel path using --model option (see --help for more info)')
+ Gyro::Log.fail!('You need to specify .xcdatamodel path using --model option (see --help for more info)')
else
basename = File.basename(options[:model])
dirname = File.dirname(options[:model])
- Gyro::Log::success("Using #{basename} in #{dirname}")
+ Gyro::Log.success("Using #{basename} in #{dirname}")
end
-# Android
-if options[:android_dir].nil?
- Gyro::Log::info('You need to specify a dir using --android option to generate Android Realm model classes (see --help for more info)')
-else
- if Dir.exist?(options[:android_dir])
- if options[:package].nil?
- Gyro::Log::info('You need to specify an Android package name using --package option (see --help for more info)')
- else
- xcdatamodel = Gyro::XCDataModel::Parser::XCDataModel.new(options[:model])
- Gyro::Realm::Java::Generator.new(options[:android_dir], options[:package], xcdatamodel, options[:wrappers], options[:annotations])
- end
- else
- Gyro::Log::info('You need to specify a valid Android Realm model classes dir')
- end
+# Liquid Templates
+if options[:template].nil?
+ # Generate JSON if no -t is specified
+ Gyro::Log.info('Note: You can specify a template using the --template option (see --help for more info)')
+ xcdatamodel = Gyro::Parser::XCDataModel::XCDataModel.new(options[:model])
+ Gyro::Generator::Json.new(xcdatamodel)
+ exit
end
-# iOS
-if options[:ios_dir].nil?
- Gyro::Log::info('You need to specify a dir using --ios option to generate iOS Realm model classes (see --help for more info)')
-else
- if Dir.exist?(options[:ios_dir])
- xcdatamodel = Gyro::XCDataModel::Parser::XCDataModel.new(options[:model])
- if options[:ios_swift]
- Gyro::Realm::Swift::Generator.new(options[:ios_dir], xcdatamodel, options[:ios_json])
- else
- Gyro::Realm::ObjC::Generator.new(options[:ios_dir], xcdatamodel, options[:ios_json], options[:ios_framework], options[:wrappers])
- end
- else
- Gyro::Log::info('You need to specify a valid iOS Realm model classes dir')
- end
+template_dir = Gyro::Template.find(options[:template])
+
+if options[:output].nil?
+ Gyro::Log.fail!('You need to specify output directory path using --output option (see --help for more info)')
end
+
+output_dir = Pathname.new(options[:output])
+
+unless output_dir.exist?
+ Gyro::Log.fail!("The output directory #{output_dir} does not exist. Please create it first.")
+end
+
+puts <<-INFO.gsub(/ \|/, '')
+ |
+ |#===================================
+ |# Template : #{template_dir}
+ |# Output Dir : #{output_dir}
+ |# Params : #{options[:params].inspect}
+ |#===================================
+INFO
+
+xcdatamodel = Gyro::Parser::XCDataModel::XCDataModel.new(options[:model])
+gen = Gyro::Generator::Liquid.new(template_dir, output_dir, options[:params])
+gen.generate(xcdatamodel)
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 0000000..582014b
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,3 @@
+test:
+ post:
+ - bundle exec rubocop
diff --git a/gyro.gemspec b/gyro.gemspec
index c1f0da3..101c96f 100644
--- a/gyro.gemspec
+++ b/gyro.gemspec
@@ -4,21 +4,26 @@ Gem::Specification.new do |s|
s.name = 'gyro'
s.version = Gyro::VERSION
s.date = '2017-04-10'
- s.summary = "Generate Realm.io models for Swift, Java & ObjC from xcdatamodel"
+ s.summary = 'Generate Realm.io models for Swift, Java & ObjC from xcdatamodel'
s.description = <<-DESC
This tools allows you to use the visual Xcode editor to design your DataModels
- using the xcdatamodel format (originally designed for CoreData) but then
+ using the xcdatamodel format (originally designed for CoreData) but then
generate the code for Realm.io models for Swift, Java & ObjC from that xcdatamodel.
This way you can take advantage of the xcdatamodel visual editor and Xcode integration
while using Realm instead of CoreData.
DESC
- s.authors = ["NijiDigital", "Olivier Halligon", "François Ganard"]
+ s.authors = ['NijiDigital', 'Olivier Halligon', 'François Ganard']
s.email = 'contact@niji.fr'
s.homepage = 'https://github.com/NijiDigital/gyro'
s.license = 'Apache-2.0'
- s.files = Dir["lib/**/*"] + Dir["bin/gyro"] + %w(README.md LICENSE) + Dir["documentation/"]
+ s.files = Dir['lib/**/*'] + Dir['bin/gyro'] + %w[README.md LICENSE] + Dir['documentation/**/*']
s.executables << 'gyro'
s.required_ruby_version = '>= 2.0.0'
+
+ s.add_dependency 'liquid', '~> 3.0'
+ s.add_dependency 'nokogiri', '~> 1.6.8'
+
+ s.add_development_dependency 'rspec', '~> 3.5'
end
diff --git a/lib/gyro.rb b/lib/gyro.rb
index 0639786..38da045 100644
--- a/lib/gyro.rb
+++ b/lib/gyro.rb
@@ -1,27 +1,21 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
require 'gyro/version'
-require 'gyro/xcdatamodel/parser'
-require 'gyro/realm/java/generator'
-require 'gyro/realm/objc/generator'
-require 'gyro/realm/swift/generator'
+require 'gyro/parser/xcdatamodel'
+require 'gyro/generator/json'
+require 'gyro/generator/liquid'
-require 'gyro/utils/log'
-require 'gyro/utils/file_utils'
-require 'gyro/utils/string_xcdatamodel'
-require 'gyro/utils/error'
\ No newline at end of file
+require 'gyro/log'
diff --git a/lib/gyro/generator/json.rb b/lib/gyro/generator/json.rb
new file mode 100644
index 0000000..29d630b
--- /dev/null
+++ b/lib/gyro/generator/json.rb
@@ -0,0 +1,30 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'json'
+require 'gyro/parser/xcdatamodel'
+
+module Gyro
+ module Generator
+ # Generates the JSON representation of the input datamodel
+ #
+ class Json
+ # PUBLIC METHODS #######################################################
+ def initialize(xcdatamodel)
+ Gyro::Log.title('Generating JSON')
+ puts JSON.pretty_generate(xcdatamodel.to_h)
+ end
+ end
+ end
+end
diff --git a/lib/gyro/generator/liquid.rb b/lib/gyro/generator/liquid.rb
new file mode 100644
index 0000000..d641b2a
--- /dev/null
+++ b/lib/gyro/generator/liquid.rb
@@ -0,0 +1,22 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'liquid'
+require 'pathname'
+
+require 'gyro/parser/xcdatamodel'
+require 'gyro/template'
+require 'gyro/generator/liquid/liquid'
+require 'gyro/generator/liquid/whitespace_patch'
+require 'gyro/generator/liquid/filters'
diff --git a/lib/gyro/generator/liquid/filters.rb b/lib/gyro/generator/liquid/filters.rb
new file mode 100644
index 0000000..cfb5f42
--- /dev/null
+++ b/lib/gyro/generator/liquid/filters.rb
@@ -0,0 +1,53 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module Gyro
+ module Generator
+ # Declare some custom Liquid Filters used by the template, then render it
+ #
+ module LiquidFilters
+ def escape_quotes(input)
+ input.gsub('"', '\"')
+ end
+
+ def snake_to_camel_case(input)
+ input.split('_').map(&:capitalize).join
+ end
+
+ def snake_case(input)
+ input.gsub(/::/, '/')
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
+ .tr('-', '_')
+ .downcase
+ end
+
+ def uncapitalize(input)
+ input_strip = input.strip
+ input_strip[0, 1].downcase + input_strip[1..-1]
+ end
+
+ def titleize(input)
+ input_strip = input.strip
+ input_strip[0, 1].upcase + input_strip[1..-1]
+ end
+
+ def delete_objc_prefix(input)
+ i = 0
+ i += 1 while i < input.length - 1 && /[[:upper:]]/.match(input[i + 1])
+ input[i..input.length]
+ end
+ end
+ end
+end
diff --git a/lib/gyro/generator/liquid/liquid.rb b/lib/gyro/generator/liquid/liquid.rb
new file mode 100644
index 0000000..b60c0ec
--- /dev/null
+++ b/lib/gyro/generator/liquid/liquid.rb
@@ -0,0 +1,124 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module Gyro
+ module Generator
+ # Generates arbitrary output from the input datamodel, using a Liquid template provided by the user
+ #
+ class Liquid
+ attr_accessor :params, :output_dir
+
+ # PUBLIC METHODS #######################################################
+
+ # rubocop:disable Metrics/AbcSize
+ def initialize(template_dir, output_dir, params)
+ Gyro::Log.title('Generating Model')
+
+ @params = params
+ @output_dir = Pathname.new(output_dir)
+
+ # Define Template path for Liquid file system to use Include Tag
+ ::Liquid::Template.file_system = ::Liquid::LocalFileSystem.new(template_dir)
+
+ @entity_template = load_template(template_dir + 'entity.liquid', false)
+ @entity_filename_template = load_template(template_dir + 'entity_filename.liquid', true)
+ @enum_template = load_template(template_dir + 'enum.liquid', false)
+ enum_fn_tpl = template_dir + 'enum_filename.liquid'
+ enum_fn_tpl = template_dir + 'filename.liquid' unless enum_fn_tpl.exist?
+ @enum_filename_template = load_template(enum_fn_tpl, true)
+ end
+ # rubocop:enable Metrics/AbcSize
+
+ def generate(xcdatamodel)
+ generate_entities(xcdatamodel)
+ Gyro::Log.success('Model objects are generated !')
+ end
+
+ private ################################################################
+
+ def load_template(template_path, prevent_return_line)
+ unless template_path.exist?
+ Gyro::Log.fail!('Bad template directory content ! Your template needs a ' + template_path.to_s + ' file')
+ end
+ template_path_string = template_path.read
+ if prevent_return_line && template_path_string.include?("\n")
+ msg = 'The given template ' + template_path.to_s + ' contains return line(s). This can lead to side effets.'
+ Gyro::Log.error(msg)
+ end
+ ::Liquid::Template.parse(template_path_string)
+ end
+
+ def generate_entities(xcdatamodel)
+ xcdatamodel.to_h['entities'].each do |entity|
+ entity_context = { 'params' => @params, 'entity' => entity }
+ # Rendering template using entity and params context
+ output = render_entity(entity_context)
+ # Don't generate empty output
+ next if output.delete("\n").empty?
+
+ filename_context = { 'params' => @params, 'name' => entity['name'] }
+ # Rendering filename template using entity name and params context
+ filename = render_filename(filename_context)
+ Gyro::Log.success("#{filename} is created !")
+ # Write model object
+ File.write(@output_dir + filename, output)
+ # Generate model object enums
+ generate_enums(entity['attributes'])
+ end
+ end
+
+ def generate_enums(attributes)
+ enums = []
+ attributes.each do |attribute|
+ enum_type = attribute['enum_type']
+ next if enums.include?(enum_type) || enum_type.empty?
+ enums.push(enum_type)
+
+ enum_context = { 'params' => @params, 'attribute' => attribute }
+ # Rendering enum template using attribute and params context
+ output = render_enum(enum_context)
+ # Don't generate empty output
+ next if output.delete("\n").empty?
+
+ generate_enum(enum_type, output)
+ end
+ end
+
+ def generate_enum(enum_name, output)
+ # Rendering enum filename template using enum name and params context
+ enum_filename_context = { 'params' => @params, 'name' => enum_name }
+ enum_filename = render_enum_filename(enum_filename_context)
+ File.write(@output_dir + enum_filename, output)
+ end
+
+ def render_entity(context)
+ @entity_template.render(context, filters: [Gyro::Generator::LiquidFilters])
+ .gsub(/^ +$/, '')
+ end
+
+ def render_filename(context)
+ @entity_filename_template.render(context).chomp
+ end
+
+ def render_enum(context)
+ @enum_template.render(context, filters: [Gyro::Generator::LiquidFilters])
+ .gsub(/^ +$/, '')
+ end
+
+ def render_enum_filename(context)
+ @enum_filename_template.render(context).chomp
+ end
+ end
+ end
+end
diff --git a/lib/gyro/generator/liquid/whitespace_patch.rb b/lib/gyro/generator/liquid/whitespace_patch.rb
new file mode 100644
index 0000000..d141439
--- /dev/null
+++ b/lib/gyro/generator/liquid/whitespace_patch.rb
@@ -0,0 +1,61 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'liquid'
+
+if Gem::Version.new(Liquid::VERSION) < Gem::Version.new('4.0.0')
+ module Gyro
+ module Generator
+ # This allows to support whitespace trimming (which is only supported in Liquid 4) when using Liquid 3.0
+ # It works by patching Liquid::Template using ruby's `Module#prepend` feature
+ # See http://stackoverflow.com/a/4471202/803787 for more info on Monkey-Patching in Ruby
+ #
+ module LiquidWhitespacePatch
+ # Patch for Liquid::Template class
+ #
+ module Template
+ def parse(source, options = {})
+ super(source.gsub(/\s*{%-/, "\v{%").gsub(/-%}\s*/, "%}\v"), options)
+ end
+
+ def render(*params)
+ super(*params).delete("\v")
+ end
+ end
+
+ # Patch for Liquid's String extension
+ #
+ module String
+ def to_liquid
+ super.delete("\v")
+ end
+ end
+ end
+ end
+ end
+
+ module Liquid
+ # Monkey-Patch Liquid::Template class
+ #
+ class Template
+ prepend Gyro::Generator::LiquidWhitespacePatch::Template
+ end
+ end
+
+ # Monkey-Patch String
+ #
+ class String
+ prepend Gyro::Generator::LiquidWhitespacePatch::String
+ end
+end
diff --git a/lib/gyro/log.rb b/lib/gyro/log.rb
new file mode 100644
index 0000000..1bbcf73
--- /dev/null
+++ b/lib/gyro/log.rb
@@ -0,0 +1,46 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module Gyro
+ # Print nice and colored output for various error/success/title messages of Gyro
+ #
+ module Log
+ @quiet = false
+ def self.quiet=(value)
+ @quiet = value
+ end
+
+ def self.title(str) # bg yellow
+ puts "\n\e[44;37m#{str}\e[0m" unless @quiet
+ end
+
+ def self.error(str)
+ puts "\e[1;31m! #{str}\e[0m" unless @quiet
+ end
+
+ def self.info(str)
+ puts "\e[1;33m> #{str}\e[0m" unless @quiet
+ end
+
+ def self.success(str)
+ puts "\e[1;32m√ #{str}\e[0m" unless @quiet
+ end
+
+ def self.fail!(message, stacktrace: false)
+ Gyro::Log.error message
+ raise message if stacktrace
+ exit 1
+ end
+ end
+end
diff --git a/lib/gyro/parser/xcdatamodel.rb b/lib/gyro/parser/xcdatamodel.rb
new file mode 100644
index 0000000..f9900b6
--- /dev/null
+++ b/lib/gyro/parser/xcdatamodel.rb
@@ -0,0 +1,20 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'nokogiri'
+
+require 'gyro/parser/xcdatamodel/xcdatamodel'
+require 'gyro/parser/xcdatamodel/attribute'
+require 'gyro/parser/xcdatamodel/relationship'
+require 'gyro/parser/xcdatamodel/entity'
diff --git a/lib/gyro/parser/xcdatamodel/attribute.rb b/lib/gyro/parser/xcdatamodel/attribute.rb
new file mode 100644
index 0000000..4ea5c43
--- /dev/null
+++ b/lib/gyro/parser/xcdatamodel/attribute.rb
@@ -0,0 +1,129 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module Gyro
+ module Parser
+ module XCDataModel
+ # One Attribute in an Entity of the xcdatamodel
+ #
+ class Attribute
+ attr_accessor :entity_name, :name, :type, :optional, :indexed, :default
+ attr_accessor :realm_ignored, :realm_read_only, :enum_type, :enum_values
+ attr_accessor :json_key_path, :json_values, :transformer, :comment, :support_annotation
+
+ alias optional? optional
+ alias indexed? indexed
+ alias realm_ignored? realm_ignored
+
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
+ def initialize(attribute_xml, entity_name)
+ @entity_name = entity_name
+ @name = attribute_xml.xpath('@name').to_s
+ @optional = attribute_xml.xpath('@optional').to_s == 'YES'
+ @indexed = attribute_xml.xpath('@indexed').to_s == 'YES'
+ @default = attribute_xml.xpath('@defaultValueString').to_s
+ @type = attribute_xml.xpath('@attributeType').to_s.downcase.tr(' ', '_').to_sym
+ @realm_ignored = !Gyro::Parser::XCDataModel.user_info(attribute_xml, 'realmIgnored').empty?
+ @realm_read_only = Gyro::Parser::XCDataModel.user_info(attribute_xml, 'realmReadOnly')
+ @enum_type = Gyro::Parser::XCDataModel.user_info(attribute_xml, 'enumType')
+ @enum_values = Gyro::Parser::XCDataModel.user_info(attribute_xml, 'enumValues').split(',')
+ @json_key_path = Gyro::Parser::XCDataModel.user_info(attribute_xml, 'JSONKeyPath')
+ @json_values = Gyro::Parser::XCDataModel.user_info(attribute_xml, 'JSONValues').split(',')
+ @transformer = Gyro::Parser::XCDataModel.user_info(attribute_xml, 'transformer').strip
+ @comment = Gyro::Parser::XCDataModel.user_info(attribute_xml, 'comment')
+ @support_annotation = Gyro::Parser::XCDataModel.user_info(attribute_xml, 'supportAnnotation')
+ search_for_error
+ end
+
+ def to_h
+ {
+ 'entity_name' => entity_name, 'name' => name,
+ 'type' => type.to_s,
+ 'optional' => optional,
+ 'indexed' => indexed,
+ 'default' => default,
+ 'realm_ignored' => realm_ignored, 'realm_read_only' => realm_read_only,
+ 'enum_type' => enum_type, 'enum_values' => enum_values,
+ 'json_key_path' => json_key_path, 'json_values' => json_values,
+ 'transformer' => transformer, 'need_transformer' => need_transformer?,
+ 'comment' => comment,
+ 'support_annotation' => support_annotation,
+ 'is_decimal' => decimal?, 'is_integer' => integer?, 'is_number' => number?, 'is_bool' => bool?
+ }
+ end
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
+
+ def enum?
+ !@enum_type.empty?
+ end
+
+ def read_only?
+ !@realm_read_only.empty?
+ end
+
+ def default?
+ !@default.empty?
+ end
+
+ def to_s
+ items = [
+ "name=#{@name}",
+ "type=#{@type}",
+ "optional=#{@optional}",
+ "default=#{@default}",
+ "indexed=#{@indexed}"
+ ]
+ "\tAttribute => " + items.join(' | ') + "\n"
+ end
+
+ def decimal?
+ (@type == :decimal) || (@type == :double) || (@type == :float)
+ end
+
+ def integer?
+ (@type == :integer_16) || (@type == :integer_32) || (@type == :integer_64)
+ end
+
+ def number?
+ decimal? || integer?
+ end
+
+ def bool?
+ @type == :boolean
+ end
+
+ def need_transformer?
+ !@enum_type.empty? || (@type == :boolean) || (@type == :date) || !@transformer.empty?
+ end
+
+ private ################################################################
+
+ def search_for_error
+ # rubocop:disable Style/GuardClause
+ if @type == :undefined || @type.empty?
+ msg = %(The attribute "#{@name}" from "#{@entity_name}" has no type - please fix it)
+ Gyro::Log.fail!(msg, stacktrace: true)
+ end
+ if !@json_key_path.empty? && !@enum_values.empty? && (@enum_values.size != @json_values.size)
+ message = %(The attribute "#{@name}" from "#{@entity_name}" is wrongly annotated:) +
+ %(when declaring an type with enum and JSONKeyPath, you must have the same number of items) +
+ %(in the 'enumValues' and 'JSONValues' annotations.)
+ Gyro::Log.fail!(message, stacktrace: true)
+ end
+ # rubocop:enable Style/GuardClause
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gyro/parser/xcdatamodel/entity.rb b/lib/gyro/parser/xcdatamodel/entity.rb
new file mode 100644
index 0000000..0f465af
--- /dev/null
+++ b/lib/gyro/parser/xcdatamodel/entity.rb
@@ -0,0 +1,195 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module Gyro
+ module Parser
+ module XCDataModel
+ # One Entity in the xcdatamodel
+ #
+ class Entity
+ attr_accessor :name, :parent, :abstract, :attributes, :relationships, :identity_attribute, :comment
+ alias abstract? abstract
+
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
+ def initialize(entity_xml)
+ @name = entity_xml.xpath('@name').to_s
+ @parent = entity_xml.xpath('@parentEntity').to_s
+ @abstract = entity_xml.xpath('@isAbstract').to_s == 'YES' ? true : false
+ @clean = false
+ @identity_attribute = Gyro::Parser::XCDataModel.user_info(entity_xml, 'identityAttribute')
+ @comment = Gyro::Parser::XCDataModel.user_info(entity_xml, 'comment')
+ @attributes = {}
+ @relationships = {}
+ load_entity(entity_xml)
+ end
+
+ def to_h
+ {
+ 'attributes' => attributes.values.map(&:to_h),
+ 'relationships' => relationships.values.map(&:to_h),
+ 'name' => name,
+ 'parent' => parent,
+ 'abstract' => abstract,
+ 'identity_attribute' => identity_attribute,
+ 'comment' => comment,
+ 'has_no_inverse_relationship' => no_inverse_relationship?,
+ 'has_ignored' => ignored_attributes_relationships?, 'has_required' => required_attributes?,
+ 'has_primary_key' => primary_key?, 'has_indexed_attributes' => indexed_attributes?,
+ 'has_json_key_path' => json_key_paths?, 'has_enum_attributes' => enum_attributes?,
+ 'has_custom_transformers' => custom_transformers?, 'need_transformer' => need_transformer?,
+ 'has_bool_attributes' => bool_attributes?,
+ 'has_number_attributes' => number_attributes?,
+ 'has_date_attribute' => date_attributes?,
+ 'has_list_relationship' => list_relationships?,
+ 'has_list_attributes' => list_attributes?,
+ 'has_only_inverse' => only_inverse_relationships?
+ }
+ end
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
+
+ def to_s
+ "\nEntity => #{@name}\n" +
+ @attributes.map { |_, attr| attr.to_s } +
+ @relationships.map { |_, rel| rel.to_s }
+ end
+
+ def used_as_list_by_other?(entities)
+ entities.any? do |_, entity|
+ entity.relationships.any? do |_, relationship|
+ (relationship.inverse_type == @name) && (relationship.type == :to_many)
+ end
+ end
+ end
+
+ def list_attributes?(include_inverse = false)
+ @relationships.any? do |_, relationship|
+ (relationship.type == :to_many) && (include_inverse ? true : !relationship.inverse?)
+ end
+ end
+
+ def no_inverse_relationship?
+ @relationships.none? { |_, relationship| relationship.inverse? }
+ end
+
+ def ignored_attributes?
+ @attributes.any? { |_, attribute| attribute.realm_ignored? }
+ end
+
+ def ignored_relationships?
+ @relationships.any? { |_, relationship| relationship.realm_ignored? }
+ end
+
+ def ignored_attributes_relationships?
+ ignored_attributes? || ignored_relationships?
+ end
+
+ def primary_key?
+ !@identity_attribute.empty?
+ end
+
+ def required_attributes?
+ @attributes.any? { |_, attribute| required?(attribute) }
+ end
+
+ def required?(attribute)
+ return false if attribute.optional?
+ return true unless primary_key?
+ return true if primary_key? && !attribute.name.eql?(identity_attribute)
+ end
+
+ def default_value?(attribute)
+ attribute.name != @identity_attribute
+ end
+
+ def indexed_attributes?
+ @attributes.any? { |_, attribute| attribute.indexed? }
+ end
+
+ def json_key_paths?
+ @attributes.any? do |_, attribute|
+ !attribute.json_key_path.empty?
+ end || @relationships.any? do |_, relationship|
+ !relationship.inverse? && !relationship.json_key_path.empty?
+ end
+ end
+
+ def enum_attributes?
+ @attributes.any? { |_, attribute| !attribute.enum_type.empty? }
+ end
+
+ def transformers
+ transformers = Set.new
+ @attributes.each do |_, attribute|
+ transformers.add attribute.transformer unless attribute.transformer.empty?
+ end
+ transformers
+ end
+
+ def custom_transformers?
+ @attributes.any? { |_, attribute| !attribute.transformer.empty? }
+ end
+
+ def need_transformer?
+ enum_attributes? || bool_attributes? || custom_transformers? || date_attributes?
+ end
+
+ def bool_attributes?
+ @attributes.any? { |_, attribute| attribute.type == :boolean }
+ end
+
+ NUMBER_TYPES = %i[integer_16 integer_32 integer_64 decimal double float].freeze
+ def number_attributes?
+ @attributes.any? do |_, attribute|
+ attribute.enum_type.empty? && NUMBER_TYPES.include?(attribute.type)
+ end
+ end
+
+ def date_attributes?
+ @attributes.any? { |_, attribute| attribute.type == :date }
+ end
+
+ def list_relationships?
+ @relationships.any? { |_, relationship| !relationship.destination.empty? }
+ end
+
+ def only_inverse_relationships?
+ @relationships.all? { |_, relationship| relationship.inverse? }
+ end
+
+ private ################################################################
+
+ def load_entity(entity_xml)
+ load_attributes(entity_xml)
+ load_relationships(entity_xml)
+ end
+
+ def load_attributes(entity_xml)
+ entity_xml.xpath('attribute').each do |node|
+ attribute = Attribute.new(node, @name)
+ if attribute.type != 'Transformable'
+ @attributes[attribute.name] = attribute
+ end
+ end
+ end
+
+ def load_relationships(entity_xml)
+ entity_xml.xpath('relationship').each do |node|
+ relationship = Relationship.new(node, @name)
+ @relationships[relationship.name] = relationship
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gyro/parser/xcdatamodel/relationship.rb b/lib/gyro/parser/xcdatamodel/relationship.rb
new file mode 100644
index 0000000..b36be1a
--- /dev/null
+++ b/lib/gyro/parser/xcdatamodel/relationship.rb
@@ -0,0 +1,84 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module Gyro
+ module Parser
+ module XCDataModel
+ # One Relationship between attributes in the xcdatamodel
+ #
+ class Relationship
+ attr_accessor :entity_name, :name, :type, :optional, :deletion_rule
+ attr_accessor :inverse_name, :inverse_type, :json_key_path, :support_annotation
+ attr_accessor :realm_ignored
+ attr_accessor :destination
+
+ alias realm_ignored? realm_ignored
+
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
+ def initialize(relationship_xml, entity_name)
+ @entity_name = entity_name
+ @name = relationship_xml.xpath('@name').to_s
+ @optional = relationship_xml.xpath('@optional').to_s == 'YES' ? true : false
+ @deletion_rule = relationship_xml.xpath('@deletionRule').to_s
+ @inverse_name = relationship_xml.xpath('@inverseName').to_s
+ @inverse_type = relationship_xml.xpath('@destinationEntity').to_s
+ @json_key_path = Gyro::Parser::XCDataModel.user_info(relationship_xml, 'JSONKeyPath')
+ @realm_ignored = Gyro::Parser::XCDataModel.user_info(relationship_xml, 'realmIgnored').empty? ? false : true
+ @support_annotation = Gyro::Parser::XCDataModel.user_info(relationship_xml, 'supportAnnotation')
+ load_type(relationship_xml)
+ @destination = Gyro::Parser::XCDataModel.user_info(relationship_xml, 'destination')
+ search_for_error
+ end
+
+ def to_h
+ { 'entity_name' => entity_name, 'name' => name, 'type' => type.to_s,
+ 'optional' => optional, 'deletion_rule' => deletion_rule,
+ 'inverse_name' => inverse_name, 'inverse_type' => inverse_type,
+ 'json_key_path' => json_key_path, 'support_annotation' => support_annotation,
+ 'realm_ignored' => realm_ignored, 'destination' => destination, 'inverse' => inverse? }
+ end
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
+
+ def to_s
+ "\tRelationship => name=#{@name} | type=#{@type} | optional=#{@optional} | deletion_rule=#{@deletion_rule}\n"
+ end
+
+ def inverse?
+ @name.end_with?('_')
+ end
+
+ private ################################################################
+
+ def load_type(relationship_xml)
+ max_count = relationship_xml.xpath('@maxCount').to_s
+ @type = !max_count.nil? && (max_count == '1') ? :to_one : :to_many
+ end
+
+ def search_for_error
+ # rubocop:disable Style/GuardClause
+ if inverse_type.empty? && destination.empty?
+ message = %(The relationship "#{@name}" from "#{@entity_name}" is wrong - please fix it)
+ Gyro::Log.fail!(message, stacktrace: true)
+ end
+ if !destination.empty? && type != :to_many
+ message = %(The relationship "#{@name}" from "#{@entity_name}" is wrong - ) +
+ %(please set a 'No Value' relationship as 'To Many')
+ Gyro::Log.fail!(message, stacktrace: true)
+ end
+ # rubocop:enable Style/GuardClause
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gyro/parser/xcdatamodel/xcdatamodel.rb b/lib/gyro/parser/xcdatamodel/xcdatamodel.rb
new file mode 100644
index 0000000..76ff3e1
--- /dev/null
+++ b/lib/gyro/parser/xcdatamodel/xcdatamodel.rb
@@ -0,0 +1,65 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module Gyro
+ module Parser
+ # Parser for CoreData's xcdatamodel files
+ #
+ module XCDataModel
+ def self.find_in_dir(dir)
+ Dir.chdir(dir) do
+ files = Dir.glob('*.xcdatamodel')
+ files.first.nil? ? nil : File.expand_path(files.first, dir)
+ end
+ end
+
+ def self.user_info(xml, key)
+ xml.xpath("userInfo/entry[@key='#{key}']/@value").to_s
+ end
+
+ # Represents the whole xcdatamodel file struture, once parsed
+ #
+ class XCDataModel
+ attr_accessor :entities
+
+ def initialize(xcdatamodel_dir)
+ contents_file = File.join(xcdatamodel_dir, 'contents')
+ Gyro::Log.fail!('Unable to find contents of xcdatamodel', stacktrace: true) unless File.exist?(contents_file)
+ @entities = {}
+ file = File.open(contents_file)
+ document_xml = Nokogiri::XML(file).remove_namespaces!
+ file.close
+ load_entities(document_xml)
+ end
+
+ def to_h
+ { 'entities' => entities.values.map(&:to_h) }
+ end
+
+ def to_s
+ @entities.values.map(&:to_s).join
+ end
+
+ private
+
+ def load_entities(document_xml)
+ document_xml.xpath('//entity').each do |node|
+ entity = Entity.new(node)
+ @entities[entity.name] = entity
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gyro/realm/java/converter.rb b/lib/gyro/realm/java/converter.rb
deleted file mode 100644
index 61deff8..0000000
--- a/lib/gyro/realm/java/converter.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module Java
- module Converter
-
- TYPES = {
- :integer_16 => 'short',
- :integer_32 => 'int',
- :integer_64 => 'long',
- :decimal => 'double',
- :double => 'double',
- :float => 'float',
- :string => 'String',
- :boolean => 'boolean',
- :date => 'Date',
- :binary => 'byte[]'
- }
-
- WRAPPER_TYPES = {
- :integer_16 => 'Short',
- :integer_32 => 'Integer',
- :integer_64 => 'Long',
- :decimal => 'Double',
- :double => 'Double',
- :float => 'Float',
- :string => 'String',
- :boolean => 'Boolean',
- :date => 'Date',
- :binary => 'Byte[]'
- }
-
- def convert_type(type, useWrapperClass)
- if (useWrapperClass)
- WRAPPER_TYPES[type]
- else
- TYPES[type]
- end
- end
-
- def is_primitive(type_str)
- TYPES.has_value?(type_str) and type_str != "String" and type_str != "Date"
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/java/enum_generator.rb b/lib/gyro/realm/java/enum_generator.rb
deleted file mode 100644
index 914a345..0000000
--- a/lib/gyro/realm/java/enum_generator.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module Java
- module EnumGenerator
-
- # INCLUDES #############################################################
- include Templates
-
- # PUBLIC METHODS #######################################################
-
- def generate_enums(path, package, attributes, support_annotations = false)
- enums = Array.new
- attributes.each do |_, attribute|
- if attribute.enum? and !enums.include?(attribute.enum_type)
- enum_type = attribute.enum_type.delete_objc_prefix
- enums.push(enum_type)
- generate_enum(path, package, enum_type, attribute.enum_values, attribute.json_values, support_annotations)
- end
- end
- end
-
- def generate_enum_getter_and_setter(enum_type, attribute_name, support_annotations)
- getter = String.new
- setter = String.new
- getter << ' ' + '@android.support.annotation.Nullable' + "\n" if support_annotations
- getter << ' public ' + enum_type + ' get'+ attribute_name.capitalize_first_letter + 'Enum() {' + "\n" +
- ' ' + 'return '+ enum_type + '.get(get' + attribute_name.capitalize_first_letter + '());' + "\n" +
- ' ' + '}' + "\n"
- setter << ' ' + 'public void set'+ attribute_name.capitalize_first_letter + 'Enum('
- setter << '@android.support.annotation.NonNull ' if support_annotations
- setter << 'final ' + enum_type + ' ' + attribute_name +') {' + "\n" +
- ' ' + 'this.' + attribute_name + ' = ' + attribute_name + '.getJsonValue();' + "\n" +
- ' ' + '}' + "\n"
- getter + "\n" + setter
- end
-
- private #################################################################
-
- def generate_enum(path, package, enum_name, enum_values, json_values, support_annotations)
- enum_file = String.new
- enum_file << PACKAGE_TEMPLATE%[package] + "\n\n"
- enum_file << GENERATED_MESSAGE + "\n\n"
- enum_file << ENUM_TEMPLATE%[enum_name] + "\n\n"
- json_values = get_json_values(enum_values, json_values)
- if enum_values.length != 0
- (0..enum_values.length - 1).each { |idx|
- gson_value = json_values[idx]
- enum_value = generate_enum_string(enum_values[idx], gson_value)
- enum_file << (idx != enum_values.length - 1 ? enum_value + ",\n" : enum_value + ";\n")
- }
- enum_file << "\n" ' ' + FINAL_ATTRIBUTE_TEMPLATE%%w(String jsonValue) + "\n\n"
- enum_file << generate_enum_gson_constructor(enum_name) + "\n"
- enum_file << generate_static_gson_getter(enum_name, support_annotations) + "\n"
- enum_file << generate_gson_getter(support_annotations)
- enum_file << '}' + "\n"
- Gyro.write_file_with_name(path, JAVA_FILE_TEMPLATE%[enum_name], enum_file)
- end
- end
-
- def generate_enum_string(enum_value, gson_value)
- enum_value = enum_value.delete_objc_prefix.camel_case
- gson_annotation = gson_value.empty? ? '' : ENUM_JSON_VALUE%[gson_value]
- ' ' + enum_value + gson_annotation
- end
-
- def generate_enum_gson_constructor(enum_name)
- ' ' + enum_name +'(final String jsonValue) {' + "\n" +
- ' ' + 'this.jsonValue = jsonValue;' + "\n" +
- ' ' + '}' + "\n"
- end
-
- # Methods to bypass enum restriction in Realm
- def generate_static_gson_getter(enum_name, support_annotations)
- getter = String.new
- getter << ' @android.support.annotation.Nullable' + "\n" if support_annotations
- getter << ' public static ' + enum_name + ' get(final String jsonValue) {' + "\n" +
- ' ' + 'for (final ' + enum_name + ' type : ' + enum_name + '.values()) {' + "\n" +
- ' ' + 'if (type.getJsonValue().equals(jsonValue)) {' + "\n" +
- ' ' + 'return type;' + "\n" +
- ' ' + '}'+ "\n" +
- ' ' + '}' + "\n" +
- ' ' + 'return null;' + "\n" +
- ' ' + '}' + "\n"
- getter
- end
-
- def generate_gson_getter(support_annotations)
- getter = String.new
- getter << ' @android.support.annotation.NonNull' + "\n" if support_annotations
- getter << ' ' + 'public String getJsonValue() {' + "\n" +
- ' ' + 'return jsonValue;' + "\n" +
- ' ' + '}' + "\n"
- getter
- end
-
- def get_json_values(enum_values, json_values)
- if json_values.empty?
- enum_values.each { |value|
- json_values << value.delete_objc_prefix.underscore
- }
- end
- json_values
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/java/generator.rb b/lib/gyro/realm/java/generator.rb
deleted file mode 100644
index d3b0e01..0000000
--- a/lib/gyro/realm/java/generator.rb
+++ /dev/null
@@ -1,180 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-require 'gyro/xcdatamodel/parser'
-require 'gyro/realm/java/templates'
-require 'gyro/realm/java/converter'
-require 'gyro/realm/java/enum_generator'
-
-module Gyro
- module Realm
- module Java
-
- class Generator
-
- # INCLUDES #############################################################
-
- include Gyro::XCDataModel::Parser
- include Converter
- include EnumGenerator
- include Templates
-
- # PUBLIC METHODS #######################################################
-
- def initialize(path, package_name, xcdatamodel, use_wrappers = false, support_annotations = false)
- puts "\n"
- Gyro::Log::title('Android Realm')
- xcdatamodel.entities.each do |_, entity|
- unless entity.abstract?
- Gyro::Log::success("Generating entity #{entity.name}...")
- generate_class(path, package_name, entity, use_wrappers, support_annotations)
- end
- end
- end
-
- private ################################################################
-
- def generate_class(path, package, entity, use_wrappers, support_annotations)
- class_file = String.new
- entity.name = entity.name.delete_objc_prefix
- generate_header(class_file, package, entity)
- generate_attributes(class_file, entity.attributes, entity.relationships, entity.identity_attribute, use_wrappers, support_annotations)
- generate_footer(class_file)
- Gyro.write_file_with_name(path, JAVA_FILE_TEMPLATE%[entity.name], class_file)
- generate_enums(path, package, entity.attributes, support_annotations)
- end
-
- def generate_header(class_file, package, entity)
- class_file << PACKAGE_TEMPLATE%[package] + "\n\n"
- class_file << IMPORT_GSON + "\n\n" if entity.has_json_key_path?
- class_file << IMPORT_DATE + "\n" if entity.has_date_attribute?
- class_file << IMPORT_LIST + "\n" if entity.has_list_relationship?
- class_file << "\n" if entity.has_date_attribute? or entity.has_list_relationship?
- class_file << IMPORT_REALM_LIST + "\n" if entity.has_list_attributes?
- class_file << IMPORT_REALM_OBJECT + "\n"
- class_file << IMPORT_REALM_IGNORE + "\n" if entity.has_ignored? or entity.has_enum_attributes?
- class_file << IMPORT_REALM_INDEX + "\n" if entity.has_indexed_attributes?
- class_file << IMPORT_REALM_PRIMARY_KEY + "\n" if entity.has_primary_key?
- class_file << "\n" if class_file != PACKAGE_TEMPLATE%[package]
- class_file << GENERATED_MESSAGE + "\n\n"
- class_file << CLASS_COMMENT_TEMPLATE%[entity.comment] + "\n" unless entity.comment.empty?
- class_file << CLASS_TEMPLATE%[entity.name] + "\n\n"
- class_file << generate_constants(entity)
- end
-
- def generate_constants(entity)
- attribute_constants = String.new
- unless entity.attributes.empty?
- attribute_constants << ' ' + ATTRIBUTE_CONSTANTS + "\n"
- entity.attributes.each do |_, attribute|
- unless attribute.realm_ignored? or attribute.read_only?
- attribute_constants << ' ' + ATTRIBUTE_COMMENT_TEMPLATE%[attribute.comment] + "\n" unless attribute.comment.empty?
- attribute_constants << ' ' + CONSTANT_TEMPLATE%[attribute.name.underscore.upcase, attribute.name] + "\n"
- end
- end
- attribute_constants << ' ' + '}'+ "\n\n"
- end
- relationship_constants = String.new
- if not entity.relationships.empty? and not entity.has_only_inverse?
- relationship_constants << ' ' + RELATIONSHIP_CONSTANTS + "\n"
- entity.relationships.each do |_, relationship|
- relationship_constants << ' ' + CONSTANT_TEMPLATE%[relationship.name.underscore.upcase, relationship.name] + "\n" unless relationship.inverse?
- end
- relationship_constants << ' ' + '}'+ "\n\n"
- end
- attribute_constants + relationship_constants
- end
-
- def generate_footer(class_file)
- class_file << '}' + "\n"
- end
-
- def generate_attributes(class_file, attributes, relationships, primary_key, use_wrappers, support_annotations)
- # "NORMAL" ATTRIBUTES
- (attributes_string, getters_and_setters_string) = write_attributes(attributes, primary_key, use_wrappers, support_annotations)
- # "RELATIONSHIP" ATTRIBUTES
- relationships.each do |_, relationship|
- unless relationship.inverse?
- if relationship.destination.empty?
- type_without_prefix = relationship.inverse_type.delete_objc_prefix
- type = relationship.type == :to_many ? REALM_LIST_TEMPLATE%[type_without_prefix] : type_without_prefix
- name = relationship.name
- else
- type = LIST_TEMPLATE%[relationship.destination]
- name = relationship.name
- end
- attributes_string << ' ' + IGNORED_ANNOTATION + "\n" if relationship.realm_ignored?
- attributes_string << ' ' + GSON_ANNOTATION%[relationship.json_key_path]+ "\n" unless relationship.json_key_path.empty?
- attributes_string << ' ' + ATTRIBUTE_TEMPLATE%[type, name] + "\n"
- getters_and_setters_string << "\n" unless getters_and_setters_string.empty?
- getters_and_setters_string << generate_getter_and_setter(type, name, (support_annotations and relationship.optional), (support_annotations and !relationship.optional), relationship.support_annotation)
- end
- end
- class_file << attributes_string + "\n" + getters_and_setters_string
- end
-
- def write_attributes(attributes, primary_key, use_wrappers, support_annotations)
- attributes_string = String.new
- getters_and_setters_string = String.new
- attributes.each_with_index do |(_, attribute), idx|
- unless attribute.read_only?
- name = attribute.name
- type = attribute.enum? ? 'String' : convert_type(attribute.type, (use_wrappers and attribute.optional))
- if type
- # Realm annotations
- attributes_string << ' ' + PRIMARY_KEY_ANNOTATION + "\n" if name == primary_key
- attributes_string << ' ' + INDEXED_ANNOTATION + "\n" if attribute.indexed
- attributes_string << ' ' + IGNORED_ANNOTATION + "\n" if attribute.realm_ignored? or attribute.enum?
- if attribute.enum?
- ignored_type = attribute.enum? ? attribute.enum_type.delete_objc_prefix : type
- ignored_name = attribute.enum? ? name + 'Enum' : name
- attributes_string << ' ' + ATTRIBUTE_TEMPLATE%[ignored_type, ignored_name] + "\n"
- end
- attributes_string << ' ' + GSON_ANNOTATION%[attribute.json_key_path]+ "\n" unless attribute.json_key_path.empty?
- attributes_string << ' ' + SUPPORT_ANNOTATION%[attribute.support_annotation] + "\n" unless attribute.support_annotation.empty?
- attributes_string << ' ' + ATTRIBUTE_TEMPLATE%[type, name] + "\n"
- end
- getters_and_setters_string << generate_getter_and_setter(type, name, (support_annotations and ((use_wrappers and attribute.optional) or (attribute.enum? and attribute.optional))), (support_annotations and ((!is_primitive(type) and !attribute.optional) or (attribute.enum? and !attribute.optional))), attribute.support_annotation)
- getters_and_setters_string << "\n" + generate_enum_getter_and_setter(attribute.enum_type.delete_objc_prefix, name, support_annotations) if attribute.enum?
- getters_and_setters_string << "\n" if idx != attributes.count - 1
- end
- end
- return attributes_string, getters_and_setters_string
- end
-
- def generate_getter_and_setter(type, name, nullable, nonnull, support_annotation)
- getter_setter = String.new
- getter_setter << ' ' + SUPPORT_ANNOTATION%['Nullable'] + "\n" if nullable
- getter_setter << ' ' + SUPPORT_ANNOTATION%['NonNull'] + "\n" if nonnull
- getter_setter << ' ' + SUPPORT_ANNOTATION%[support_annotation] + "\n" unless support_annotation.empty?
- getter_setter << ' ' + 'public ' + type + ' get' + name.capitalize_first_letter + '() {' + "\n"
- getter_setter << ' ' + 'return '+ name + ';' + "\n"
- getter_setter << ' ' + '}' + "\n\n"
- getter_setter << ' ' + 'public void set' + name.capitalize_first_letter + '('
- getter_setter << SUPPORT_ANNOTATION%['Nullable'] + ' ' if nullable
- getter_setter << SUPPORT_ANNOTATION%['NonNull'] + ' ' if nonnull
- getter_setter << SUPPORT_ANNOTATION%[support_annotation] + ' ' unless support_annotation.empty?
- getter_setter << 'final ' + type + ' ' + name + ') {' + "\n"
- getter_setter << ' ' + 'this.' + name + ' = ' + name + ';' + "\n"
- getter_setter << ' ' + '}' + "\n"
- getter_setter
- end
-
- end
-
- end
- end
-end
diff --git a/lib/gyro/realm/java/templates.rb b/lib/gyro/realm/java/templates.rb
deleted file mode 100644
index 4a318d6..0000000
--- a/lib/gyro/realm/java/templates.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module Java
- module Templates
-
- # ANNOTATIONS
- PRIMARY_KEY_ANNOTATION = '@PrimaryKey'
- INDEXED_ANNOTATION = '@Index'
- IGNORED_ANNOTATION = '@Ignore'
- GSON_ANNOTATION = "@SerializedName(\"%s\")"
- SUPPORT_ANNOTATION = "@android.support.annotation.%s"
-
- # COMMONS
- PACKAGE_TEMPLATE = 'package %s;'
- JAVA_FILE_TEMPLATE = '%s.java'
- GENERATED_MESSAGE = '/* DO NOT EDIT | Generated by gyro */'
-
- # IMPORTS
- IMPORT_DATE = 'import java.util.Date;'
- IMPORT_LIST = 'import java.util.List;'
- IMPORT_GSON = 'import com.google.gson.annotations.SerializedName;'
- IMPORT_REALM_LIST = 'import io.realm.RealmList;'
- IMPORT_REALM_OBJECT = 'import io.realm.RealmObject;'
- IMPORT_REALM_IGNORE = 'import io.realm.annotations.Ignore;'
- IMPORT_REALM_INDEX = 'import io.realm.annotations.Index;'
- IMPORT_REALM_PRIMARY_KEY = 'import io.realm.annotations.PrimaryKey;'
-
- # ENUM
- ENUM_TEMPLATE = 'public enum %s {'
- ENUM_JSON_VALUE = "(\"%s\")"
-
- # CLASS
- CLASS_TEMPLATE = 'public class %s extends RealmObject {'
- ATTRIBUTE_TEMPLATE = 'private %s %s;'
- FINAL_ATTRIBUTE_TEMPLATE = 'private final %s %s;'
- REALM_LIST_TEMPLATE = 'RealmList<%s>'
- LIST_TEMPLATE = 'List<%s>'
-
- # CONSTANTS
- ATTRIBUTE_CONSTANTS = 'public interface Attributes {'
- RELATIONSHIP_CONSTANTS = 'public interface Relationships {'
- CONSTANT_TEMPLATE = 'String %s = "%s";'
-
- # COMMENTS
- CLASS_COMMENT_TEMPLATE = "/**\n * %s\n */"
- ATTRIBUTE_COMMENT_TEMPLATE = '/** %s */'
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/objc/converter.rb b/lib/gyro/realm/objc/converter.rb
deleted file mode 100644
index e9b8ed7..0000000
--- a/lib/gyro/realm/objc/converter.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module ObjC
- module Converter
-
- TYPES = {
- :integer_16 => 'int',
- :integer_32 => 'long',
- :integer_64 => 'long long',
- :decimal => 'double',
- :double => 'double',
- :float => 'float',
- :string => 'NSString *',
- :boolean => 'BOOL',
- :date => 'NSDate *',
- :binary => 'NSData *'
- }
-
- DEFAULTS = {
- :integer_16 => '@(0)',
- :integer_32 => '@(0)',
- :integer_64 => '@(0)',
- :decimal => '@(0.0)',
- :double => '@(0.0)',
- :float => '@(0.0)',
- :string => '@""',
- :boolean => '@(NO)',
- :date => '[NSDate date]',
- :binary => '[NSData new]'
- }
-
- def is_number_type?(type)
- [:integer_16, :integer_32, :integer_64, :decimal, :double, :float].include?(type)
- end
-
- def convert_type(type, use_nsnumber = false)
- use_nsnumber && is_number_type?(type) ? 'NSNumber *' : TYPES[type]
- end
-
- def convert_default(type)
- DEFAULTS[type]
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/objc/enum_generator.rb b/lib/gyro/realm/objc/enum_generator.rb
deleted file mode 100644
index 7305555..0000000
--- a/lib/gyro/realm/objc/enum_generator.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module ObjC
- module EnumGenerator
-
- # INCLUDES #############################################################
-
- include Templates
- include Converter
-
- # PUBLIC METHODS #######################################################
-
- def generate_enum_file(path, xcdatamodel)
- enums = []
- enum_file = String.new
- enum_file << GENERATED_MESSAGE + "\n"
- enum_file << "\n" + SEPARATOR + "\n\n"
- enum_file << PRAGMA_MARK_TYPES + "\n\n"
- xcdatamodel.entities.each do |_, entity|
- entity.attributes.each do |_, attribute|
- if attribute.enum? and !enums.include?(attribute.enum_type)
- int_type = convert_type(attribute.type)
- enum_type = attribute.enum_type
- enum_file << "\n" if enums.length > 0
- enums.push(enum_type)
- enum = generate_enum(int_type, enum_type, attribute)
- enum_file << enum
- end
- end
- end
- if enums.size != 0
- Gyro.write_file_with_name(path, HEADER_TEMPLATE%[ENUM_FILE_NAME], enum_file)
- end
- end
-
- private ################################################################
-
- def generate_enum(int_type, enum_name, attribute)
- enum_string = String.new
- enum_string << ENUM_TYPEDEF_TEMPLATE%[int_type, enum_name] + "\n"
- enum_values = Array.new
- enum_values += %W(#{enum_name}None) if attribute.optional?
- enum_values += attribute.enum_values
- if enum_values.length != 0
- # First one
- value = enum_values[0]
- enum_string << ' ' + value + ' = 0,' + "\n"
- # Others
- (1..enum_values.length - 2).each { |i|
- value = enum_values[i]
- enum_string << ' ' + value + ',' + "\n"
- }
- # Last one if at least 2 values
- if enum_values.length > 1
- value = enum_values.last
- enum_string << ' ' + value + "\n"
- end
- end
- enum_string << '};' + "\n"
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/objc/generator.rb b/lib/gyro/realm/objc/generator.rb
deleted file mode 100644
index d11698b..0000000
--- a/lib/gyro/realm/objc/generator.rb
+++ /dev/null
@@ -1,370 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-require 'gyro/xcdatamodel/parser'
-require 'gyro/realm/objc/templates'
-require 'gyro/realm/objc/converter'
-require 'gyro/realm/objc/protocol_generator'
-require 'gyro/realm/objc/enum_generator'
-require 'gyro/realm/objc/json_category_generator'
-
-module Gyro
- module Realm
- module ObjC
-
- class Generator
-
- # INCLUDES #############################################################
-
- include Gyro::XCDataModel::Parser
- include Converter
- include Templates
- include ProtocolGenerator
- include EnumGenerator
- include JSONCategoryGenerator
-
- # PUBLIC METHODS #######################################################
-
- def initialize(path, xcdatamodel, json = false, framework = false, use_nsnumber = false)
- generate_class_files(path, xcdatamodel, use_nsnumber)
- generate_protocol_file(path, xcdatamodel)
- generate_enum_file(path, xcdatamodel)
- generate_objc_categories(path, xcdatamodel, framework) if json
- end
-
- private ################################################################
-
- def generate_class_files(path, xcdatamodel, use_nsnumber)
- puts "\n"
- Gyro::Log::title('Objc Realm')
- xcdatamodel.entities.each do |_, entity|
- unless entity.abstract?
- Gyro::Log::success("Generating entity #{entity.name}...")
- generate_class(path, entity, use_nsnumber)
- end
- end
- end
-
- def generate_class(path, entity, use_nsnumber)
- header_file = generate_header_file(entity, use_nsnumber)
- source_file = generate_source_file(entity, use_nsnumber)
- Gyro.write_file_with_name(path, HEADER_TEMPLATE%[entity.name], header_file)
- Gyro.write_file_with_name(path, SOURCE_TEMPLATE%[entity.name], source_file)
- end
-
- def generate_header_file(entity, use_nsnumber)
- header_file = String.new
- header_file << GENERATED_MESSAGE + "\n"
- header_file << "\n" + SEPARATOR + "\n\n"
- header_file << PRAGMA_MARK_IMPORTS + "\n\n"
- header_file << IMPORT_REALM + "\n"
- header_file << IMPORT_HEADER%[ENUM_FILE_NAME] + "\n" if require_enum_import(entity)
- header_file << generate_import_protocols(entity)
- header_file << generate_class_types(entity)
- header_file << generate_header_constants(entity)
- header_file << "\n" + SEPARATOR + "\n\n"
- header_file << PRAGMA_MARK_INTERFACE + "\n\n"
- header_file << CLASS_COMMENT_TEMPLATE%[entity.comment] + "\n" unless entity.comment.empty?
- header_file << INTERFACE_TEMPLATE%[entity.name] + "\n"
- header_file << generate_properties(entity, use_nsnumber)
- header_file << "\n" << END_CODE + "\n"
- end
-
- def generate_properties(entity, use_nsnumber)
- header_file = String.new
- header_file << "\n" + PRAGMA_MARK_PROPERTIES + "\n\n"
- entity.attributes.each do |_, attribute|
- header_file << PROPERTY_COMMENT_TEMPLATE%[attribute.comment] + "\n" unless attribute.comment.empty?
- name = attribute.name
- use_nsnumber_wrapper = use_nsnumber && require_nsnumber_wrapper(attribute)
- type = attribute.enum? ? attribute.enum_type : convert_type(attribute.type, use_nsnumber_wrapper)
- if type.nil?
- Gyro::Log::error("The property #{name} of entity #{entity.name} has an Undefined type.")
- type = 'id'
- end
- type = '(readonly) ' + type unless attribute.realm_read_only.empty?
- header_file << SIMPLE_PROPERTY_TEMPLATE%[type, type.end_with?('*') ? name : ' ' + name] + "\n"
- if use_nsnumber_wrapper
- num_type = convert_type(attribute.type)
- header_file << NUMBER_ACCESSOR_DECL_TEMPLATES%[num_type, name, name.capitalize_first_letter, num_type]
- end
- end
- entity.relationships.each do |_, relationship|
- is_list = relationship.type == :to_many
- if relationship.inverse?
- type = '(readonly) ' + (is_list ? 'NSArray' : relationship.inverse_type)
- name = relationship.name.delete_inverse_suffix
- else
- if relationship.destination.empty?
- type = is_list ? REALM_LIST_TEMPLATE%[relationship.inverse_type] : relationship.inverse_type
- else
- type = LIST_TEMPLATE%[convert_type(relationship.destination.downcase.to_sym)]
- end
- name = relationship.name
- end
- header_file << OBJECT_PROPERTY_TEMPLATE%[type, name] + "\n"
- end
- header_file
- end
-
- def require_nsnumber_wrapper(attribute)
- !attribute.enum? && attribute.realm_read_only.empty? && is_number_type?(attribute.type)
- end
-
- def require_enum_import(entity)
- entity.attributes.each do |_, attribute|
- if attribute.enum?
- return true
- end
- end
- false
- end
-
- def generate_import_protocols(entity)
- entity.has_list_attributes?(true) ? IMPORT_HEADER%[PROTOCOL_FILE_NAME] + "\n" : ''
- end
-
- def generate_class_types(entity)
- class_types = String.new
- entity.relationships.each do |_, relationship|
- class_types << CLASS_TEMPLATE%[relationship.inverse_type] + "\n" if relationship.inverse_type != entity.name && relationship.destination.empty?
- end
- class_types.empty? ? class_types : "\n" + PRAGMA_MARK_TYPES + "\n\n" + class_types
- end
-
- def generate_header_constants(entity)
- constants = String.new
- unless entity.attributes.empty?
- name = CONSTANT_ATTRIBUTES_NAME%[entity.name]
- constants << CONSTANT_HEADER_ATTRIBUTES%[name, name] + "\n"
- entity.attributes.each do |_, attribute|
- constants << ' ' + CONSTANT_HEADER_ITEM%[attribute.name] + "\n"
- end
- constants << "} #{name};\n"
- end
- if entity.has_no_inverse_relationship?
- constants << "\n" unless constants.empty?
- name = CONSTANT_RELATIONSHIPS_NAME%[entity.name]
- constants << CONSTANT_HEADER_RELATIONSHIPS%[name] + "\n"
- entity.relationships.each do |_, relationship|
- constants << ' ' + CONSTANT_HEADER_ITEM%[relationship.name] + "\n" unless relationship.inverse?
- end
- constants << "} #{name};\n"
- end
- constants.empty? ? constants : "\n" + PRAGMA_MARK_CONSTANTS + "\n\n"+ constants
- end
-
- def generate_source_file(entity, use_nsnumber)
- source_file = String.new
- source_file << GENERATED_MESSAGE + "\n"
- source_file << "\n" + SEPARATOR + "\n\n"
- source_file << PRAGMA_MARK_IMPORTS + "\n\n"
- source_file << IMPORT_HEADER%[entity.name] + "\n"
- source_file << generate_source_constants(entity)
- source_file << "\n" + SEPARATOR + "\n\n"
- source_file << PRAGMA_MARK_IMPLEMENTATION + "\n\n"
- source_file << IMPLEMENTATION_TEMPLATE%[entity.name] + "\n"
- source_file << generate_numbers_accessors(entity) if use_nsnumber
- if require_overriding(entity)
- source_file << "\n" + PRAGMA_MARK_SUPER + "\n"
- source_file << generate_primary_key(entity)
- source_file << generate_required_properties(entity)
- source_file << generate_default_values(entity)
- source_file << generate_ignored_properties(entity)
- source_file << generate_read_only_properties(entity, use_nsnumber)
- source_file << generate_inverse_properties(entity)
- end
- source_file << "\n" << END_CODE + "\n"
- end
-
- def generate_source_constants(entity)
- constants = String.new
- unless entity.attributes.empty?
- name = CONSTANT_ATTRIBUTES_NAME%[entity.name]
- constants << CONSTANT_SOURCE_ATTRIBUTES%[name, name] + "\n"
- entity.attributes.each_with_index do |(_, attribute), idx|
- constants << ',' + "\n" unless idx == 0
- constants << ' ' + CONSTANT_SOURCE_ITEM%[attribute.name, attribute.name]
- constants << "\n" if idx == entity.attributes.length - 1
- end
- constants << '};' + "\n"
- end
- if entity.has_no_inverse_relationship?
- constants << "\n" unless constants.empty?
- name = CONSTANT_RELATIONSHIPS_NAME%[entity.name]
- constants << CONSTANT_SOURCE_RELATIONSHIPS%[name, name] + "\n"
- has_first = false
- entity.relationships.each_with_index do |(_, relationship), idx|
- unless relationship.inverse?
- constants << ',' + "\n" if has_first
- constants << ' ' + CONSTANT_SOURCE_ITEM%[relationship.name, relationship.name]
- has_first = true
- end
- constants << "\n" if idx == entity.relationships.length - 1
- end
- constants << '};' + "\n"
- end
- constants.empty? ? constants : "\n" + PRAGMA_MARK_CONSTANTS + "\n\n" + constants
- end
-
- def generate_numbers_accessors(entity)
- number_accessors = String.new
- entity.attributes.each do |_, attribute|
- if require_nsnumber_wrapper(attribute)
- type = convert_type(attribute.type)
- name = attribute.name
- selector = type.gsub(/ /, '') + 'Value'
- number_accessors << NUMBER_ACCESSOR_SOURCE_TEMPLATES%[type, name, name, selector, name.capitalize_first_letter, type, name] + "\n"
- end
- end
- number_accessors.empty? ? "" : "\n" + PRAGMA_MARK_NUMBER_ACCESSORS + "\n\n" + number_accessors
- end
-
- def require_overriding(entity)
- if entity.has_primary_key? or entity.attributes.count > 0
- return true
- end
- false
- end
-
- def generate_primary_key(entity)
- primary_key = String.new
- if entity.has_primary_key?
- primary_key << "\n" + '+ (NSString *)primaryKey' + "\n"
- primary_key << '{' + "\n"
- primary_key << ' ' + "return @\"" + entity.identity_attribute + "\";" + "\n"
- primary_key << '}' + "\n"
- end
- primary_key
- end
-
- def generate_required_properties(entity)
- required_properties = String.new
- if entity.has_required?
- required_properties << "\n" + '// Specify required properties' + "\n"
- required_properties << '+ (NSArray *)requiredProperties' + "\n"
- required_properties << '{' + "\n"
- required_properties << ' ' + 'return @['
- entity.attributes.each do |_, attribute|
- required_properties << ARRAY_TEMPLATE%[attribute.name.add_quotes] if entity.is_required?(attribute)
- end
- required_properties = required_properties[0..required_properties.length - 3] # delete last coma
- required_properties << '];' + "\n"
- required_properties << '}' + "\n"
- end
- required_properties
- end
-
- def generate_default_values(entity)
- default_values = String.new
- if entity.has_required?
- default_values << "\n" + '// Specify default values for required properties' + "\n"
- default_values << '+ (NSDictionary *)defaultPropertyValues' + "\n"
- default_values << '{' + "\n"
- default_values << ' ' + 'return @{'
- entity.attributes.each do |_, attribute|
- if entity.is_required?(attribute)
- default_value = convert_default(attribute.type)
- if entity.has_default_value?(attribute) && !attribute.default.empty?
- default_value = attribute.type == :string ? "@\"#{attribute.default})\"" : "@(#{attribute.default})"
- end
- default_values << DICTIONARY_DEFAULT%[attribute.name.add_quotes, default_value] + ' '
- end
- end
- default_values = default_values[0..default_values.length - 3] # delete last coma
- default_values << '};' + "\n"
- default_values << '}' + "\n"
- end
- default_values
- end
-
- def generate_ignored_properties(entity)
- ignored_properties = String.new
- if entity.has_ignored?
- ignored_properties << "\n" + "// Specify properties to ignore (Realm won't persist these)" + "\n"
- ignored_properties << '+ (NSArray *)ignoredProperties' + "\n"
- ignored_properties << '{' + "\n"
- ignored_properties << ' ' + 'return @['
- entity.attributes.each do |_, attribute|
- ignored_properties << ARRAY_TEMPLATE%[attribute.name.add_quotes] if attribute.realm_ignored?
- end
- entity.relationships.each do |_, relationship|
- ignored_properties << ARRAY_TEMPLATE%[relationship.name.add_quotes] if relationship.realm_ignored?
- end
- ignored_properties = ignored_properties[0..ignored_properties.length - 3] # delete last coma
- ignored_properties << '];' + "\n"
- ignored_properties << '}' + "\n"
- end
- ignored_properties
- end
-
- def generate_read_only_properties(entity, use_nsnumber)
- read_only_properties = String.new
- entity.attributes.each do |_, attribute|
- unless attribute.realm_read_only.empty?
- type = attribute.enum? ? attribute.enum_type : convert_type(attribute.type, use_nsnumber)
- read_only_properties << "\n" + READ_ONLY_DEF_TEMPLATE%[type, attribute.name] + "\n"
- read_only_properties << '{' + "\n"
- read_only_properties << ' ' + attribute.realm_read_only + "\n"
- read_only_properties << '}' + "\n"
- end
- end
- read_only_properties
- end
-
- def generate_inverse_properties(entity)
- inverse_properties = String.new
- entity.relationships.each do |_, relationship|
- if relationship.inverse?
- if relationship.type == :to_many
- definition = INVERSE_DEF_TEMPLATE%['NSArray', relationship.name.delete_inverse_suffix]
- value = INVERSE_MANY_TEMPLATE%[relationship.inverse_type, relationship.inverse_name]
- else
- definition = INVERSE_DEF_TEMPLATE%[relationship.inverse_type, relationship.name.delete_inverse_suffix]
- value = INVERSE_ONE_TEMPLATE%[relationship.inverse_type, relationship.inverse_name]
- end
- inverse_properties << "\n" + definition + "\n"
- inverse_properties << '{' + "\n"
- inverse_properties << ' ' + value + "\n"
- inverse_properties << '}' + "\n"
- end
- end
- inverse_properties
- end
-
- def generate_indexed_properties(entity)
- indexed_properties = String.new
- if entity.has_indexed_attributes?
- indexed_properties << "\n" + '// Specify properties to index' + "\n"
- indexed_properties << '+ (NSArray *)ignoredProperties' + "\n"
- indexed_properties << '{' + "\n"
- indexed_properties << ' ' + 'return @['
- entity.attributes.each do |_, attribute|
- if attribute.indexed
- indexed_properties << ARRAY_TEMPLATE%[attribute.name.add_quotes]
- end
- end
- indexed_properties = indexed_properties[0..indexed_properties.length - 3] # delete last coma
- indexed_properties << '];' + "\n"
- indexed_properties << '}' + "\n"
- end
- indexed_properties
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/objc/json_category_generator.rb b/lib/gyro/realm/objc/json_category_generator.rb
deleted file mode 100644
index f784954..0000000
--- a/lib/gyro/realm/objc/json_category_generator.rb
+++ /dev/null
@@ -1,168 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module ObjC
- module JSONCategoryGenerator
-
- # INCLUDES #############################################################
-
- include Templates
-
- # PUBLIC METHODS #######################################################
-
- def generate_objc_categories(path, xcdatamodel, framework = false)
- json_path = File.join(path, 'JSON')
- Dir.mkdir(json_path) unless Dir.exists?(json_path)
- xcdatamodel.entities.each do |_, entity|
- generate_json_category_file(json_path, entity, framework)
- end
- end
-
- private #################################################################
-
- def generate_json_category_file(path, entity, framework)
- if !entity.attributes.empty? || !entity.relationships.empty?
- source_file = generate_source_category_file(entity)
- header_file = generate_header_category_file(entity, framework)
- file_name = JSON_CATEGORY_NAME%[entity.name]
- Gyro.write_file_with_name(path, HEADER_TEMPLATE%[file_name], header_file) unless header_file.empty?
- Gyro.write_file_with_name(path, SOURCE_TEMPLATE%[file_name], source_file) unless source_file.empty?
- end
- end
-
- def generate_header_category_file(entity, framework)
- header_file = String.new
- header_file << GENERATED_MESSAGE + "\n"
- header_file << "\n" + SEPARATOR + "\n\n"
- header_file << PRAGMA_MARK_IMPORTS + "\n\n"
- header_file << (framework ? IMPORT_REALM_JSON_FRAMEWORK : IMPORT_REALM_JSON_LIBRARY) + "\n"
- header_file << IMPORT_HEADER%[entity.name] + "\n"
- header_file << "\n" + SEPARATOR + "\n\n"
- header_file << PRAGMA_MARK_INTERFACE + "\n\n"
- header_file << JSON_CATEGORY_INTERFACE%[entity.name] + "\n\n"
- header_file << END_CODE + "\n"
- header_file
- end
-
- def generate_source_category_file(entity)
- source_file = String.new
- source_file << GENERATED_MESSAGE + "\n"
- source_file << "\n" + SEPARATOR + "\n\n"
- source_file << PRAGMA_MARK_IMPORTS + "\n\n"
- source_file << IMPORT_HEADER%[JSON_CATEGORY_NAME%[entity.name]] + "\n"
- source_file << IMPORT_HEADER%[ENUM_FILE_NAME] + "\n" if entity.has_enum_attributes?
- entity.transformers.each do |name|
- source_file << IMPORT_HEADER%[name]+"\n"
- end
- source_file << "\n" + SEPARATOR + "\n\n"
- source_file << PRAGMA_MARK_IMPLEMENTATION + "\n\n"
- source_file << JSON_CATEGORY_IMPLEMENTATION%[entity.name] + "\n\n"
- source_file << generate_mapping(entity)
- source_file << "\n" + generate_json_transformers(entity) if entity.need_transformer?
- source_file << "\n" + END_CODE + "\n"
- source_file
- end
-
- def generate_mapping(entity)
- inbound_mapping_string = '+ (NSDictionary *)JSONInboundMappingDictionary' + "\n"
- inbound_mapping_string << '{' + "\n"
- inbound_mapping_string << ' ' + 'return @{' + "\n"
- outbound_mapping_string = '+ (NSDictionary *)JSONOutboundMappingDictionary' + "\n"
- outbound_mapping_string << '{' + "\n"
- outbound_mapping_string << ' '+ 'return @{' + "\n"
-
- entity.attributes.each do |_, attribute|
- json_value = attribute.json_key_path.empty? ? attribute.name.add_quotes : attribute.json_key_path.add_quotes
- inbound_mapping_string << ' ' + DICTIONARY_JSON%[json_value, attribute.name.add_quotes] + "\n"
- outbound_mapping_string << ' ' + DICTIONARY_JSON%[attribute.name.add_quotes, json_value] + "\n"
- end
-
- entity.relationships.each do |_, relationship|
- json_value = relationship.json_key_path.empty? ? relationship.name.add_quotes : relationship.json_key_path.add_quotes
- inbound_mapping_string << ' ' + DICTIONARY_JSON%[json_value, relationship.name.add_quotes] + "\n"
- outbound_mapping_string << ' ' + DICTIONARY_JSON%[relationship.name.add_quotes, json_value] + "\n"
- end
-
- #delete last coma
- inbound_mapping_string = inbound_mapping_string[0..inbound_mapping_string.length - 3] + "\n"
- inbound_mapping_string << ' ' +'};' + "\n"
- inbound_mapping_string << '}' + "\n\n"
- outbound_mapping_string = outbound_mapping_string[0..outbound_mapping_string.length - 3] + "\n"
- outbound_mapping_string << ' ' + '};' + "\n"
- outbound_mapping_string << '}' + "\n"
- inbound_mapping_string + outbound_mapping_string
- end
-
- def generate_json_transformers(entity)
- json_transformer_string = String.new
- first_transformer = true
- entity.attributes.each do |(_, attribute)|
- if attribute.need_transformer?
- json_transformer_string << "\n" unless first_transformer
- first_transformer = false
- json_transformer_string << JSON_TRANSFORMER_DEF%[attribute.name] + "\n"
- json_transformer_string << '{'+ "\n"
- if !attribute.enum_type.empty? or attribute.type == :boolean # Enum | Boolean
- json_transformer_string << ' ' + 'return [MCJSONValueTransformer valueTransformerWithMappingDictionary:@{' + "\n"
- if attribute.type == :boolean
- json_values = TRANSFORMER_BOOL_JSON
- model_values = TRANSFORMER_BOOL_MODEL
- else
- if attribute.json_values.empty?
- Gyro::Error::raise("The attribute \"%s\" from \"%s\" is enum without JSONValues - please fix it"%[attribute.name, attribute.entity_name])
- end
- json_values = TRANSFORMER_ENUM_JSON + attribute.json_values.map { |enum| '@' + enum.add_quotes }
- enums = attribute.enum_values
- if attribute.optional?
- default = attribute.enum_type + 'None'
- else
- default = enums[attribute.default.to_i]
- end
- model_values = [default, default, default] + enums
- model_values = [default, default, default, default] + enums
- end
- json_transformer_string << format_json_transformers(json_values, model_values)
-
- # delete last coma
- json_transformer_string = json_transformer_string[0..json_transformer_string.length - 3] + "\n"
- json_transformer_string << ' ' + '}];' + "\n"
- else # custom transformer, or a default one
- transformer = attribute.transformer
- transformer = 'ISO8601DateTransform' if transformer.empty? && attribute.type == :date # default one for dates if none provided
- json_transformer_string << ' ' + TRANSFORMER%[transformer] + "\n" unless transformer.empty?
- end
- json_transformer_string << '}' + "\n"
- end
- end
- json_transformer_string
- end
-
- def format_json_transformers(json_values, model_values)
- json_transformer_string = String.new
- (0..model_values.length - 1).each { |enum|
- json_value = json_values[enum]
- enum_value = model_values[enum].add_parentheses
- json_transformer_string << ' ' + DICTIONARY_JSON_CATEGORY%[json_value, enum_value] + "\n"
- }
- json_transformer_string
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/objc/protocol_generator.rb b/lib/gyro/realm/objc/protocol_generator.rb
deleted file mode 100644
index 19aa884..0000000
--- a/lib/gyro/realm/objc/protocol_generator.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module ObjC
- module ProtocolGenerator
-
- # INCLUDES #############################################################
-
- include Templates
- include Converter
-
- # PUBLIC METHODS #######################################################
-
- def generate_protocol_file(path, xcdatamodel)
- content = String.new
- xcdatamodel.entities.each do |_, entity|
- unless entity.abstract?
- if entity.used_as_list_by_other?(xcdatamodel.entities)
- content << protocol_file_template if content.empty?
- content << REALM_LIST_TYPE_TEMPLATE%[entity.name, entity.name] + "\n"
- end
- end
- end
- Gyro.write_file_with_name(path, HEADER_TEMPLATE%[PROTOCOL_FILE_NAME], content) unless content.empty?
- end
-
- private ################################################################
-
- def protocol_file_template
- content = String.new
- content << GENERATED_MESSAGE + "\n"
- content << "\n" + SEPARATOR + "\n\n"
- content << PRAGMA_MARK_PROTOCOLS + "\n\n"
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/objc/templates.rb b/lib/gyro/realm/objc/templates.rb
deleted file mode 100644
index 3601087..0000000
--- a/lib/gyro/realm/objc/templates.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module ObjC
- module Templates
-
- # IMPORTS
- IMPORT_REALM = '#import '
- IMPORT_HEADER = '#import "%s.h"'
- IMPORT_REALM_JSON_LIBRARY = '#import '
- IMPORT_REALM_JSON_FRAMEWORK = '#import "RLMObject+JSON.h"'
-
-
- # PRAGMA
- PRAGMA_MARK_IMPORTS = '#pragma mark - Imports'
- PRAGMA_MARK_TYPES = '#pragma mark - Types'
- PRAGMA_MARK_CONSTANTS = '#pragma mark - Defines & Constants'
- PRAGMA_MARK_INTERFACE = '#pragma mark - Interface'
- PRAGMA_MARK_PROPERTIES = '#pragma mark - Properties'
- PRAGMA_MARK_PROTOCOLS = '#pragma mark - Protocols'
- PRAGMA_MARK_IMPLEMENTATION = '#pragma mark - Implementation'
- PRAGMA_MARK_NUMBER_ACCESSORS = '#pragma mark - NSNumber Convenience Accessors'
- PRAGMA_MARK_SUPER = '#pragma mark - Superclass Overrides'
-
- # COMMONS
- GENERATED_MESSAGE = '// DO NOT EDIT | Generated by gyro'
- END_CODE = '@end'
- SEPARATOR = '////////////////////////////////////////////////////////////////////////////////'
- DICTIONARY_JSON = '@%s : @%s,'
- DICTIONARY_DEFAULT = '@%s : %s,'
- DICTIONARY_JSON_CATEGORY = '%s : @%s,'
-
- # HEADER
- HEADER_TEMPLATE = '%s.h'
- NUMBER_TRANSFORMER_FILE_NAME = 'NFNumberTransformer'
- CLASS_COMMENT_TEMPLATE = "/**\n * %s\n */"
- CLASS_TEMPLATE = '@class %s;'
- INTERFACE_TEMPLATE = '@interface %s : RLMObject'
- PROPERTY_COMMENT_TEMPLATE = '/** %s */'
- SIMPLE_PROPERTY_TEMPLATE = '@property %s%s;'
- OBJECT_PROPERTY_TEMPLATE = '@property %s *%s;'
- NUMBER_ACCESSOR_DECL_TEMPLATES = "-(%s)%sValue;\n-(void)set%sValue:(%s)value;\n"
- LIST_TEMPLATE = 'NSArray<%s>'
- REALM_LIST_TYPE_TEMPLATE = "// This protocol enables typed collections. i.e.: RLMArray<%s>\nRLM_ARRAY_TYPE(%s)"
- REALM_LIST_TEMPLATE = 'RLMArray<%s>'
-
- # COMMONS
- CONSTANT_ATTRIBUTES_NAME = '%sAttributes'
- CONSTANT_RELATIONSHIPS_NAME = '%sRelationships'
- CONSTANT_HEADER_ATTRIBUTES = 'extern const struct %s {'
- CONSTANT_HEADER_RELATIONSHIPS = 'extern const struct %s {'
- CONSTANT_HEADER_ITEM = '__unsafe_unretained NSString *%s;'
- CONSTANT_SOURCE_ATTRIBUTES = 'const struct %s %s = {'
- CONSTANT_SOURCE_RELATIONSHIPS = 'const struct %s %s = {'
- CONSTANT_SOURCE_ITEM = '.%s = @"%s"'
-
- # ENUM
- ENUM_TYPEDEF_TEMPLATE = 'typedef NS_ENUM(%s, %s) {'
- ENUM_FILE_NAME = 'RLMTypes'
- PROTOCOL_FILE_NAME = 'RLMProtocols'
-
- # SOURCE
- SOURCE_TEMPLATE = '%s.m'
- IMPLEMENTATION_TEMPLATE = '@implementation %s'
- ARRAY_TEMPLATE ='@%s, '
- READ_ONLY_DEF_TEMPLATE = '- (%s)%s'
- NUMBER_ACCESSOR_SOURCE_TEMPLATES = "-(%s)%sValue\n{\n return [self.%s %s];\n}\n-(void)set%sValue:(%s)value\n{\n self.%s = @(value);\n}"
- INVERSE_DEF_TEMPLATE = '- (%s *)%s'
- INVERSE_MANY_TEMPLATE = 'return [self linkingObjectsOfClass:@"%s" forProperty:@"%s"];'
- INVERSE_ONE_TEMPLATE = 'return [[self linkingObjectsOfClass:@"%s" forProperty:@"%s"] objectAtIndex:0];'
-
- # JSON_CATEGORY
- JSON_CATEGORY_NAME = '%s+JSON'
- JSON_TRANSFORMER_DEF = '+ (NSValueTransformer *)%sJSONTransformer'
- JSON_CATEGORY_IMPLEMENTATION = '@implementation %s (JSON)'
- JSON_CATEGORY_INTERFACE = '@interface %s (JSON)'
- TRANSFORMER_BOOL_JSON = ['@"null"', '[NSNull null]', '@""', '@"false"', '@"true"', '@(NO)', '@(YES)']
- TRANSFORMER_BOOL_MODEL = %w(NO NO NO NO YES NO YES)
- TRANSFORMER_ENUM_JSON = ['@"null"', '[NSNull null]', '@""', '@""']
- TRANSFORMER = 'return [[%s alloc] init];'
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/swift/converter.rb b/lib/gyro/realm/swift/converter.rb
deleted file mode 100644
index b7fd05f..0000000
--- a/lib/gyro/realm/swift/converter.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module Swift
- module Converter
-
- TYPES = {
- :integer_16 => 'Int16',
- :integer_32 => 'Int32',
- :integer_64 => 'Int64',
- :decimal => 'Double',
- :double => 'Double',
- :float => 'Float',
- :string => 'String',
- :boolean => 'Bool',
- :date => 'NSDate',
- :binary => 'NSData'
- }
-
- DEFAULTS = {
- :integer_16 => '0',
- :integer_32 => '0',
- :integer_64 => '0',
- :decimal => '0.0',
- :double => '0.0',
- :float => '0.0',
- :string => '""',
- :boolean => 'false',
- :date => 'NSDate()',
- :binary => 'NSData()'
- }
-
- def convert_type(type)
- TYPES[type]
- end
-
- def convert_default(type, value = nil)
- if value.nil?
- return DEFAULTS[type]
- end
- # Do some conversions for some special types
- case [type, value]
- when [:boolean, 'YES']
- return 'true'
- when [:boolean, 'NO']
- return 'false'
- else
- return value
- end
- end
-
- def is_number?(type)
- type != :string && type != :date && type != :binary
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/swift/enum_generator.rb b/lib/gyro/realm/swift/enum_generator.rb
deleted file mode 100644
index faf825d..0000000
--- a/lib/gyro/realm/swift/enum_generator.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module Swift
- module EnumGenerator
-
- # INCLUDES #############################################################
-
- include Templates
-
- # PUBLIC METHODS #######################################################
-
- def generate_enums(path, attributes)
- enums = Array.new
- attributes.each do |_, attribute|
- if attribute.enum? and !enums.include?(attribute.enum_type)
- enum_type = attribute.enum_type.delete_objc_prefix
- enums.push(enum_type)
- generate_enum(path, enum_type, attribute.enum_values, attribute.json_values)
- end
- end
- end
-
- private ################################################################
-
- def generate_enum(path, enum_name, enum_values, raw_values)
- enum_file = String.new
- enum_file << GENERATED_MESSAGE + "\n\n"
- enum_file << ENUM_STRING_DEF_TEMPLATE%[enum_name] + "\n"
- raw_values = raw_values(enum_values, raw_values)
- if !enum_values.empty? and raw_values.length == enum_values.length
- (0..enum_values.length - 1).each { |idx|
- enum_value = enum_values[idx].delete_objc_prefix
- raw_value = raw_values[idx]
- enum_file << ' ' + ENUM_STRING_CASE_TEMPLATE%[enum_value, raw_value] + "\n"
- }
- enum_file << '}' + "\n"
- Gyro.write_file_with_name(path, SWIFT_FILE_TEMPLATE%[enum_name], enum_file)
- end
- end
-
- def raw_values(enum_values, raw_values)
- if raw_values.empty?
- raw_values = enum_values.map { |value|
- value.delete_objc_prefix.underscore
- }
- end
- raw_values
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/swift/generator.rb b/lib/gyro/realm/swift/generator.rb
deleted file mode 100644
index 918a952..0000000
--- a/lib/gyro/realm/swift/generator.rb
+++ /dev/null
@@ -1,262 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-require 'gyro/xcdatamodel/parser'
-require 'gyro/realm/swift/templates'
-require 'gyro/realm/swift/converter'
-require 'gyro/realm/swift/enum_generator'
-require 'gyro/realm/swift/object_mapper_generator'
-
-module Gyro
- module Realm
- module Swift
-
- class Generator
-
- # INCLUDES #############################################################
-
- include Gyro::XCDataModel::Parser
- include Converter
- include EnumGenerator
- include Templates
- include ObjectMapperGenerator
-
- # PUBLIC METHODS #######################################################
-
- def initialize(path, xcdatamodel, json = false)
- puts "\n"
- Gyro::Log::title('Swift Realm')
- xcdatamodel.entities.each do |_, entity|
- unless entity.abstract?
- Gyro::Log::success("Generating entity #{entity.name}...")
- generate_class(path, entity, json)
- generate_object_mapper_categories(path, xcdatamodel) if json
- end
- end
- end
-
- private ################################################################
-
- def generate_class(path, entity, json)
- class_file = String.new
- entity.name = entity.name.delete_objc_prefix
- class_file << generate_header(entity)
- class_file << generate_attributes(entity.attributes, entity.relationships, json)
- class_file << generate_inverse_properties(entity)
- class_file << generate_primary_key(entity)
- class_file << generate_indexed_properties(entity)
- class_file << generate_ignored_properties(entity)
- class_file << '}' + "\n"
- Gyro.write_file_with_name(path, SWIFT_FILE_TEMPLATE%[entity.name], class_file)
- generate_enums(path, entity.attributes)
- end
-
- def generate_header(entity)
- class_file = String.new
- class_file << GENERATED_MESSAGE + "\n\n"
- class_file << IMPORT_REALM + "\n\n"
- class_file << CLASS_COMMENT_TEMPLATE%[entity.comment] + "\n" unless entity.comment.empty?
- class_file << CLASS_TEMPLATE%[entity.name] + "\n\n"
- class_file << generate_constants(entity)
- end
-
- def generate_constants(entity)
- attribute_constants = String.new
- unless entity.attributes.empty?
- attribute_constants << ' ' + ENUM_STRING_DEF_TEMPLATE%['Attributes'] + "\n"
- entity.attributes.each do |_, attribute|
- unless attribute.realm_ignored? or attribute.read_only?
- attribute_constants << ' ' + ATTRIBUTE_COMMENT_TEMPLATE%[attribute.comment] + "\n" unless attribute.comment.empty?
- attribute_constants << ' ' + ENUM_STRING_CASE_TEMPLATE%[attribute.name.capitalize_first_letter, attribute.name] + "\n"
- end
- end
- attribute_constants << ' ' + '}'+ "\n\n"
- end
- relationship_constants = String.new
- if not entity.relationships.empty? and not entity.has_only_inverse?
- relationship_constants << ' ' + ENUM_STRING_DEF_TEMPLATE%['Relationships'] + "\n"
- entity.relationships.each do |_, relationship|
- relationship_constants << ' ' + ENUM_STRING_CASE_TEMPLATE%[relationship.name.capitalize_first_letter, relationship.name] + "\n" if not relationship.inverse?
- end
- relationship_constants << ' ' + '}'+ "\n\n"
- end
- attribute_constants + relationship_constants
- end
-
- def generate_attributes(attributes, relationships, json)
- # "NORMAL" ATTRIBUTES
- attributes_string = write_attributes(attributes, json)
- # "RELATIONSHIP" ATTRIBUTES
- relationships.each do |_, relationship|
- unless relationship.inverse?
- is_list = relationship.type == :to_many
- name = relationship.name
- type = relationship.inverse_type.delete_objc_prefix
- if is_list
- if json
- attributes_string << ' ' + PROPERTY_LIST_VAR_TEMPLATE%[name, type] + "\n"
- else
- attributes_string << ' ' + PROPERTY_LIST_TEMPLATE%[name, type] + "\n"
- end
- else
- attributes_string << ' ' + PROPERTY_OBJECT_TEMPLATE%[name, type] + "\n"
- end
- end
- end
- attributes_string + "\n"
- end
-
- def write_attributes(attributes, json)
- attributes_string = String.new
- attributes.each_with_index do |(_, attribute)|
- unless attribute.read_only?
- if attribute.enum?
- attributes_string << write_enum_attribute(attribute, json)
- else
- if attribute.optional?
- attributes_string << write_optional_attribute(attribute, json) + "\n"
- else
- default_value = convert_default(attribute.type, attribute.has_default? ? attribute.default : nil)
- attributes_string << ' ' + PROPERTY_DEFAULT_TEMPLATE%[attribute.name, convert_type(attribute.type), default_value] + "\n"
- end
- end
- end
- end
- attributes_string
- end
-
- def write_optional_attribute(attribute, json)
- optional_string = String.new
- type = convert_type(attribute.type)
- if attribute.is_number? or attribute.is_bool?
- if json
- optional_string << ' ' + PROPERTY_OPTIONAL_NUMBER_VAR_TEMPLATE%[attribute.name, type]
- else
- optional_string << ' ' + PROPERTY_OPTIONAL_NUMBER_TEMPLATE%[attribute.name, type]
- end
- else
- optional_string << ' ' + PROPERTY_OPTIONAL_NON_NUMBER_TEMPLATE%[attribute.name, type]
- end
- optional_string
- end
-
- def write_enum_attribute(attribute, json)
- enum_string = String.new
- if attribute.optional?
- enum_string << ' ' + PROPERTY_OPTIONAL_ENUM_TEMPLATE%[attribute.name] + "\n"
- else
- enum_string << ' ' + PROPERTY_ENUM_TEMPLATE%[attribute.name] + "\n"
- end
- enum_type = attribute.enum_type.delete_objc_prefix
- enum_name = attribute.name + 'Enum'
-
- enum_string << ' ' + PROPERTY_OPTIONAL_COMPUTED_TEMPLATE%[enum_name, enum_type] + "\n"
- enum_string << ' ' + 'get {' + "\n"
- if attribute.optional?
- enum_string << ' ' + "guard let #{attribute.name} = #{attribute.name},\n"
- enum_string << ' ' + " let enumValue = #{enum_type}(rawValue: #{attribute.name})\n"
- enum_string << ' ' + " else { return nil }" + "\n"
- else
- enum_string << ' ' + "guard let enumValue = #{enum_type}(rawValue: #{attribute.name}) else { return nil }" + "\n"
- end
- enum_string << ' ' + "return enumValue" + "\n"
- enum_string << ' ' + '}' + "\n"
-
- if attribute.optional?
- enum_string << ' ' + "set { #{attribute.name} = newValue?.rawValue ?? nil }" + "\n"
- else
- enum_string << ' ' + "set { #{attribute.name} = newValue?.rawValue ?? \"\" }" + "\n"
- end
- enum_string << ' ' + '}' + "\n\n"
- end
-
- def generate_primary_key(entity)
- primary_key = String.new
- if entity.has_primary_key?
- primary_key << ' ' + 'override static func primaryKey() -> String? {' + "\n"
- primary_key << ' ' + "return #{entity.identity_attribute.add_quotes}" + "\n"
- primary_key << ' ' + '}' + "\n\n"
- end
- primary_key
- end
-
- def generate_ignored_properties(entity)
- ignored_properties = String.new
- if entity.has_ignored?
- ignored_properties << ' ' + "// Specify properties to ignore (Realm won't persist these)" + "\n"
- ignored_properties << ' ' +'override static func ignoredProperties() -> [String] {' + "\n"
- ignored_properties << ' ' + 'return ['
- entity.attributes.each do |_, attribute|
- ignored_properties << ARRAY_TEMPLATE%[attribute.name.add_quotes] if attribute.realm_ignored?
- end
- entity.relationships.each do |_, relationship|
- ignored_properties << ARRAY_TEMPLATE%[relationship.name.add_quotes] if relationship.realm_ignored?
- end
- ignored_properties = ignored_properties[0..ignored_properties.length - 3] # delete last coma
- ignored_properties << ']' + "\n"
- ignored_properties << ' ' + '}' + "\n\n"
- end
- ignored_properties
- end
-
- def generate_inverse_properties(entity)
- inverse_properties = []
- entity.relationships.each do |_, relationship|
- if relationship.inverse?
- if relationship.type == :to_many
- inverse_properties << PROPERTY_INVERSE_MANY_TEMPLATE%[
- relationship.name.delete_inverse_suffix,
- relationship.inverse_type.delete_objc_prefix,
- relationship.inverse_name
- ]
- else
- inverse_properties << PROPERTY_INVERSE_MANY_TEMPLATE%[
- relationship.name.delete_inverse_suffix + 's', # 's' for plural form
- relationship.inverse_type.delete_objc_prefix,
- relationship.inverse_name
- ]
- inverse_properties << PROPERTY_INVERSE_ONE_TEMPLATE%[
- relationship.name.delete_inverse_suffix,
- relationship.inverse_type.delete_objc_prefix,
- relationship.name.delete_inverse_suffix + 's' # 's' for plural form
- ]
- end
- end
- end
- return '' if inverse_properties.empty?
- inverse_properties.map { |value| " #{value}\n" }.join + "\n"
- end
-
- def generate_indexed_properties(entity)
- indexed_properties = String.new
- if entity.has_indexed_attributes?
- indexed_properties << ' ' + '// Specify properties to index' + "\n"
- indexed_properties << ' ' +'override static func indexedProperties() -> [String] {' + "\n"
- indexed_properties << ' ' + 'return ['
- entity.attributes.each do |_, attribute|
- indexed_properties << ARRAY_TEMPLATE%[attribute.name.add_quotes] if attribute.indexed?
- end
- indexed_properties = indexed_properties[0..indexed_properties.length - 3] # delete last coma
- indexed_properties << ']' + "\n"
- indexed_properties << '}' + "\n\n"
- end
- indexed_properties
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/swift/object_mapper_generator.rb b/lib/gyro/realm/swift/object_mapper_generator.rb
deleted file mode 100644
index c18f78d..0000000
--- a/lib/gyro/realm/swift/object_mapper_generator.rb
+++ /dev/null
@@ -1,120 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module Swift
- module ObjectMapperGenerator
-
- # INCLUDES #############################################################
-
- include Templates
-
- # PUBLIC METHODS #######################################################
-
- def generate_object_mapper_categories(path, xcdatamodel, framework = false)
- json_path = File.join(path, 'ObjectMapper')
- Dir.mkdir(json_path) unless Dir.exists?(json_path)
- xcdatamodel.entities.each do |_, entity|
- generate_json_category_file(json_path, entity, framework)
- end
- end
-
- # PRIVATE METHODS #######################################################
-
- def generate_json_category_file(path, entity, framework)
- if !entity.attributes.empty? || !entity.relationships.empty?
- source_file = generate_source_extension_file(entity)
- file_name = EXTENSION_NAME%[entity.name]
- Gyro.write_file_with_name(path, SOURCE_TEMPLATE%[file_name], source_file) unless source_file.empty?
- end
- end
-
- def generate_source_extension_file(entity)
- source_file = String.new
- source_file << GENERATED_MESSAGE + "\n\n"
- source_file << IMPORT_OBJECT_MAPPER + "\n\n"
- source_file << EXTENSION_TEMPLATE%[entity.name, "Mappable"] + "\n\n"
- source_file << generate_init
- source_file << generate_mapping(entity)
- source_file << "}\n"
- source_file
- end
-
- def generate_mapping(entity)
- source_file = String.new
- source_file << " // MARK: Mappable\n\n"
- source_file << " func mapping(map: Map) {\n"
- source_file << generate_mapper_attributes(entity) if !entity.attributes.empty?
- source_file << generate_mapper_relationships(entity) if !entity.relationships.empty?
- source_file << " }\n"
- source_file
- end
-
- def generate_init()
- source_file = String.new
- source_file << " // MARK: Initializers\n\n"
- source_file << " convenience init?(_ map: Map) {\n"
- source_file << " self.init()\n"
- source_file << " }\n\n"
- source_file
- end
-
- def generate_mapper_attributes(entity)
- attributes = String.new
- attributes << "\n // MARK: Attributes\n"
- entity.attributes.each do |_, attribute|
- attrKey = attribute.json_key_path.empty? ? attribute.name : attribute.json_key_path
- case
- when attribute.type == :date
- transformer = attribute.transformer.empty? ? "ISO8601DateTransform" : attribute.transformer
- attributes << " self." + attribute.name + " <- (map[" + attrKey.add_quotes + "], " + transformer + "())\n"
- when attribute.type == :integer_16 && attribute.optional && attribute.enum_type == ""
- attributes << " self." + attribute.name + " <- (map[" + attrKey.add_quotes + "], RealmOptionalInt16Transform())\n"
- when attribute.type == :integer_32 && attribute.optional
- attributes << " self." + attribute.name + " <- (map[" + attrKey.add_quotes + "], RealmOptionalInt32Transform())\n"
- when attribute.type == :integer_64 && attribute.optional
- attributes << " self." + attribute.name + " <- (map[" + attrKey.add_quotes + "], RealmOptionalInt64Transform())\n"
- when attribute.type == :float && attribute.optional
- attributes << " self." + attribute.name + " <- (map[" + attrKey.add_quotes + "], RealmOptionalFloatTransform())\n"
- when attribute.type == :double && attribute.optional
- attributes << " self." + attribute.name + " <- (map[" + attrKey.add_quotes + "], RealmOptionalDoubleTransform())\n"
- when
- attributes << " self." + attribute.name + " <- map[" + attrKey.add_quotes + "]\n"
- end
- end
- attributes
- end
-
- def generate_mapper_relationships(entity)
- relationships = String.new
- relationships << "\n // MARK: Relationships\n"
- entity.relationships.each do |_, relationship|
- next if relationship.inverse?
- relationKey = relationship.json_key_path.empty? ? relationship.name : relationship.json_key_path
- if relationship.type == :to_many
- relationships << " self." + relationship.name + " <- (map[" + relationKey.add_quotes + "], ListTransform<" + relationship.inverse_type + ">())\n"
- else
- relationships << " self." + relationship.name + " <- map[" + relationKey.add_quotes + "]\n"
- end
- end
- relationships
- end
-
- end
- end
- end
-end
diff --git a/lib/gyro/realm/swift/templates.rb b/lib/gyro/realm/swift/templates.rb
deleted file mode 100644
index 6aacea8..0000000
--- a/lib/gyro/realm/swift/templates.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Realm
- module Swift
- module Templates
-
- # COMMONS
- GENERATED_MESSAGE = '/* DO NOT EDIT | Generated by gyro */'
-
- # IMPORTS
- IMPORT_REALM = 'import RealmSwift'
- SWIFT_FILE_TEMPLATE = '%s.swift'
- IMPORT_OBJECT_MAPPER = 'import ObjectMapper'
-
- # ENUM
- ENUM_STRING_DEF_TEMPLATE = 'enum %s: String {'
- ENUM_STRING_CASE_TEMPLATE = 'case %s = "%s"'
-
- # CLASS
- ARRAY_TEMPLATE ='%s, '
- CLASS_TEMPLATE = 'final class %s: Object {'
- PROPERTY_DEFAULT_TEMPLATE = 'dynamic var %s: %s = %s'
- PROPERTY_OBJECT_TEMPLATE = 'dynamic var %s: %s?'
- PROPERTY_LIST_TEMPLATE = 'let %s = List<%s>()'
- PROPERTY_LIST_VAR_TEMPLATE = 'var %s = List<%s>()'
- PROPERTY_PRIVATE_ENUM_TEMPLATE = 'private dynamic var %s: String?'
- PROPERTY_ENUM_TEMPLATE = 'dynamic var %s: String = ""'
- PROPERTY_OPTIONAL_ENUM_TEMPLATE = 'dynamic var %s: String? = nil'
- PROPERTY_COMPUTED_TEMPLATE = 'var %s: %s {'
- PROPERTY_OPTIONAL_COMPUTED_TEMPLATE = 'var %s: %s? {'
- PROPERTY_INVERSE_ONE_TEMPLATE = 'var %s: %s? { return %s.first }'
- PROPERTY_INVERSE_MANY_TEMPLATE = 'let %s = LinkingObjects(fromType: %s.self, property: "%s")'
- PROPERTY_OPTIONAL_NON_NUMBER_TEMPLATE = 'dynamic var %s: %s? = nil'
- PROPERTY_OPTIONAL_NUMBER_TEMPLATE = 'let %s = RealmOptional<%s>()'
- PROPERTY_OPTIONAL_NUMBER_VAR_TEMPLATE = 'var %s = RealmOptional<%s>()'
-
- # EXTENSION
- EXTENSION_TEMPLATE = 'extension %s: %s {'
- EXTENSION_NAME = '%sMapper'
- SOURCE_TEMPLATE = '%s.swift'
-
- # COMMENTS
- CLASS_COMMENT_TEMPLATE = "/**\n * %s\n */"
- ATTRIBUTE_COMMENT_TEMPLATE = '/** %s */'
-
- end
- end
- end
-end
diff --git a/lib/gyro/template.rb b/lib/gyro/template.rb
new file mode 100644
index 0000000..52fafe4
--- /dev/null
+++ b/lib/gyro/template.rb
@@ -0,0 +1,68 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module Gyro
+ # Gyro Template Helper
+ #
+ module Template
+ def self.print_list
+ Gyro::Template.directory.children.select(&:directory?).each do |entry|
+ puts " - #{entry.basename}"
+ end
+ end
+
+ def self.print_infos(template)
+ readme = if template.include?('/')
+ Pathname.new(template) + 'README.md'
+ else
+ Gyro::Template.directory + template + 'README.md'
+ end
+
+ Gyro::Log.fail!("No README.md found for template #{template}.") unless readme.exist?
+ puts readme.read
+ end
+
+ def self.directory
+ Pathname.new(File.dirname(__FILE__)) + '../templates'
+ end
+
+ def self.find(template_param)
+ if template_param.include? '/'
+ find_by_path(template_param)
+ else
+ find_by_name(template_param)
+ end
+ end
+
+ def self.find_by_path(path)
+ template_dir = Pathname.new(path)
+ unless template_dir.exist?
+ Gyro::Log.fail!('You need to specify existing template directory using --template option' \
+ ' (see --help for more info)')
+ end
+
+ return template_dir if template_dir.directory?
+ return template_dir.dirname if template_dir.file?
+ Gyro::Log.fail!('You need to specify right template directory using --template option' \
+ ' (see --help for more info)')
+ end
+
+ def self.find_by_name(name)
+ template_dir = Gyro::Template.directory + name
+ return template_dir if template_dir.exist?
+ Gyro::Log.fail!('You need to specify existing default template name using --template option' \
+ ' (see --help for more info)')
+ end
+ end
+end
diff --git a/lib/gyro/utils/error.rb b/lib/gyro/utils/error.rb
deleted file mode 100644
index 20f604d..0000000
--- a/lib/gyro/utils/error.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Error
- def self.raise(str)
- Kernel.raise "\e[1;31m! #{str}\e[0m"
- end
-
- def self.exit_with_error(message)
- Gyro::Log::error message
- exit 1
- end
- end
-end
diff --git a/lib/gyro/utils/file_utils.rb b/lib/gyro/utils/file_utils.rb
deleted file mode 100644
index 07c174f..0000000
--- a/lib/gyro/utils/file_utils.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- def self.find_xcdatamodel(dir)
- Dir.chdir(dir) do
- files = Dir.glob('*.xcdatamodel')
- files.first.nil? ? nil : File.expand_path(files.first, dir)
- end
- end
-
- def self.write_file_with_name(dir, name_file, content)
- file_path = File.expand_path(name_file, dir)
- File.write(file_path, content)
- end
-end
diff --git a/lib/gyro/utils/log.rb b/lib/gyro/utils/log.rb
deleted file mode 100644
index 247f7bc..0000000
--- a/lib/gyro/utils/log.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module Log
-
- def self.title(str) # bg yellow
- puts "\e[44;37m#{str}\e[0m"
- end
-
- def self.error(str)
- puts "\e[1;31m! #{str}\e[0m"
- end
-
- def self.info(str)
- puts "\e[1;33m> #{str}\e[0m"
- end
-
- def self.success(str)
- puts "\e[1;32m√ #{str}\e[0m"
- end
-
- def self.prompt(str, url = nil)
- prompt = "\e[1;36m ! #{str} [y/n]?\e[0m "
- url_info = ' '*10 + "\e[0;37m (use '?' to show in browser)\e[0m"
- print prompt
- print "#{url_info}\r#{prompt}" if url
-
- answer = get_char do |c|
- `open '#{url}'` if url && (c == '?')
- "yn\003".include?(c.downcase) # \003 = ctrl-C
- end
- puts answer + (url ? ' '*url_info.length : '')
- answer.downcase == 'y'
- end
-
- private ######################################################################
-
- def self.get_char
- stop = false
- typed_char = ''
- begin
- system('stty raw -echo')
- until stop
- typed_char = STDIN.getc.chr
- stop = yield typed_char
- end
- ensure
- system('stty -raw echo')
- end
- typed_char
- end
-
- end
-end
diff --git a/lib/gyro/utils/string_xcdatamodel.rb b/lib/gyro/utils/string_xcdatamodel.rb
deleted file mode 100644
index c9dc27d..0000000
--- a/lib/gyro/utils/string_xcdatamodel.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-class String
-
- def delete_objc_prefix
- i = 0
- while i < self.length - 1 and /[[:upper:]]/.match(self[i+1])
- i += 1
- end
- self[i..self.length]
- end
-
- def delete_inverse_suffix
- self.gsub('_', '')
- end
-
- def capitalize_first_letter
- self.slice(0, 1).capitalize + self.slice(1..-1)
- end
-
- def camel_case
- words = self.scan(/[A-Z][a-z]+/)
- words.map!(&:upcase)
- words.join('_')
- end
-
- def underscore
- self.gsub(/::/, '/').
- gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
- gsub(/([a-z\d])([A-Z])/, '\1_\2').
- tr('-', '_').
- downcase
- end
-
- def add_quotes
- "\"" + self + "\""
- end
-
- def add_parentheses
- '(' + self + ')'
- end
-
-
-end
diff --git a/lib/gyro/version.rb b/lib/gyro/version.rb
index c51bd15..8ad998d 100644
--- a/lib/gyro/version.rb
+++ b/lib/gyro/version.rb
@@ -1,3 +1,19 @@
+# Copyright 2016 - Niji
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Gyro Version
+#
module Gyro
- VERSION = '0.4.1'
+ VERSION = '1.0.0'.freeze
end
diff --git a/lib/gyro/xcdatamodel/parser.rb b/lib/gyro/xcdatamodel/parser.rb
deleted file mode 100644
index 5cbb915..0000000
--- a/lib/gyro/xcdatamodel/parser.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-require 'nokogiri'
-
-require 'gyro/xcdatamodel/parser/attribute'
-require 'gyro/xcdatamodel/parser/relationship'
-require 'gyro/xcdatamodel/parser/entity'
-require 'gyro/xcdatamodel/parser/xcdatamodel'
diff --git a/lib/gyro/xcdatamodel/parser/attribute.rb b/lib/gyro/xcdatamodel/parser/attribute.rb
deleted file mode 100644
index 860aba0..0000000
--- a/lib/gyro/xcdatamodel/parser/attribute.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module XCDataModel
- module Parser
-
- class Attribute
-
- attr_accessor :entity_name, :name, :type, :optional, :indexed, :default
- attr_accessor :realm_ignored, :realm_read_only, :enum_type, :enum_values
- attr_accessor :json_key_path, :json_values, :transformer, :comment, :support_annotation
-
- alias_method :optional?, :optional
- alias_method :indexed?, :indexed
- alias_method :realm_ignored?, :realm_ignored
-
- def initialize (attribute_xml, entity_name)
- @entity_name = entity_name
- @name = attribute_xml.xpath('@name').to_s
- @optional = attribute_xml.xpath('@optional').to_s == 'YES' ? true : false
- @indexed = attribute_xml.xpath('@indexed').to_s == 'YES' ? true : false
- @default = attribute_xml.xpath('@defaultValueString').to_s
- @type = attribute_xml.xpath('@attributeType').to_s.downcase.gsub(' ', '_').to_sym
- @realm_ignored = attribute_xml.xpath(USERINFO_VALUE%['realmIgnored']).to_s.empty? ? false : true
- @realm_read_only = attribute_xml.xpath(USERINFO_VALUE%['realmReadOnly']).to_s
- @enum_type = attribute_xml.xpath(USERINFO_VALUE%['enumType']).to_s
- @enum_values = attribute_xml.xpath(USERINFO_VALUE%['enumValues']).to_s.split(',')
- @json_key_path = attribute_xml.xpath(USERINFO_VALUE%['JSONKeyPath']).to_s
- @json_values = attribute_xml.xpath(USERINFO_VALUE%['JSONValues']).to_s.split(',')
- @transformer = attribute_xml.xpath(USERINFO_VALUE%['transformer']).to_s.strip
- @comment = attribute_xml.xpath(USERINFO_VALUE%['comment']).to_s
- @support_annotation = attribute_xml.xpath(USERINFO_VALUE%['supportAnnotation']).to_s
- search_for_error
- end
-
- def enum?
- !@enum_type.empty?
- end
-
- def read_only?
- !@realm_read_only.empty?
- end
-
- def has_default?
- !@default.empty?
- end
-
- def to_s
- "\tAttribute => name=#{@name} | type=#{@type} | optional=#{@optional} | default=#{@default} | indexed=#{@indexed}\n"
- end
-
- def is_decimal?
- @type == :decimal or @type == :double or @type == :float
- end
-
- def is_integer?
- @type == :integer_16 or @type == :integer_32 or @type == :integer_64
- end
-
- def is_number?
- is_decimal? or is_integer?
- end
-
- def is_bool?
- @type == :boolean
- end
-
- def need_transformer?
- !@enum_type.empty? or @type == :boolean or @type == :date or !@transformer.empty?
- end
-
- private ################################################################
-
- def search_for_error
- Gyro::Error::raise("The attribute \"%s\" from \"%s\" has no type - please fix it"%[@name, @entity_name]) if @type == :undefined || @type.empty?
- Gyro::Error::raise("The attribute \"%s\" from \"%s\" is enum with incorrect type (not Integer) - please fix it"%[@name, @entity_name]) if !@enum_type.empty? and !@enum_values.empty? and !is_integer?
- Gyro::Error::raise("The attribute \"%s\" from \"%s\" is wrongly annotated: when declaring an type with enum and JSONKeyPath, you must have the same number of items in the 'enumValues' and 'JSONValues' annotations - please fix it"%[@name, @entity_name]) if !@json_key_path.empty? and !@enum_values.empty? and @enum_values.size != @json_values.size
- end
-
- end
-
- end
- end
-end
diff --git a/lib/gyro/xcdatamodel/parser/entity.rb b/lib/gyro/xcdatamodel/parser/entity.rb
deleted file mode 100644
index 2166d2b..0000000
--- a/lib/gyro/xcdatamodel/parser/entity.rb
+++ /dev/null
@@ -1,229 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module XCDataModel
- module Parser
- class Entity
-
- attr_accessor :name, :parent, :abstract, :attributes, :relationships, :identity_attribute, :comment
- alias_method :abstract?, :abstract
-
- def initialize(entity_xml)
- @name = entity_xml.xpath('@name').to_s
- @parent = entity_xml.xpath('@parentEntity').to_s
- @abstract = entity_xml.xpath('@isAbstract').to_s == 'YES' ? true : false
- @clean = false
- @identity_attribute = entity_xml.xpath(USERINFO_VALUE%['identityAttribute']).to_s
- @comment = entity_xml.xpath(USERINFO_VALUE%['comment']).to_s
- @attributes = Hash.new
- @relationships = Hash.new
- load_entity(entity_xml)
- end
-
- def to_s
- str = "\nEntity => #{@name}\n"
- @attributes.each do |_, attribute|
- str += attribute.to_s
- end
- @relationships.each do |_, relationship|
- str += relationship.to_s
- end
- str
- end
-
- def used_as_list_by_other?(entities)
- entities.each do |_, entity|
- entity.relationships.each do |_, relationship|
- return true if relationship.inverse_type == @name and relationship.type == :to_many
- end
- end
- false
- end
-
- def has_list_attributes?(inverse = false)
- @relationships.each do |_, relationship|
- return true if relationship.type == :to_many and (!inverse ? !relationship.inverse? : true)
- end
- false
- end
-
- def has_no_inverse_relationship?
- @relationships.each do |_, relationship|
- return true unless relationship.inverse?
- end
- false
- end
-
- def has_ignored?
- @attributes.each do |_, attribute|
- return true if attribute.realm_ignored?
- end
- @relationships.each do |_, relationship|
- return true if relationship.realm_ignored?
- end
- false
- end
-
- def has_primary_key?
- !@identity_attribute.empty?
- end
-
- def has_required?
- @attributes.each do |_, attribute|
- return true if is_required?(attribute)
- end
- false
- end
-
- def is_required?(attribute)
- unless attribute.optional?
- return true unless self.has_primary_key?
- return true if self.has_primary_key? && !attribute.name.eql?(identity_attribute)
- end
- false
- end
-
- def has_default_value?(attribute = @identity_attribute)
- attribute.name != @identity_attribute
- end
-
- def has_indexed_attributes?
- @attributes.each do |_, attribute|
- return true if attribute.indexed?
- end
- false
- end
-
- def has_json_key_path?
- @attributes.each do |_, attribute|
- return true unless attribute.json_key_path.empty?
- end
- @relationships.each do |_, relationship|
- return true if !relationship.inverse? and !relationship.json_key_path.empty?
- end
- false
- end
-
- def has_enum_attributes?
- @attributes.each do |_, attribute|
- return true unless attribute.enum_type.empty?
- end
- false
- end
-
- def transformers
- transformers = Set.new
- @attributes.each do |_, attribute|
- transformers.add attribute.transformer unless attribute.transformer.empty?
- end
- transformers
- end
-
- def has_custom_transformers?
- @attributes.each do |_, attribute|
- return true unless attribute.transformer.empty?
- end
- false
- end
-
- def need_transformer?
- has_enum_attributes? || has_bool_attributes? || has_custom_transformers? || has_date_attribute?
- end
-
- def has_bool_attributes?
- has_bool_attributes = false
- @attributes.each do |_, attribute|
- has_bool_attributes = true if attribute.type == :boolean
- end
- has_bool_attributes
- end
-
- def has_number_attributes?
- has_number_attributes = false
- @attributes.each do |_, attribute|
- if attribute.enum_type.empty?
- case attribute.type
- when :integer_16 then
- has_number_attributes = true
- when :integer_32 then
- has_number_attributes = true
- when :integer_64 then
- has_number_attributes = true
- when :decimal then
- has_number_attributes = true
- when :double then
- has_number_attributes = true
- when :float then
- has_number_attributes = true
- else
- has_number_attributes = false
- end
- break if has_number_attributes
- end
- end
- has_number_attributes
- end
-
- def has_date_attribute?
- @attributes.each do |_, attribute|
- return true if attribute.type == :date
- end
- false
- end
-
- def has_list_relationship?
- @relationships.each do |_, relationship|
- return true unless relationship.destination.empty?
- end
- false
- end
-
- def has_only_inverse?
- nb_inverses = 0
- @relationships.each do |_, relationship|
- nb_inverses+=1 if relationship.inverse?
- end
- nb_inverses==@relationships.size
- end
-
- private ################################################################
-
- def load_entity(entity_xml)
- load_attributes(entity_xml)
- load_relationships(entity_xml)
- end
-
- def load_attributes(entity_xml)
- entity_xml.xpath('attribute').each do |node|
- attribute = Attribute.new(node, @name)
- if attribute.type != 'Transformable'
- @attributes[attribute.name] = attribute
- end
- end
- end
-
- def load_relationships(entity_xml)
- entity_xml.xpath('relationship').each do |node|
- relationship = Relationship.new(node, @name)
- @relationships[relationship.name] = relationship
- end
- end
- end
-
- end
- end
-end
diff --git a/lib/gyro/xcdatamodel/parser/relationship.rb b/lib/gyro/xcdatamodel/parser/relationship.rb
deleted file mode 100644
index b6c3566..0000000
--- a/lib/gyro/xcdatamodel/parser/relationship.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module XCDataModel
- module Parser
-
- class Relationship
-
- attr_accessor :entity_name, :name, :type, :optional, :deletion_rule, :inverse_name, :inverse_type, :json_key_path, :support_annotation
- attr_accessor :realm_ignored
- attr_accessor :destination
-
- alias_method :realm_ignored?, :realm_ignored
-
- def initialize(relationship_xml, entity_name)
- @entity_name = entity_name
- @name = relationship_xml.xpath('@name').to_s
- @optional = relationship_xml.xpath('@optional').to_s
- @deletion_rule = relationship_xml.xpath('@deletionRule').to_s
- @inverse_name = relationship_xml.xpath('@inverseName').to_s
- @inverse_type = relationship_xml.xpath('@destinationEntity').to_s
- @json_key_path = relationship_xml.xpath(USERINFO_VALUE%['JSONKeyPath']).to_s
- @realm_ignored = relationship_xml.xpath(USERINFO_VALUE%['realmIgnored']).to_s.empty? ? false : true
- @support_annotation = relationship_xml.xpath(USERINFO_VALUE%['supportAnnotation']).to_s
- load_type(relationship_xml)
- @destination = relationship_xml.xpath(USERINFO_VALUE%['destination']).to_s
- search_for_error
- end
-
- def to_s
- "\tRelationship => name=#{@name} | type=#{@type} | optional=#{@optional} | deletion_rule=#{@deletion_rule}\n"
- end
-
- def inverse?
- @name.end_with?('_')
- end
-
- private ################################################################
-
- def load_type(relationship_xml)
- max_count = relationship_xml.xpath('@maxCount').to_s
- @type = (!max_count.nil? and max_count == '1') ? :to_one : :to_many
- end
-
- def search_for_error
- Gyro::Error::raise("The relationship \"%s\" from \"%s\" is wrong - please fix it"%[name, entity_name]) if inverse_type.empty? && destination.empty?
- Gyro::Error::raise("The relationship \"%s\" from \"%s\" is wrong - please set a 'No Value' relationship as 'To Many'"%[name, entity_name]) if !destination.empty? && type != :to_many
- end
-
- end
-
- end
- end
-end
diff --git a/lib/gyro/xcdatamodel/parser/xcdatamodel.rb b/lib/gyro/xcdatamodel/parser/xcdatamodel.rb
deleted file mode 100644
index ecc94f5..0000000
--- a/lib/gyro/xcdatamodel/parser/xcdatamodel.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-=begin
-Copyright 2016 - Niji
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-=end
-
-module Gyro
- module XCDataModel
- module Parser
- USERINFO_VALUE = "userInfo/entry[@key='%s']/@value"
-
- class XCDataModel
-
- attr_accessor :entities
-
- def initialize(xcdatamodel_dir)
- contents_file = File.join(xcdatamodel_dir, 'contents')
- Gyro::Error::raise('Unable to find contents of xcdatamodel dir') unless File.exist?(contents_file)
- @entities = Hash.new
- file = File.open(contents_file)
- document_xml = Nokogiri::XML(file).remove_namespaces!
- file.close
- load_entities(document_xml)
- end
-
- def to_s
- str = String.new
- @entities.each do |_, value|
- str += value.to_s
- end
- str
- end
-
- private
-
- def load_entities(document_xml)
- document_xml.xpath('//entity').each do |node|
- entity = Entity.new(node)
- @entities[entity.name] = entity
- end
- end
- end
- end
- end
-end
diff --git a/lib/templates/android/README.md b/lib/templates/android/README.md
new file mode 100644
index 0000000..a4f159e
--- /dev/null
+++ b/lib/templates/android/README.md
@@ -0,0 +1,164 @@
+# Android Template Information
+
+| Name | Description |
+| --------- | ----------------- |
+| Folder name | templates/android |
+| Invocation example | `gyro -m -t android …` |
+| Language | Java |
+
+If you want to use this template you need to work with `Realm`.
+
+# Caracteristics
+
+In this template you have additional parameters to inject constants :
+
+- package (ex : **com.gyro.model.realm**)
+- use_wrappers (define if you want to use wrapper types (ex Boolean) instead of primitive types (ex boolean) when you attribute is **optional** inside xcdatamodel)
+- support_annotations (you can define annotation for properties ex : **@android.support.annotation.IntRange(from=0,to=255)** or **@android.support.annotation.Nullable**)
+
+# Usage
+
+
+Package exemple :
+
+```bash
+gyro -m -t android -o