katana_resource 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 13fe737e54c6eec8768b494347bc898ab0a19f8c
4
+ data.tar.gz: e19e589f3574b569a53c8f75b959dff4bcf7bae4
5
+ SHA512:
6
+ metadata.gz: 1f488768b4ee804de25ddd539c30c6ad7ff08a0427ca401806c6dda481153b8c4396f61350e480b66d86f74d00a9f2891f6d7110beabf16b489cddcb58991f52
7
+ data.tar.gz: ef99d4b43abca67bf9fe9bad3ec7a2e3f969e76f6b2943208c1b68e7f1974fb2904753d8b99316bf382164371c6c0f5ab891efaede37afe3fcbc6d6c7899640b
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ ./build
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ .repl_history
12
+ .ruby-version
13
+ build/iPhoneSimulator-8.0-Development
14
+ katana_resource-*.gem
15
+ vendor
@@ -0,0 +1,6 @@
1
+ [submodule "vendor/RKSwipeBetweenViewControllers"]
2
+ path = vendor/RKSwipeBetweenViewControllers
3
+ url = git@github.com:cwRichardKim/RKSwipeBetweenViewControllers.git
4
+ [submodule "vendor/AKETooltip"]
5
+ path = vendor/AKETooltip
6
+ url = git@github.com:akeara/AKETooltip.git
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in katana_resource.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Bodacious
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,41 @@
1
+ # KatanaResource
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/katana_resource`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'katana_resource'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install katana_resource
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake false` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/katana_resource.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
@@ -0,0 +1,36 @@
1
+ # -*- coding: utf-8 -*-
2
+ $:.unshift("/Library/RubyMotion/lib")
3
+ $:.unshift("./lib")
4
+ require 'motion/project/template/ios'
5
+
6
+ begin
7
+ Bundler.setup
8
+ Bundler.require
9
+ rescue LoadError
10
+ puts "Please install bundler!"
11
+ end
12
+
13
+ Motion::Require.require_relative_enabled = true
14
+ require "afmotion"
15
+ require "webstub"
16
+
17
+
18
+ require "katana_resource"
19
+
20
+ Motion::Require.all(Dir.glob('app/**/*.rb'))
21
+
22
+ # Better test output
23
+ ENV["output"] ||= "test_unit"
24
+
25
+ Motion::Project::App.setup do |app|
26
+ # Use `rake config' to see complete project settings.
27
+ app.name = "Katana Resource"
28
+
29
+ app.identifier = "com.katanacode.katanaresource"
30
+
31
+ app.deployment_target = "8.0"
32
+
33
+ app.files += Dir.glob(File.join(File.dirname(__FILE__), "lib/katana_resource/**/*.rb"))
34
+
35
+
36
+ end
@@ -0,0 +1,10 @@
1
+ class AppDelegate
2
+
3
+ def application(application, didFinishLaunchingWithOptions: launchOptions)
4
+ # If we're in test mode, don't create a UIWindow
5
+ return true if RUBYMOTION_ENV == 'test'
6
+
7
+ true
8
+ end
9
+
10
+ end
@@ -0,0 +1,4 @@
1
+ class Bicycle < KatanaResource::Base
2
+ self.root_url = "http://localhost:3000"
3
+
4
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'katana_resource/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "katana_resource"
8
+ spec.version = KatanaResource::VERSION
9
+ spec.authors = ["Bodacious"]
10
+ spec.email = ["gavin@gavinmorrice.com"]
11
+
12
+ spec.summary = %q{A work in progress, please ignore for now}
13
+ spec.description = %q{A work in progress, please ignore for now :)}
14
+ spec.homepage = "http://katanacode.com"
15
+ spec.license = "MIT"
16
+ spec.files = `git ls-files -z`.split("\x0").
17
+ reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+
19
+ spec.require_paths = ["lib"]
20
+ spec.add_runtime_dependency "motion-require", "~> 0.2"
21
+ spec.add_runtime_dependency "afmotion", "~> 2.6"
22
+ spec.add_runtime_dependency "motion-support", "~> 1.1.0"
23
+ spec.add_runtime_dependency "motion-cocoapods", ">= 1.7.8"
24
+ spec.add_runtime_dependency "bubble-wrap", "~> 1.9"
25
+ spec.add_development_dependency "webstub", "~> 1.1"
26
+ spec.add_development_dependency "motion-redgreen", "~> 1.0"
27
+ spec.add_development_dependency "bundler", "~> 1.10"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ end
@@ -0,0 +1,9 @@
1
+ require "motion-require"
2
+ require "motion-support"
3
+ require "afmotion"
4
+ require "bubble-wrap/core"
5
+
6
+ Motion::Require.require_relative_enabled = true
7
+
8
+ Motion::Require.all(Dir.glob(File.expand_path('../core_ext/**/*.rb', __FILE__)))
9
+ Motion::Require.all(Dir.glob(File.expand_path('../katana_resource/**/*.rb', __FILE__)))
@@ -0,0 +1,22 @@
1
+ module KatanaResource
2
+
3
+ module Associations
4
+
5
+ extend MotionSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ def has_one(assoc_name, options = {})
10
+ options[:class_name] ||= assoc_name.to_s.classify
11
+ attribute(assoc_name, options[:class_name].to_s.constantize)
12
+ end
13
+
14
+ def has_many(assoc_name, options = {})
15
+ options[:class_name] ||= assoc_name.to_s.singularize.classify
16
+ attribute(assoc_name, options[:class_name].to_s.constantize, collection: true) end
17
+
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,178 @@
1
+ require_relative "resource_attribute"
2
+ require_relative "unknown_attribute_type"
3
+ module KatanaResource
4
+
5
+
6
+ module Attributes
7
+
8
+ extend MotionSupport::Concern
9
+
10
+ def encodeWithCoder(coder)
11
+ self.class.resource_attributes.each do |att|
12
+ value = self.send(att.name)
13
+ coder.send(:"encode#{coder_method_for_type(att.type)}:forKey", value, att.name)
14
+ end
15
+ end
16
+
17
+ def initWithCoder(coder)
18
+ self.class.resource_attributes.each do |att|
19
+ value = coder.send(:"decode#{coder_method_for_type(att.type)}ForKey", att.name)
20
+ self.send(:"#{att.name}=", value)
21
+ end
22
+ return self
23
+ end
24
+
25
+ # This record's attributes. Keys are always Strings.
26
+ #
27
+ # Example:
28
+ #
29
+ # User.new(name: "Bodacious", id: 7).attributes
30
+ # # => {"name" => "Bodacious", "id" => 7}
31
+ #
32
+ # Returns a Hash
33
+ def attributes
34
+ return_val = Hash[self.class.resource_attributes.map do |attribute|
35
+ [attribute.name.to_s, send(attribute.name)]
36
+ end]
37
+ end
38
+
39
+ def non_association_attributes()
40
+ return_val = Hash[self.class.resource_attributes.map do |attribute|
41
+ [attribute.name.to_s, send(attribute.name)] unless attribute.options[:collection]
42
+ end]
43
+ end
44
+
45
+ def primary_key
46
+ @primary_key ||= self.class.primary_key
47
+ end
48
+
49
+
50
+ private
51
+
52
+
53
+ def coder_method_for_type(type)
54
+ case type
55
+ when Integer then :Int
56
+ when Float then :Float
57
+ when Boolean then :Bool
58
+ else
59
+ :Object
60
+ end
61
+ end
62
+
63
+
64
+ module ClassMethods
65
+
66
+ # When a class is inherited, pass on the inherited attributes to subclass.
67
+ # If the class is inheriting from Base, don't pass on attributes.
68
+ #
69
+ #
70
+ # klass - The class inheriting from self
71
+ def inherited(klass)
72
+ return if self == KatanaResource::Base
73
+ klass.instance_variable_set("@resource_attributes", resource_attributes)
74
+ end
75
+
76
+ def resource_attributes
77
+ @resource_attributes ||= []
78
+ end
79
+
80
+ def primary_key(value = nil)
81
+ if value
82
+ @primary_key = value
83
+ else
84
+ return @primary_key || :id
85
+ end
86
+ end
87
+
88
+ def attribute(*args)
89
+ options = args.extract_options!
90
+ type = args.last.is_a?(Class) ? args.pop : String
91
+ args.each do |name|
92
+ self.resource_attributes << ResourceAttribute.new(name, type, options)
93
+
94
+ define_method :"#{name}?" do
95
+ instance_variable_get(:"@#{name}").present?
96
+ end
97
+
98
+ define_method :"#{name}=" do |value|
99
+ instance_variable_set(:"@#{name}",
100
+ self.class.cast_value_as_attribute_type(value, name))
101
+ end unless options[:read_only]
102
+
103
+ define_method :"#{name}" do
104
+ default_val = options[:collection] == true ? [] : nil
105
+ ivar_val = instance_variable_get(:"@#{name}")
106
+ ivar_val.nil? ? default_val : ivar_val
107
+ end unless options[:write_only]
108
+
109
+ end
110
+ end
111
+
112
+ def cast_value_as_attribute_type(value, name)
113
+ case
114
+ when type_name_for_attribute(name) == "Time"
115
+ time = Time.iso8601_with_timezone(value)
116
+ time.utc if time
117
+
118
+ when type_name_for_attribute(name) == "Date" then value.to_date
119
+ when type_name_for_attribute(name) == "String" then unescape_string(value.to_s)
120
+ when type_name_for_attribute(name) == "Symbol" then value.to_sym
121
+ when type_name_for_attribute(name) == "Integer" then value.to_i
122
+ when type_name_for_attribute(name) == "Float" then value.to_f
123
+ when type_name_for_attribute(name) == "Boolean" then Boolean.new(value).bool_val
124
+ when defined?(type_for_attribute(name))
125
+
126
+ # This will be true if we're decoding the record from disc rather than loading
127
+ # from server
128
+ # TODO: Refacator this...
129
+ if value.is_a?(KatanaResource::Base)
130
+ return value
131
+ end
132
+ return_val = [value].flatten.map { |atts| type_for_attribute(name).new(atts) }
133
+ if attribute_is_collection?(name)
134
+ return_val
135
+ else
136
+ return_val.first
137
+ end
138
+ else
139
+ raise UnknownAttributeType, "Don't know how to cast #{type} for attribute"
140
+ end
141
+ end
142
+
143
+ def attribute_named(name)
144
+ resource_attributes.detect { |att| att.name.to_s == name.to_s }
145
+ end
146
+
147
+
148
+ private
149
+
150
+
151
+ def type_for_attribute(name)
152
+ attribute_named(name).type
153
+ end
154
+
155
+ def type_name_for_attribute(name)
156
+ attribute_named(name).type.name
157
+ end
158
+
159
+ def attribute_is_collection?(name)
160
+ attribute_named(name).collection?
161
+ end
162
+
163
+ UNESCAPES = {
164
+ 'a' => "\x07", 'b' => "\x08", 't' => "\x09",
165
+ 'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c",
166
+ 'r' => "\x0d", 'e' => "\x1b", "\\\\" => "\x5c",
167
+ "\"" => "\x22", "'" => "\x27"
168
+ }
169
+
170
+ def unescape_string(string)
171
+ string
172
+ end
173
+
174
+ end
175
+
176
+ end
177
+
178
+ end
@@ -0,0 +1,61 @@
1
+ require_relative "associations"
2
+ require_relative "attributes"
3
+ require_relative "client"
4
+ require_relative "create"
5
+ require_relative "delete"
6
+ require_relative "freeform_requests"
7
+ require_relative "read"
8
+ require_relative "resource_paths"
9
+ require_relative "singleton_records"
10
+ require_relative "url_encoding"
11
+ require_relative "configuration"
12
+
13
+ module KatanaResource
14
+
15
+
16
+ class Base
17
+
18
+ include KatanaResource::Associations
19
+ include KatanaResource::Attributes
20
+ include KatanaResource::Client
21
+ include KatanaResource::Configuration
22
+ include KatanaResource::Create
23
+ include KatanaResource::Delete
24
+ include KatanaResource::Read
25
+ include KatanaResource::FreeformRequests
26
+ include KatanaResource::ResourcePaths
27
+ include KatanaResource::SingletonRecords
28
+ include KatanaResource::UrlEncoding
29
+
30
+ def initialize(attributes = {})
31
+ attributes = attributes.with_indifferent_access
32
+ self.class.resource_attributes.each do |resource_att|
33
+ next unless attributes[resource_att.name]
34
+ send(:"#{resource_att.name}=", attributes[resource_att.name])
35
+ end
36
+ end
37
+
38
+ # TODO: Find a better way to represent a persisted record
39
+ def persisted?
40
+ id.present?
41
+ end
42
+
43
+ def ==(record)
44
+ return false unless record.respond_to?(:id)
45
+ self.id == record.id
46
+ end
47
+
48
+
49
+
50
+
51
+
52
+ class << self
53
+
54
+ def fetched_data=(data)
55
+ @fetched_data = data
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,15 @@
1
+ module KatanaResource
2
+ class ::Boolean
3
+
4
+ def initialize(value)
5
+ @value = true if value.to_s =~ /true|1/
6
+ @value = false if value.to_s =~ /false|0/
7
+ raise ArgumentError, "#{value} is not a valid boolean" if @value.nil? && !value.nil?
8
+ end
9
+
10
+ def bool_val
11
+ @value
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ module KatanaResource
2
+
3
+ module Client
4
+
5
+ extend MotionSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ # DSL Mapping to properties of AFHTTPSessionManager
10
+ def client
11
+ AFMotion::Client.shared || load_client
12
+ end
13
+
14
+
15
+ def load_client
16
+ AFMotion::Client.build_shared(root_url) do
17
+ header "Accept", "application/json"
18
+ KatanaResource::Base.additional_headers.each do |name, value|
19
+ header name, value
20
+ end
21
+ response_serializer :json
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,25 @@
1
+ module KatanaResource
2
+
3
+ module Configuration
4
+
5
+ extend MotionSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+
10
+ def additional_headers
11
+ @additional_headers ||= {}
12
+ end
13
+
14
+ def root_url=(value)
15
+ @@root_url = value
16
+ end
17
+
18
+ def root_url
19
+ @@root_url || raise("Base::root_url not defined")
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,15 @@
1
+ class NSString
2
+
3
+ def to_date
4
+ Date.new(*split(/\/|\-/))
5
+ end
6
+
7
+ def to_time
8
+ Time.iso8601_with_timezone(self)
9
+ end
10
+
11
+ def to_bool
12
+ KatanaResource::Boolean.new(self).bool_val
13
+ end
14
+
15
+ end
@@ -0,0 +1,54 @@
1
+ module KatanaResource
2
+
3
+ module Create
4
+
5
+ extend MotionSupport::Concern
6
+
7
+ def save(additional_params = {}, &block)
8
+ attributes = self.attributes
9
+ record = self
10
+ self.class.class_eval { save_record(record, attributes, additional_params, &block) }
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+
16
+ def create(attributes, &block)
17
+ new(attributes).save(&block)
18
+ end
19
+
20
+
21
+ protected
22
+
23
+
24
+ def save_record(record, attributes = {}, additional_params = {}, &block)
25
+ UIApplication.sharedApplication.setNetworkActivityIndicatorVisible(true)
26
+ attributes = attributes.reject { |k,v| v.nil? }
27
+ nested_attributes = { record.class.name.underscore => attributes }
28
+ verb = save_verb_for_record(record)
29
+ path = save_path_for_record(record)
30
+ params = nested_attributes.merge(additional_params)
31
+ call_block = Proc.new do |result, error|
32
+ UIApplication.sharedApplication.setNetworkActivityIndicatorVisible(false)
33
+ prepare_return_object_for_result(result, block)
34
+ end
35
+ client.send(verb, path, params, &call_block)
36
+ end
37
+
38
+ def save_path_for_record(record)
39
+ if record.persisted?
40
+ prepare_url_and_params(singular_resource_path, id: record.id)
41
+ else
42
+ prepare_url_and_params(plural_resource_path)
43
+ end.first
44
+ end
45
+
46
+ def save_verb_for_record(record)
47
+ record.persisted? ? :patch : :post
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,33 @@
1
+ module KatanaResource
2
+
3
+ module Delete
4
+
5
+ extend MotionSupport::Concern
6
+
7
+ def delete(additional_params = {}, &block)
8
+ return false unless persisted?
9
+ attributes = self.attributes
10
+ record = self
11
+ self.class.class_eval do
12
+ delete_record(record, attributes, additional_params, &block)
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ def delete_record(record, attributes = {}, params = {}, &block)
19
+ UIApplication.sharedApplication.setNetworkActivityIndicatorVisible(true)
20
+ verb = :delete
21
+ path = prepare_url_and_params(singular_resource_path, id: record.id).first
22
+ call_block = Proc.new do |result, error|
23
+ UIApplication.sharedApplication.setNetworkActivityIndicatorVisible(false)
24
+ prepare_return_object_for_result(result, block)
25
+ end
26
+ client.send(:delete, path, params, &call_block)
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,65 @@
1
+ module KatanaResource
2
+
3
+ class Error
4
+
5
+ attr_reader :ns_error
6
+
7
+ attr_reader :description
8
+
9
+ attr_reader :localized_description
10
+
11
+
12
+ def initialize(ns_error)
13
+ @ns_error = ns_error
14
+ @description = ns_error.description unless nil?
15
+ @localized_description = ns_error.localizedDescription unless nil?
16
+ end
17
+
18
+ def code
19
+ val = description[/(?<=\()\d{3}(?=\))/].to_i
20
+ return val if val > 0
21
+ end
22
+
23
+ def unauthorized?
24
+ code == 401
25
+ end
26
+
27
+ def bad_request?
28
+ code == 400
29
+ end
30
+
31
+ def not_found?
32
+ code == 404
33
+ end
34
+
35
+ def offline?
36
+ description.index('The Internet connection appears to be offline.')
37
+ end
38
+
39
+ def nil?
40
+ ns_error.nil?
41
+ end
42
+
43
+ def data
44
+ json = user_info["com.alamofire.serialization.response.error.data"] || "{}"
45
+ BW::JSON.parse(json)
46
+ end
47
+
48
+ def user_info
49
+ ns_error.userInfo
50
+ end
51
+
52
+ def message
53
+ if !nil? && data["errors"]
54
+ case data["errors"]
55
+ when String then data["errors"]
56
+ when Array then data["errors"].join(", ")
57
+ else
58
+ data["errors"].to_s
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,33 @@
1
+ module KatanaResource
2
+ module FreeformRequests
3
+
4
+ extend MotionSupport::Concern
5
+
6
+
7
+ def patch(action_name, params = {}, &block)
8
+ self.class.patch(self, action_name, params, &block)
9
+ end
10
+
11
+
12
+ alias_method :put, :patch
13
+
14
+
15
+ module ClassMethods
16
+
17
+ def patch(record, action_name, params = {}, &block)
18
+ resource_action_path = File.join(singular_resource_path, action_name)
19
+ params_with_primary_key = params.merge(primary_key => record.send(primary_key))
20
+ url_and_params = prepare_url_and_params(resource_action_path,
21
+ params_with_primary_key)
22
+ UIApplication.sharedApplication.setNetworkActivityIndicatorVisible(true)
23
+ client.put(*url_and_params) do |result, error|
24
+ UIApplication.sharedApplication.setNetworkActivityIndicatorVisible(false)
25
+ prepare_return_object_for_result(result, block)
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,94 @@
1
+ require_relative "error"
2
+ module KatanaResource
3
+
4
+ module Read
5
+
6
+ extend MotionSupport::Concern
7
+
8
+ module ClassMethods
9
+
10
+ # TODO: Support finding records where id is not provided
11
+ def find(key, params = {}, &block)
12
+ UIApplication.sharedApplication.setNetworkActivityIndicatorVisible(true)
13
+ client.get(*resource_path_for_key(key, params)) do |result, error|
14
+ UIApplication.sharedApplication.setNetworkActivityIndicatorVisible(false)
15
+ prepare_return_object_for_result(result, block)
16
+ end
17
+ end
18
+
19
+ def all
20
+ @all ||= []
21
+ end
22
+
23
+ def all=(value)
24
+ @all = value
25
+ end
26
+
27
+ def any?
28
+ all.any?
29
+ end
30
+
31
+ def none?
32
+ all.none?
33
+ end
34
+
35
+ alias :empty? :none?
36
+
37
+ def disabled_method(*)
38
+ puts "Cannot call :#{__callee__} from #{self}. Is disabled"
39
+ end
40
+
41
+
42
+ private
43
+
44
+
45
+ def require_parent_resource!
46
+ singleton_class.send(:alias_method, :find, :disabled_method)
47
+ singleton_class.send(:alias_method, :all, :disabled_method)
48
+ end
49
+
50
+ def prepare_return_object_for_result(result, block)
51
+ puts "No block given for find" and return unless block
52
+ if result.success?
53
+ return_val = prepare_success_return_object_for_result(result, block)
54
+ else
55
+ return_val = prepare_failure_return_object_for_result(result, block)
56
+ end
57
+ block.call(return_val, Error.new(result.error))
58
+ end
59
+
60
+ def prepare_success_return_object_for_result(result, block)
61
+ if result.object.is_a?(Hash)
62
+ new(result.object)
63
+ else
64
+ result.object.map { |atts| new(atts) }
65
+ end
66
+ end
67
+
68
+ def prepare_failure_return_object_for_result(result, block)
69
+ log_result(result) && nil
70
+ end
71
+
72
+ def resource_path_for_key(key, params)
73
+ case key
74
+ when Integer, String
75
+ complete_params = params.merge(primary_key => key)
76
+ prepare_url_and_params(singular_resource_path, complete_params)
77
+ when :all
78
+ prepare_url_and_params(plural_resource_path, params)
79
+ else
80
+ raise "FREAKOUT!"
81
+ end
82
+ end
83
+
84
+ def log_result(result)
85
+ if RUBYMOTION_ENV == "development"
86
+ log("#{result.object}.\n#{result.error.description}")
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
94
+ end
@@ -0,0 +1,29 @@
1
+ module KatanaResource
2
+
3
+ class ResourceAttribute
4
+
5
+
6
+ attr_reader :name
7
+
8
+ attr_reader :type
9
+
10
+ attr_reader :options
11
+
12
+
13
+ def initialize(name, type, options = {})
14
+ @name = name
15
+ @type = type
16
+ @options = options.with_indifferent_access
17
+ end
18
+
19
+ def collection?
20
+ options[:collection] == true
21
+ end
22
+
23
+ def singular?
24
+ !collection?
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,42 @@
1
+ module KatanaResource
2
+
3
+ module ResourcePaths
4
+
5
+ extend MotionSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ def plural_resource_path(value = nil)
10
+ if value
11
+ @plural_resource_path = value
12
+ else
13
+ @plural_resource_path || default_plural_resource_path
14
+ end
15
+ end
16
+
17
+
18
+ def singular_resource_path(value = nil)
19
+ if value
20
+ @singular_resource_path = value
21
+ else
22
+ @singular_resource_path || default_singular_resource_path
23
+ end
24
+ end
25
+
26
+
27
+ private
28
+
29
+
30
+ def default_plural_resource_path
31
+ self.name.underscore.pluralize
32
+ end
33
+
34
+ def default_singular_resource_path
35
+ self.name.underscore.pluralize + "/:id"
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,50 @@
1
+ module KatanaResource
2
+
3
+ module SingletonRecords
4
+
5
+ extend MotionSupport::Concern
6
+
7
+ module IncludedClassMethods
8
+
9
+ def current=(record)
10
+ @current = record
11
+ end
12
+
13
+ def current
14
+ @current
15
+ end
16
+
17
+ def current?
18
+ !!@current
19
+ end
20
+
21
+ def current_path
22
+ (App.documents_path + "/#{name}.current").freeze
23
+ end
24
+
25
+ def save_current(value = nil)
26
+ self.current = value if value
27
+ NSKeyedArchiver.archiveRootObject(current, toFile: current_path)
28
+ end
29
+
30
+ def clear_current
31
+ self.current = nil
32
+ save_current
33
+ end
34
+
35
+ def load_current
36
+ self.current = NSKeyedUnarchiver.unarchiveObjectWithFile(current_path)
37
+ end
38
+
39
+ end
40
+
41
+ module ClassMethods
42
+
43
+ def singleton_record!
44
+ extend IncludedClassMethods
45
+ end
46
+
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,6 @@
1
+ module KatanaResource
2
+
3
+ class UnknownAttributeType < StandardError
4
+ end
5
+
6
+ end
@@ -0,0 +1,33 @@
1
+ module KatanaResource
2
+
3
+ module UrlEncoding
4
+
5
+ extend MotionSupport::Concern
6
+
7
+
8
+ module ClassMethods
9
+
10
+ # Prepares a url string by replacing named parameters with param values and
11
+ # removing these from the params.
12
+ #
13
+ # Examples
14
+ #
15
+ # prepare_url_and_params("users/:id/:foo", {id: 1, foo: 'foo', bar: 'bar'})
16
+ # # => ["users/1/foo", {bar: "bar"}]
17
+ #
18
+ # Returns an Array with url String and params Hash
19
+ def prepare_url_and_params(url_string, params = {})
20
+ params, key_names = params.with_indifferent_access, []
21
+ new_string = url_string.gsub(/\:([^\/\:]+)/) { |match|
22
+ key_names << match.slice(1..-1)
23
+ "%{#{$1}}"
24
+ } % params
25
+ return "#{new_string}.json", params.delete_if { |key,v| key_names.include?(key) }
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
@@ -0,0 +1,3 @@
1
+ module KatanaResource
2
+ VERSION = "0.7.3"
3
+ end
metadata ADDED
@@ -0,0 +1,200 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: katana_resource
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.3
5
+ platform: ruby
6
+ authors:
7
+ - Bodacious
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: motion-require
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: afmotion
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: motion-support
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.1.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: motion-cocoapods
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.7.8
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.7.8
69
+ - !ruby/object:Gem::Dependency
70
+ name: bubble-wrap
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.9'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webstub
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: motion-redgreen
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: bundler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.10'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.10'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '10.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '10.0'
139
+ description: A work in progress, please ignore for now :)
140
+ email:
141
+ - gavin@gavinmorrice.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".gitmodules"
148
+ - ".travis.yml"
149
+ - Gemfile
150
+ - LICENSE.txt
151
+ - README.md
152
+ - Rakefile
153
+ - app/app_delegate.rb
154
+ - app/models/bicycle.rb
155
+ - katana_resource-0.1.0.gem
156
+ - katana_resource.gemspec
157
+ - lib/katana_resource.rb
158
+ - lib/katana_resource/associations.rb
159
+ - lib/katana_resource/attributes.rb
160
+ - lib/katana_resource/base.rb
161
+ - lib/katana_resource/boolean.rb
162
+ - lib/katana_resource/client.rb
163
+ - lib/katana_resource/configuration.rb
164
+ - lib/katana_resource/core_ext/ns_string.rb
165
+ - lib/katana_resource/create.rb
166
+ - lib/katana_resource/delete.rb
167
+ - lib/katana_resource/error.rb
168
+ - lib/katana_resource/freeform_requests.rb
169
+ - lib/katana_resource/read.rb
170
+ - lib/katana_resource/resource_attribute.rb
171
+ - lib/katana_resource/resource_paths.rb
172
+ - lib/katana_resource/singleton_records.rb
173
+ - lib/katana_resource/unknown_attribute_type.rb
174
+ - lib/katana_resource/url_encoding.rb
175
+ - lib/katana_resource/version.rb
176
+ homepage: http://katanacode.com
177
+ licenses:
178
+ - MIT
179
+ metadata: {}
180
+ post_install_message:
181
+ rdoc_options: []
182
+ require_paths:
183
+ - lib
184
+ required_ruby_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ requirements: []
195
+ rubyforge_project:
196
+ rubygems_version: 2.5.1
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: A work in progress, please ignore for now
200
+ test_files: []