motionscan 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ .repl_history
2
+ build
3
+ .build
4
+ libPods.a
5
+ vendor/Pods/Pods.xcodeproj
6
+ tags
7
+ resources/*.nib
8
+ resources/*.momd
9
+ resources/*.storyboardc
10
+ .DS_Store
11
+ nbproject
12
+ .redcar
13
+ #*#
14
+ *~
15
+ *.sw[po]
16
+ .eprj
17
+ pkg/*
18
+ vendor/Pods
19
+ vendor/Podfile.lock
20
+ Gemfile.lock
21
+ wildcard_dev.mobileprovision
22
+ Rakefile
23
+ app/motionscan_config.rb
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 by Joffrey Jaffeux
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/motionscan/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "motionscan"
6
+ s.version = Motionscan::VERSION
7
+ s.authors = ["Joffrey Jaffeux"]
8
+ s.email = ["j.jaffeux@gmail.com"]
9
+ s.homepage = "https://github.com/jjaffeux/Motionscan"
10
+ s.summary = "A RubyMotion Moodstocks SDK image recognition wrapper"
11
+ s.description = "A RubyMotion Moodstocks SDK image recognition wrapper"
12
+
13
+ s.files = `git ls-files`.split($\)
14
+ s.require_paths = ["lib"]
15
+
16
+ s.add_dependency "motion-cocoapods", ">= 1.2.1"
17
+ s.add_development_dependency 'rake'
18
+ end
data/README.md ADDED
@@ -0,0 +1,174 @@
1
+ <!>Not production ready<!>
2
+
3
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/jjaffeux/Motionscan)
4
+
5
+
6
+ # Description
7
+
8
+ Motionscan is a RubyMotion wrapper for the Moodstocks SDK. Moodstocks provide an incredibly fast and easy tool to add image recognition to your app.
9
+
10
+ Watch the video to see what you will be able to build in few minutes : http://cl.ly/0Q3K163y3b12
11
+
12
+
13
+ # Usage
14
+ - Create an account on moodstocks.com
15
+ - Use Moodstocks image uploader to add images to your account
16
+ - Complete your application Rakefile with RakefileExample (most notably pod and credentials)
17
+ - Look at /app to use it in your application
18
+ - Not yet pushed to rubygems, you have to build the gem and link to it from your Gemfile
19
+
20
+ If you want build this application you need to upload images with json attached to images, for example :
21
+ http://cl.ly/image/3l331E0K0L2P
22
+
23
+ # Documentation
24
+ ## Motionscan::Sync
25
+
26
+ Moodstocks is fast because the image processor is fast but also because it allows you to cache image signature, the Sync class allows you to decide when and how you want to sync the device cache.
27
+
28
+ -init
29
+ ```ruby
30
+ sync = Motionscan::Sync.init
31
+ ```
32
+
33
+ -startWithStatus(syncStarted, success:syncCompleted, error:syncFailed, progress:syncProgressed)
34
+ ```ruby
35
+ sync.startWithStatus(
36
+ lambda {
37
+ # sync started
38
+ },
39
+ success:lambda {|result|
40
+ # sync finished
41
+ # Motionscan::Result instance
42
+ },
43
+ error:lambda {|error|
44
+ # an error occured while syncing
45
+ # NSError
46
+ },
47
+ progress:lambda {|progress, total|
48
+ # progress : current number of cached items
49
+ # total : number of items to cache
50
+ }
51
+ )
52
+ ```
53
+
54
+ ## Motionscan::Scanner
55
+
56
+
57
+ -initWithFrame(frame)
58
+ ```ruby
59
+ scanner = Motionscan::Scanner.initWithFrame([[0,0],[320,200]])
60
+ ```
61
+
62
+ -frame=(frame)
63
+ ```ruby
64
+ scanner.frame = [[0,0],[320,200]]
65
+ ```
66
+
67
+ -displayScannerInView(view)
68
+ ```ruby
69
+ scanner.displayScannerInView(self.view)
70
+ ```
71
+
72
+ -startWithStatus(scanStarted, success:scanCompleted, error:scanError, notFound:scanNotFound)
73
+ ```ruby
74
+ scanner.startWithStatus(
75
+ lambda {
76
+ # scan started
77
+ # will fire once at the beginning
78
+ # from this point everything captured
79
+ # by the video will be scanned
80
+ },
81
+ success:lambda {|result|
82
+ # scan found and image in the cache
83
+ # Motionscan::Result instance
84
+ },
85
+ error:lambda {|error|
86
+ # scan error
87
+ # NSError
88
+ },
89
+ notFound:lambda {
90
+ # scan didn't find the image in the cache
91
+ # you shouldn't do anything expensive here
92
+ # as it will fire very frequently
93
+ }
94
+ )
95
+ ```
96
+
97
+
98
+ -pause
99
+
100
+ If you wish to pause the scanner, and prevent current result to change you can use this method. Use scanner.resume to resume scanning process.
101
+ ```ruby
102
+ scanner.pause
103
+ ```
104
+
105
+ -stop
106
+
107
+ Call this method to terminate the scanner, usually in viewWillDisappear(animated). /!\ You have to call it at some point or pushing a new UIViewController and going back to this one, will crash the app.
108
+ ```ruby
109
+ scanner.stop
110
+ ```
111
+
112
+ -resume
113
+
114
+ ```ruby
115
+ scanner.resume
116
+ ```
117
+
118
+ -searchWithStatus(searchStarted, success:searchCompleted, error:searchError, notFound:searchNotFound)
119
+
120
+ If an image is not in the cache, you can trigger an API call to search it on your account.
121
+
122
+ ```ruby
123
+ scanner.searchWithStatus(
124
+ lambda {
125
+ #search in remote started
126
+ },
127
+ success:lambda {|result|
128
+ #search found and image in remote
129
+ },
130
+ error:lambda {|error|
131
+ #search error
132
+ },
133
+ notFound:lambda {
134
+ #search didn't find the image in remote
135
+ }
136
+ )
137
+ ```
138
+
139
+ ## Motionscan::Result
140
+
141
+ Result is a simple wrapper arround MSResult, every methods that should return a result will give an instance of Motionscan::Result.
142
+
143
+ -imageId
144
+
145
+ Returns the imageId of the scanned image.
146
+
147
+ ```ruby
148
+ result.imageId
149
+ ```
150
+
151
+ -msSDKResult
152
+
153
+ Let you access the raw MSResult
154
+
155
+ ```ruby
156
+ result.msSDKResult
157
+ ```
158
+
159
+ -data
160
+
161
+ ```ruby
162
+ result.data
163
+ ```
164
+
165
+ Returns the string or the hash (json is converted to a hash) saved with the image. For example if the following json is attached to your image :
166
+ ```ruby
167
+ {"type":"businessCat", "name":"Carl"}
168
+ ```
169
+
170
+ You will get the following ruby hash :
171
+ ```ruby
172
+ result.data
173
+ # {type:"businessCat", name:"Carl"}
174
+ ```
data/RakefileExample ADDED
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+ $:.unshift("/Library/RubyMotion/lib")
3
+ require 'motion/project'
4
+ require "bundler/gem_tasks"
5
+ require "bundler/setup"
6
+ Bundler.require :default
7
+
8
+ $:.unshift("./lib/")
9
+ require './lib/motionscan'
10
+
11
+ Motion::Project::App.setup do |app|
12
+ app.name = 'Motionscan'
13
+
14
+ app.motionscan.api_key = "xxx"
15
+ app.motionscan.api_secret = "xxx"
16
+
17
+ app.pods do
18
+ pod 'Moodstocks-iOS-SDK', '~> 3.5'
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ class AppDelegate
2
+
3
+ attr_accessor :window
4
+
5
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
6
+ controller = RootViewController.alloc.init
7
+ navigationController = UINavigationController.alloc.initWithRootViewController(controller)
8
+ self.window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
9
+ self.window.rootViewController = navigationController
10
+ self.window.makeKeyAndVisible
11
+
12
+ true
13
+ end
14
+ end
@@ -0,0 +1,57 @@
1
+ class RootViewController < UIViewController
2
+
3
+ attr_accessor :counterLabel
4
+
5
+ def loadView
6
+ super
7
+ self.counterLabel = UILabel.alloc.initWithFrame([[60,80],[200,40]])
8
+ self.counterLabel.textAlignment = UITextAlignmentCenter
9
+ self.counterLabel.backgroundColor = UIColor.darkGrayColor
10
+ self.counterLabel.font = UIFont.boldSystemFontOfSize(20)
11
+ self.counterLabel.layer.cornerRadius = 5.0
12
+ self.counterLabel.text = "Sync will start"
13
+ self.view.addSubview(self.counterLabel)
14
+ end
15
+
16
+ def viewDidLoad
17
+ super
18
+ self.title = "Root"
19
+ self.view.backgroundColor = UIColor.whiteColor
20
+
21
+ @sync = Motionscan::Sync.init
22
+ @sync.startWithStatus(
23
+ lambda {
24
+ NSLog("sync started")
25
+ },
26
+ success:lambda {|result|
27
+ self.counterLabel.removeFromSuperview
28
+ addScannerViewControllerButton
29
+ },
30
+ error:lambda {|error|
31
+ p error
32
+ },
33
+ progress:lambda {|progress, total|
34
+ self.counterLabel.text = "#{progress} / #{total}"
35
+ }
36
+ )
37
+ end
38
+
39
+ private
40
+
41
+ def addScannerViewControllerButton
42
+ scanButton = UIButton.buttonWithType(UIButtonTypeCustom)
43
+ scanButton.frame = [[40,140],[240,40]]
44
+ scanButton.setTitle("Launch Scanner", forState:UIControlStateNormal)
45
+ scanButton.backgroundColor = UIColor.darkGrayColor
46
+ scanButton.addTarget(self,
47
+ action: :pushScannerViewController,
48
+ forControlEvents:UIControlEventTouchUpInside)
49
+ self.view.addSubview(scanButton)
50
+ end
51
+
52
+ def pushScannerViewController
53
+ scannerController = ScannerViewController.alloc.init
54
+ self.navigationController.pushViewController(scannerController, animated:true)
55
+ end
56
+
57
+ end
@@ -0,0 +1,33 @@
1
+ class ScannerViewController < UIViewController
2
+
3
+ def init
4
+ super
5
+ @overlay = UILabel.alloc.initWithFrame([[10,10],[300,40]])
6
+ @overlay.textAlignment = UITextAlignmentCenter
7
+ @overlay.alpha = 0.8
8
+ @overlay.backgroundColor = UIColor.darkGrayColor
9
+ @overlay.layer.zPosition = 2
10
+ self.view.addSubview(@overlay)
11
+ self
12
+ end
13
+
14
+ def viewDidAppear(animated)
15
+ @scanner = Motionscan::Scanner.initWithFrame(self.view.bounds)
16
+ @scanner.displayScannerInView(self.view)
17
+ @scanner.startWithStatus(
18
+ lambda {
19
+ @overlay.text = "Will start scanning"
20
+ },
21
+ success:lambda {|result|
22
+ @overlay.text = "#{result.data[:type]}"
23
+ },
24
+ error:lambda {|error|},
25
+ notFound:lambda {}
26
+ )
27
+ end
28
+
29
+ def viewWillDisappear(animated)
30
+ @scanner.stop
31
+ end
32
+
33
+ end
@@ -0,0 +1,78 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "This file must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+ class MotionscanConfig
6
+ attr_accessor :api_key
7
+ attr_accessor :api_secret
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ end
12
+
13
+ def api_secret=(api_secret)
14
+ @api_secret = api_secret
15
+ create_code
16
+ end
17
+
18
+ private
19
+
20
+ def create_code
21
+ unless @api_key && @api_secret
22
+ raise "Need to configure `app.motionscan.api_key' and `app.motionscan.api_secret' variables"
23
+ end
24
+
25
+ code = <<EOF
26
+ # Moodstocks SDK launcher
27
+ # This file is automatically generated. Do not edit.
28
+ if MSDeviceCompatibleWithSDK()
29
+ scanner = MSScanner.sharedInstance
30
+ error_ptr = Pointer.new(:object)
31
+ unless scanner.openWithKey("#{@api_key}", secret:"#{@api_secret}", error:error_ptr)
32
+ errorCode = error_ptr[0].code
33
+ if errorCode == MS_CREDMISMATCH
34
+ NSLog("[MOODSTOCKS SDK] There is a problem with your key/secret pair : see https://github.com/Moodstocks/moodstocks-sdk/blob/master/sample/iphone/demo/Demo/MSAppDelegate.m#L65")
35
+ NSException.exceptionWithName("MSScannerException", reason:"Credentials mismatch", userInfo:nil).raise
36
+ else
37
+ errorString = NSString.stringWithCString(ms_errmsg(errorCode), encoding:NSUTF8StringEncoding)
38
+ NSLog("[MOODSTOCKS SDK] Scanner open error code :" + errorString)
39
+ end
40
+ end
41
+ NSLog("[MOODSTOCKS SDK] Scanner initialized.")
42
+ else
43
+ NSLog("[MOODSTOCKS SDK] Your device is not compatible with the Moodstocks SDK.")
44
+ end
45
+ EOF
46
+
47
+ motionscan_file = './app/motionscan_config.rb'
48
+ create_stub(motionscan_file, code)
49
+ add_file(motionscan_file)
50
+ end
51
+
52
+ def create_stub(path, code)
53
+ if !File.exist?(path) or File.read(path) != code
54
+ File.open(path, 'w') { |io| io.write(code) }
55
+ end
56
+ end
57
+
58
+ def add_file(path)
59
+ files = @config.files.flatten
60
+ @config.files << path unless files.find { |x| File.expand_path(x) == File.expand_path(path) }
61
+ end
62
+ end
63
+
64
+ module Motion
65
+ module Project
66
+ class Config
67
+
68
+ variable :motionscan
69
+
70
+ def motionscan
71
+ @motionscan ||= MotionscanConfig.new(self)
72
+ yield @motionscan if block_given?
73
+ @motionscan
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,18 @@
1
+ module Motionscan
2
+
3
+ module Flash
4
+
5
+ def flash
6
+ flashView = UIView.alloc.initWithFrame(@view.bounds)
7
+ flashView.backgroundColor = UIColor.whiteColor
8
+ @view.addSubview(flashView)
9
+
10
+ UIView.animateWithDuration(0.4,
11
+ animations:lambda { flashView.alpha = 0.0 },
12
+ completion:lambda { |finished| flashView.removeFromSuperview }
13
+ )
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,39 @@
1
+ module Motionscan
2
+
3
+ class Result
4
+
5
+ attr_reader :msSDKResult
6
+
7
+ def initialize(msSDKResult)
8
+ @msSDKResult = msSDKResult
9
+ end
10
+
11
+ def imageId
12
+ @msSDKResult.getValue
13
+ end
14
+
15
+ def data
16
+ error_ptr = Pointer.new(:object)
17
+
18
+ options = NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves | NSJSONReadingAllowFragments
19
+ json = NSJSONSerialization.JSONObjectWithData(base64URLString,
20
+ options:options,
21
+ error:error_ptr)
22
+
23
+ error = error_ptr[0]
24
+ error.nil? ? json : decodeImageBase64URLString
25
+ end
26
+
27
+ private
28
+
29
+ def decodeImageBase64URLString
30
+ NSString.alloc.initWithData(base64URLString, encoding:NSUTF8StringEncoding)
31
+ end
32
+
33
+ def base64URLString
34
+ MSResult.dataFromBase64URLString(@msSDKResult.getValue)
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,109 @@
1
+ module Motionscan
2
+ class Scanner
3
+ include Flash
4
+
5
+ attr_accessor :flashEnabled
6
+ attr_accessor :frame
7
+ attr_reader :session
8
+ attr_reader :view
9
+
10
+ def self.initWithFrame(frame)
11
+ instance = allocate
12
+ instance.scannerInitializer(frame)
13
+ instance
14
+ end
15
+
16
+ def scannerInitializer(frame)
17
+ @frame = frame
18
+ @flashEnabled = true
19
+ @view = UIView.alloc.initWithFrame(frame)
20
+ end
21
+
22
+ def frame=(frame)
23
+ @view.frame = frame
24
+ end
25
+
26
+ def displayScannerInView(view)
27
+ @session = MSScannerSession.alloc.initWithScanner(MSScanner.sharedInstance)
28
+ @session.scanOptions = MS_RESULT_TYPE_IMAGE
29
+ @session.delegate = self
30
+
31
+ @view.backgroundColor = UIColor.blackColor
32
+ @view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
33
+ @view.autoresizesSubviews = true
34
+ view.addSubview(@view)
35
+ end
36
+
37
+ # scanner session flow
38
+ def startWithStatus(scanStarted, success:scanCompleted,
39
+ error:scanError, notFound:scanNotFound)
40
+ scanStarted.call
41
+ @scanCompleted = scanCompleted
42
+ @scanError = scanError
43
+ @scanNotFound = scanNotFound
44
+
45
+ viewLayer = @view.layer
46
+ viewLayer.setMasksToBounds(true)
47
+
48
+ captureLayer = @session.previewLayer
49
+ captureLayer.setFrame(@view.bounds)
50
+
51
+ viewLayer.insertSublayer(captureLayer, below:viewLayer.sublayers)
52
+
53
+ @session.startCapture
54
+ end
55
+
56
+ def stop
57
+ @session.stopCapture
58
+ @session.cancel
59
+ @view.removeFromSuperview
60
+ end
61
+
62
+ def resume
63
+ @session.resume
64
+ end
65
+
66
+ def pause
67
+ @session.pause
68
+ end
69
+
70
+ def searchWithStatus(searchStarted, success:searchCompleted,error:searchError,notFound:searchNotFound)
71
+ @searchStarted = searchStarted
72
+ @searchCompleted = searchCompleted
73
+ @searchError = searchError
74
+ @searchNotFound = searchNotFound
75
+
76
+ self.flash if @flashEnabled
77
+ @session.snap
78
+ @session.pause
79
+ end
80
+
81
+ private
82
+
83
+ # delegates
84
+ def session(session, didScan:result)
85
+ Dispatch::Queue.main.async {
86
+ result.nil? ? @scanNotFound.call : @scanCompleted.call(Result.new(result))
87
+ }
88
+ end
89
+
90
+ def session(session, failedToScan:error)
91
+ Dispatch::Queue.main.async { @scanError.call(error) }
92
+ end
93
+
94
+ def scannerWillSearch(scanner)
95
+ Dispatch::Queue.main.async { @searchStarted.call }
96
+ end
97
+
98
+ def scanner(scanner, didSearchWithResult:result)
99
+ Dispatch::Queue.main.async {
100
+ result.nil? ? @searchNotFound.call : @searchCompleted.call(Result.new(result))
101
+ }
102
+ end
103
+
104
+ def scanner(scanner, failedToSearchWithError:error)
105
+ Dispatch::Queue.main.async { @searchError.call(error) }
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,49 @@
1
+ module Motionscan
2
+
3
+ class Sync
4
+
5
+ attr_reader :scanner
6
+
7
+ def self.init
8
+ instance = allocate
9
+ instance
10
+ end
11
+
12
+ def startWithStatus(syncStarted, success:syncCompleted,
13
+ error:syncFailed, progress:syncProgressed)
14
+ @syncStarted = syncStarted
15
+ @syncCompleted = syncCompleted
16
+ @syncFailed = syncFailed
17
+ @syncProgressed = syncProgressed
18
+
19
+ @scanner = MSScanner.sharedInstance
20
+ @scanner.syncWithDelegate(self) unless @scanner.isSyncing
21
+ end
22
+
23
+ private
24
+
25
+ def scannerWillSync(scanner)
26
+ @syncStarted.call
27
+ end
28
+
29
+ def didSyncWithProgress(progress, total:total)
30
+ @syncProgressed.call(progress, total)
31
+ end
32
+
33
+ def scannerDidSync(scanner)
34
+ @lastSync = NSDate.date.timeIntervalSince1970
35
+ @syncCompleted.call(scanner.count(nil))
36
+ end
37
+
38
+ def scanner(scanner, failedToSyncWithError:error)
39
+ errorCode = error.code
40
+ if errorCode >= 0 and errorCode != MS_BUSY
41
+ errorString = NSString.stringWithCString(ms_errmsg(errorCode), encoding:NSUTF8StringEncoding)
42
+ NSLog("[MOODSTOCKS SDK] FAILED TO SYNC WITH ERROR : #{errorString}.")
43
+ end
44
+ @syncFailed.call(error)
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,3 @@
1
+ module Motionscan
2
+ VERSION = "0.0.1"
3
+ end
data/lib/motionscan.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'motion/project/motionscan'
2
+ require 'motionscan/version'
3
+ require 'motion-cocoapods'
4
+
5
+ unless defined?(Motion::Project::Config)
6
+ raise "This file must be required within a RubyMotion project Rakefile."
7
+ end
8
+
9
+ Motion::Project::App.setup do |app|
10
+ Dir.glob(File.join(File.dirname(__FILE__), 'motionscan/*.rb')).each do |file|
11
+ app.files.unshift(file)
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: motionscan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joffrey Jaffeux
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: motion-cocoapods
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.2.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: A RubyMotion Moodstocks SDK image recognition wrapper
47
+ email:
48
+ - j.jaffeux@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - LICENSE
56
+ - Motionscan.gemspec
57
+ - README.md
58
+ - RakefileExample
59
+ - app/app_delegate.rb
60
+ - app/root_view_controller.rb
61
+ - app/scanner_view_controller.rb
62
+ - lib/motion/project/motionscan.rb
63
+ - lib/motionscan.rb
64
+ - lib/motionscan/flash.rb
65
+ - lib/motionscan/result.rb
66
+ - lib/motionscan/scanner.rb
67
+ - lib/motionscan/sync.rb
68
+ - lib/motionscan/version.rb
69
+ homepage: https://github.com/jjaffeux/Motionscan
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.23
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: A RubyMotion Moodstocks SDK image recognition wrapper
93
+ test_files: []