katana_resource 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []