Skip to content

Commit

Permalink
Merge pull request #4 from k06a/feature/imperative
Browse files Browse the repository at this point in the history
Add DIImperative plugin and refactored some cleanups
  • Loading branch information
k06a committed May 23, 2016
2 parents b29cc1f + 2f7c19c commit 6b43173
Show file tree
Hide file tree
Showing 73 changed files with 2,011 additions and 588 deletions.
7 changes: 3 additions & 4 deletions DeluxeInjection.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "DeluxeInjection"
s.version = "0.1.6"
s.version = "0.2.0"
s.summary = "Simplest Objective-C Dependency Injection (DI:syringe:) implementation ever"

s.description = <<-DESC
Expand All @@ -14,9 +14,8 @@ Pod::Spec.new do |s|
s.source = { :git => "https://github.com/k06a/DeluxeInjection.git", :tag => s.version.to_s }
s.social_media_url = 'https://twitter.com/k06a'

s.platform = :ios, '7.0'
s.requires_arc = true
s.ios.deployment_target = '8.0'

s.source_files = 'Pod/Classes/**/*'
s.source_files = 'DeluxeInjection/Classes/**/*'
s.dependency 'RuntimeRoutines'
end
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
File renamed without changes.
113 changes: 113 additions & 0 deletions DeluxeInjection/Classes/DIImperative.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//
// DIImperative.h
// DeluxeInjection
//
// Copyright (c) 2016 Anton Bukov <k06aaa@gmail.com>
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// 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.
//

#import "DIDeluxeInjection.h"

/**
* Block to filter properties to be injected
*
* @param targetClass Class to be injected
* @param getter Selector of getter method
* @param propertyName Property name to be injected
* @param propertyClass Class of property to be injected, at least \c NSObject
* @param propertyProtocols Set of property protocols including all superprotocols
*
* @return \c YES to inject property, \c NO to skip injection
*/
typedef BOOL (^DIPropertyFilterBlock)(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet<Protocol *> *propertyProtocols);

//

@interface DIDeluxeInjectionImperativeInjector : NSObject

/**
* Set value to be injected
*
* @param valueObject Value to be injected
*/
- (instancetype)valueObject:(id)valueObject;

/**
* Set value block to be injected
*
* @param valueBlock Value block to be injected
*/
- (instancetype)valueBlock:(DIGetter)valueBlock;

/**
* Set filter class for conditional injection
*
* @param filterClass Class which sublasses properties can be injected
*/
- (instancetype)filterClass:(Class)filterClass;

/**
* Set filter block for conditional injection
*
* @param filterBlock Block which define what properties can be injected
*/
- (instancetype)filterBlock:(DIPropertyFilterBlock)filterBlock;

@end

//

@interface DIDeluxeInjectionImperative : NSObject

/**
* Inject all properties of class \c klass with \c value
*
* @param klass Property class
*
* @return Injector object to define value and options to be injected
*/
- (DIDeluxeInjectionImperativeInjector *)injectByPropertyClass:(Class)klass;

/**
* Inject all properties conforming \c protocol with \c value
*
* @param protocol Protocol of property
*
* @return Injector object to define value and options to be injected
*/
- (DIDeluxeInjectionImperativeInjector *)injectByPropertyProtocol:(Protocol *)protocol;

@end

//

/**
* Block to apply imperative injections
*
* @param lets Object to apply imperative injections
*/
typedef void (^DIImperativeBlock)(DIDeluxeInjectionImperative *lets);

@interface DeluxeInjection (DIImperative)

/**
* Method to apply imperative injections inside block. All properties
* marked with \c DIInject protocol should be injected at least once.
* Properties who will be injected several times will be logged to \c NSLog().
*
* @param block Block to apply imperative injections
*/
+ (void)imperative:(DIImperativeBlock)block;

@end
199 changes: 199 additions & 0 deletions DeluxeInjection/Classes/DIImperative.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
//
// DIImperative.m
// DeluxeInjection
//
// Copyright (c) 2016 Anton Bukov <k06aaa@gmail.com>
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// 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.
//

#import <assert.h>

#import "DIDeluxeInjectionPlugin.h"
#import "DIInject.h"
#import "DIImperative.h"

@interface DIPropertyHolder : NSObject

@property (assign, nonatomic) Class targetClass;
@property (assign, nonatomic) Class klass;
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSSet<Protocol *> *protocols;
@property (assign, nonatomic) SEL getter;
@property (assign, nonatomic) BOOL wasInjected;

@end

@implementation DIPropertyHolder

@end

//

@interface DIDeluxeInjectionImperativeInjector ()

@property (weak, nonatomic) DIDeluxeInjectionImperative *lets;
@property (assign, nonatomic) Class savedClass;
@property (assign, nonatomic) Protocol *savedProtocol;
@property (copy, nonatomic) DIGetter savedValueBlock;
@property (copy, nonatomic) DIPropertyFilterBlock savedFilterBlock;

@end

@interface DIDeluxeInjectionImperative ()

@property (strong, nonatomic) NSMutableDictionary<id,NSMutableArray<DIPropertyHolder *> *> *byClass;
@property (strong, nonatomic) NSMutableDictionary<NSValue *,NSMutableArray<DIPropertyHolder *> *> *byProtocol;

@end

//

@implementation DIDeluxeInjectionImperativeInjector

- (instancetype)valueObject:(id)valueObject {
return [self valueBlock:DIGetterIfIvarIsNil(^id (id target) {
return valueObject;
})];
}

- (instancetype)valueBlock:(DIGetter)valueBlock {
NSAssert(self.savedValueBlock == nil, @"You should call valueObject: or valueBlock: only once");
self.savedValueBlock = valueBlock;
return self;
}

- (instancetype)filterClass:(Class)filterClass {
return [self filterBlock:^BOOL(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet<Protocol *> *propertyProtocols) {
return [targetClass isSubclassOfClass:filterClass];
}];
}

- (instancetype)filterBlock:(DIPropertyFilterBlock)filterBlock {
NSAssert(self.savedFilterBlock == nil, @"You should call filterClass: or filterBlock: only once");
self.savedFilterBlock = filterBlock;
return self;
}

- (void)dealloc {
NSAssert(!!self.savedClass != !!self.savedProtocol, @"You should not define both class and protocol to inject");
NSAssert(self.savedValueBlock, @"You should call valueObject: or valueBlock: once");

NSValue *key = [NSValue valueWithPointer:(__bridge void *)(self.savedProtocol)];
NSArray *holders = self.savedClass ? self.lets.byClass[self.savedClass] : self.lets.byProtocol[key];

for (DIPropertyHolder *holder in holders) {
if (self.savedFilterBlock && !self.savedFilterBlock(holder.targetClass, holder.getter, holder.name, holder.klass, holder.protocols)) {
continue;
}
if (holder.wasInjected) {
NSLog(@"Warning: Reinjecting property [%@ %@]", holder.targetClass, NSStringFromSelector(holder.getter));
}
[DeluxeInjection inject:holder.targetClass getter:holder.getter getterBlock:self.savedValueBlock];
holder.wasInjected = YES;
}
}

@end

//

@implementation DIDeluxeInjectionImperative

- (instancetype)init
{
self = [super init];
if (self) {
_byClass = [NSMutableDictionary dictionary];
_byProtocol = [NSMutableDictionary dictionary];

[DeluxeInjection inject:^NSArray * (Class targetClass, SEL getter, SEL setter, NSString *propertyName, Class propertyClass, NSSet<Protocol *> *propertyProtocols) {

DIPropertyHolder *holder = [[DIPropertyHolder alloc] init];
holder.targetClass = targetClass;
holder.getter = getter;
holder.name = propertyName;
holder.klass = propertyClass;
holder.protocols = propertyProtocols;

if (propertyClass) {
if (_byClass[(id)propertyClass] == nil) {
_byClass[(id)propertyClass] = [NSMutableArray array];
}
[_byClass[(id)propertyClass] addObject:holder];
}

for (Protocol *protocol in propertyProtocols) {
NSValue *key = [NSValue valueWithPointer:(__bridge void *)(protocol)];
if (_byProtocol[key] == nil) {
_byProtocol[key] = [NSMutableArray array];
}
[_byProtocol[key] addObject:holder];
}

return @[[DeluxeInjection doNotInject],
[DeluxeInjection doNotInject]];
} conformingProtocol:@protocol(DIInject)];
}
return self;
}

- (DIDeluxeInjectionImperativeInjector *)injectByPropertyClass:(Class)klass {
DIDeluxeInjectionImperativeInjector *injector = [[DIDeluxeInjectionImperativeInjector alloc] init];
injector.lets = self;
injector.savedClass = klass;
return injector;
}

- (DIDeluxeInjectionImperativeInjector *)injectByPropertyProtocol:(Protocol *)protocol {
DIDeluxeInjectionImperativeInjector *injector = [[DIDeluxeInjectionImperativeInjector alloc] init];
injector.lets = self;
injector.savedProtocol = protocol;
return injector;
}

- (void)checkAllInjected {
for (Class klass in self.byClass) {
for (DIPropertyHolder *holder in self.byClass[klass]) {
NSString *problemDescription = [NSString stringWithFormat:@"Missing injection by class to [%@ %@]", holder.targetClass, NSStringFromSelector(holder.getter)];
NSAssert(holder.wasInjected, problemDescription);
if (!holder.wasInjected) {
NSLog(@"Warning: %@", problemDescription);
}
}
}

for (NSValue *key in self.byProtocol) {
for (DIPropertyHolder *holder in self.byProtocol[key]) {
NSString *problemDescription = [NSString stringWithFormat:@"Missing injection by protocol to [%@ %@]", holder.targetClass, NSStringFromSelector(holder.getter)];
NSAssert(holder.wasInjected, problemDescription);
if (!holder.wasInjected) {
NSLog(@"Warning: %@", problemDescription);
}
}
}
}

@end

//

@implementation DeluxeInjection (DIImperative)

+ (void)imperative:(void (^)(DIDeluxeInjectionImperative *lets))block; {
DIDeluxeInjectionImperative *di = [[DIDeluxeInjectionImperative alloc] init];
block(di);
[di checkAllInjected];
}

@end
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
#import "DIInject.h"
#import "DILazy.h"
#import "DIDefaults.h"
#import "DIImperative.h"

#endif // __DELUXEINJECTION__
Loading

0 comments on commit 6b43173

Please sign in to comment.