ixtlan-remote 0.1.0

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.
Files changed (89) hide show
  1. data/Gemfile +2 -0
  2. data/README.md +64 -0
  3. data/agpl-3.0.txt +661 -0
  4. data/lib/ixtlan-remote.rb +23 -0
  5. data/lib/ixtlan/passwords.rb +52 -0
  6. data/lib/ixtlan/passwords.rb~ +47 -0
  7. data/lib/ixtlan/railtie.rb~ +17 -0
  8. data/lib/ixtlan/remote.rb +23 -0
  9. data/lib/ixtlan/remote/access_controller.rb +62 -0
  10. data/lib/ixtlan/remote/access_controller.rb~ +25 -0
  11. data/lib/ixtlan/remote/constant_time_compare.rb +39 -0
  12. data/lib/ixtlan/remote/constant_time_compare.rb~ +19 -0
  13. data/lib/ixtlan/remote/heartbeat.rb~ +80 -0
  14. data/lib/ixtlan/remote/model_conversion.rb~ +245 -0
  15. data/lib/ixtlan/remote/model_helpers.rb +73 -0
  16. data/lib/ixtlan/remote/model_helpers.rb~ +53 -0
  17. data/lib/ixtlan/remote/permission.rb +40 -0
  18. data/lib/ixtlan/remote/permission.rb~ +19 -0
  19. data/lib/ixtlan/remote/railtie.rb +30 -0
  20. data/lib/ixtlan/remote/railtie.rb~ +9 -0
  21. data/lib/ixtlan/remote/remote_access_controller.rb~ +25 -0
  22. data/lib/ixtlan/remote/remote_permisson.rb~ +9 -0
  23. data/lib/ixtlan/remote/resource.rb +145 -0
  24. data/lib/ixtlan/remote/resource.rb-~ +165 -0
  25. data/lib/ixtlan/remote/resource.rb~ +152 -0
  26. data/lib/ixtlan/remote/rest.rb +108 -0
  27. data/lib/ixtlan/remote/rest.rb~ +83 -0
  28. data/lib/ixtlan/remote/rest_resource.rb~ +140 -0
  29. data/lib/ixtlan/remote/rest_resource_config.rb~ +63 -0
  30. data/lib/ixtlan/remote/rest_resource_factory.rb~ +259 -0
  31. data/lib/ixtlan/remote/rest_resource_old.rb-~ +259 -0
  32. data/lib/ixtlan/remote/server.rb +119 -0
  33. data/lib/ixtlan/remote/server.rb~ +82 -0
  34. data/lib/ixtlan/remote/summary.rb +46 -0
  35. data/lib/ixtlan/remote/summary.rb~ +23 -0
  36. data/lib/ixtlan/remote/sync.rb +104 -0
  37. data/lib/ixtlan/remote/sync.rb~ +78 -0
  38. data/lib/ixtlan/remote/sync_summary.rb~ +23 -0
  39. data/lib/ixtlan/remote/tranlation_key.rb~ +7 -0
  40. data/lib/ixtlan/remote/translation.rake~ +194 -0
  41. data/lib/ixtlan/remote/translation_models.rb~ +11 -0
  42. data/lib/ixtlan/remote/updater.rb~ +71 -0
  43. data/lib/ixtlan/user_management/application_model.rb +30 -0
  44. data/lib/ixtlan/user_management/application_model.rb~ +21 -0
  45. data/lib/ixtlan/user_management/application_resource.rb +48 -0
  46. data/lib/ixtlan/user_management/application_resource.rb~ +21 -0
  47. data/lib/ixtlan/user_management/authentcator.rb~ +31 -0
  48. data/lib/ixtlan/user_management/authentication_model.rb +31 -0
  49. data/lib/ixtlan/user_management/authentication_model.rb~ +21 -0
  50. data/lib/ixtlan/user_management/authenticator.rb +55 -0
  51. data/lib/ixtlan/user_management/authenticator.rb~ +20 -0
  52. data/lib/ixtlan/user_management/domain_resource.rb +48 -0
  53. data/lib/ixtlan/user_management/domain_resource.rb~ +21 -0
  54. data/lib/ixtlan/user_management/dummy_authentication.rb +50 -0
  55. data/lib/ixtlan/user_management/dummy_authentication.rb~ +49 -0
  56. data/lib/ixtlan/user_management/group.rb~ +39 -0
  57. data/lib/ixtlan/user_management/group_model.rb +31 -0
  58. data/lib/ixtlan/user_management/group_model.rb~ +21 -0
  59. data/lib/ixtlan/user_management/models.rb~ +39 -0
  60. data/lib/ixtlan/user_management/session-serializer.rb~ +18 -0
  61. data/lib/ixtlan/user_management/session_cuba.rb +47 -0
  62. data/lib/ixtlan/user_management/session_cuba.rb~ +44 -0
  63. data/lib/ixtlan/user_management/session_manager.rb +38 -0
  64. data/lib/ixtlan/user_management/session_model.rb +36 -0
  65. data/lib/ixtlan/user_management/session_model.rb~ +10 -0
  66. data/lib/ixtlan/user_management/session_plugin.rb +32 -0
  67. data/lib/ixtlan/user_management/session_serializer.rb +21 -0
  68. data/lib/ixtlan/user_management/session_serializer.rb~ +21 -0
  69. data/lib/ixtlan/user_management/user.rb~ +16 -0
  70. data/lib/ixtlan/user_management/user_model.rb +36 -0
  71. data/lib/ixtlan/user_management/user_model.rb~ +33 -0
  72. data/lib/ixtlan/user_management/user_resource.rb +55 -0
  73. data/lib/ixtlan/user_management/user_resource.rb~ +24 -0
  74. data/lib/ixtlan/user_management/user_serializer.rb +15 -0
  75. data/lib/ixtlan/user_management/user_serializer.rb~ +23 -0
  76. data/spec/access_controller_spec.rb +65 -0
  77. data/spec/access_controller_spec.rb~ +65 -0
  78. data/spec/model_helpers_spec.rb +40 -0
  79. data/spec/model_helpers_spec.rb~ +36 -0
  80. data/spec/remote_access_controller_spec.rb~ +36 -0
  81. data/spec/resource_spec.rb +181 -0
  82. data/spec/resource_spec.rb~ +173 -0
  83. data/spec/rest_resource_spec.rb~ +173 -0
  84. data/spec/rest_spec.rb +94 -0
  85. data/spec/rest_spec.rb~ +99 -0
  86. data/spec/rest_with_attribute_name_like_model_name_spec.rb +82 -0
  87. data/spec/sync_spec.rb +83 -0
  88. data/spec/sync_spec.rb~ +81 -0
  89. metadata +313 -0
@@ -0,0 +1,23 @@
1
+ #
2
+ # ixtlan-remote - helper sync data between miniapps or communicate wth realtime
3
+ # rest-services
4
+ # Copyright (C) 2012 Christian Meier
5
+ #
6
+ # This file is part of ixtlan-remote.
7
+ #
8
+ # ixtlan-remote is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Affero General Public License as
10
+ # published by the Free Software Foundation, either version 3 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # ixtlan-remote is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU Affero General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Affero General Public License
19
+ # along with ixtlan-remote. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ if defined?(Rails)
22
+ require 'ixtlan/remote/railtie'
23
+ end
@@ -0,0 +1,52 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+ module Ixtlan
4
+ class HashWithDefault < ::Hash
5
+ def get( key, default = HashWithDefault.new )
6
+ self[ key ] || default
7
+ end
8
+ end
9
+
10
+ class Passwords
11
+
12
+ def self.symbolize_keys(h)
13
+ result = HashWithDefault.new
14
+
15
+ h.each do |k, v|
16
+ v = ' ' if v.nil?
17
+ if v.is_a?(Hash)
18
+ result[k.to_sym] = symbolize_keys(v) unless v.size == 0
19
+ else
20
+ result[k.to_sym] = v unless k.to_sym == v.to_sym
21
+ end
22
+ end
23
+ result
24
+ end
25
+
26
+ def self.load( file )
27
+ rel_file = File.expand_path( file ).sub( /#{File.expand_path '.' }\/?/, '' )
28
+ if File.exists?( file )
29
+ warn "[Passwords] Loaded #{rel_file} file"
30
+ symbolize_keys( YAML::load( ERB.new( IO.read( file ) ).result ) )
31
+ else
32
+ warn "[Passwords] No #{rel_file} file to load"
33
+ end
34
+ end
35
+
36
+ def self.config( file = 'password.yml' )
37
+ @config ||= load( file ) || HashWithDefault.new
38
+ end
39
+
40
+ def self.[]( key )
41
+ @config[ key ]
42
+ end
43
+
44
+ def self.get( key, default = nil )
45
+ if default
46
+ @config.get( key, default )
47
+ else
48
+ @config.get( key )
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,47 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+ module Ixtlan
4
+ class Hash < ::Hash
5
+ def get( key, default = {} )
6
+ self[ key ] || default
7
+ end
8
+ end
9
+
10
+ class Passwords
11
+
12
+ def self.symbolize_keys(h)
13
+ result = Hash.new
14
+
15
+ h.each do |k, v|
16
+ v = ' ' if v.nil?
17
+ if v.is_a?(Hash)
18
+ result[k.to_sym] = symbolize_keys(v) unless v.size == 0
19
+ else
20
+ result[k.to_sym] = v unless k.to_sym == v.to_sym
21
+ end
22
+ end
23
+
24
+ result
25
+ end
26
+
27
+ def self.load( file )
28
+ if File.exists?(file)
29
+ symbolize_keys(YAML::load(ERB.new(IO.read(file)).result))
30
+ else
31
+ warn "no #{file} file to load"
32
+ end
33
+ end
34
+
35
+ def self.config( file = 'password.yml' )
36
+ @config ||= load( file ) || {}
37
+ end
38
+
39
+ def self.[]( key )
40
+ @config[ key ]
41
+ end
42
+
43
+ def self.get( key )
44
+ @config.get( key )
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ require 'ixtlan/remote/rest'
2
+ require 'ixtlan/gettext/manager'
3
+ module Ixtlan
4
+ module Remote
5
+
6
+ class Railtie < Rails::Railtie
7
+
8
+ config.rest = Ixtlan::Remote::Rest.new
9
+
10
+ config.gettext = Ixtlan::Gettext::Manager.new
11
+
12
+ rake_tasks do
13
+ load 'ixtlan/gettext/translation.rake'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ #
2
+ # ixtlan-remote - helper sync data between miniapps or communicate wth realtime
3
+ # rest-services
4
+ # Copyright (C) 2012 Christian Meier
5
+ #
6
+ # This file is part of ixtlan-remote.
7
+ #
8
+ # ixtlan-remote is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Affero General Public License as
10
+ # published by the Free Software Foundation, either version 3 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # ixtlan-remote is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU Affero General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Affero General Public License
19
+ # along with ixtlan-remote. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ if defined?(Rails)
22
+ require 'ixtlan/remote/railtie'
23
+ end
@@ -0,0 +1,62 @@
1
+ #
2
+ # ixtlan-remote - helper sync data between miniapps or communicate wth realtime
3
+ # rest-services
4
+ # Copyright (C) 2012 Christian Meier
5
+ #
6
+ # This file is part of ixtlan-remote.
7
+ #
8
+ # ixtlan-remote is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Affero General Public License as
10
+ # published by the Free Software Foundation, either version 3 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # ixtlan-remote is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU Affero General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Affero General Public License
19
+ # along with ixtlan-remote. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ require 'ixtlan/remote/constant_time_compare'
22
+ module Ixtlan
23
+ module Remote
24
+ module AccessController
25
+
26
+ private
27
+
28
+ include Ixtlan::Remote::ConstantTimeCompare
29
+
30
+ # this implementation uses rails request instance
31
+ def x_service_token
32
+ request.headers['X-SERVICE-TOKEN']
33
+ end
34
+
35
+ protected
36
+
37
+ def permission_model
38
+ Permission
39
+ end
40
+
41
+ public
42
+
43
+ def remote_permission
44
+ @_remote_permission ||=
45
+ begin
46
+ # constant time for finding the right permission
47
+ perm = nil
48
+ token = x_service_token
49
+ raise "ip #{request.remote_ip} sent no token" unless token
50
+ permission_model.all.each do |rp|
51
+ perm = rp if rp.authentication_token && constant_time_compare(rp.authentication_token, token)
52
+ end
53
+ raise "ip #{request.remote_ip} wrong authentication" unless perm
54
+ # if the perm.ip == nil then do not check IP
55
+ # server clusters have many IPs then use perm.ip = nil
56
+ raise "ip #{request.remote_ip} not allowed" if (!perm.allowed_ip.blank? && request.remote_ip != perm.allowed_ip)
57
+ perm
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,25 @@
1
+ require 'ixtlan/remote/constant_time_compare'
2
+ module Ixtlan
3
+ module Remote
4
+ module AccessController
5
+ include Ixtlan::Remote::ConstantTimeCompare
6
+
7
+ def x_service_token
8
+ @_x_service_token ||= request.headers['X-SERVICE-TOKEN']
9
+ end
10
+
11
+ def remote_permission
12
+ # constant time for finding the right permission
13
+ perm = nil
14
+ Permission.all.each do |rp|
15
+ perm = rp if constant_time_compare(rp.token, x_service_token)
16
+ end
17
+ raise "ip #{request.remote_ip} wrong authentication" unless perm
18
+ # if the perm.ip == nil then do not check IP
19
+ # server clusters have many IPs then use perm.ip = nil
20
+ raise "ip #{request.remote_ip} not allowed" if (!perm.ip.blank? && request.remote_ip != perm.ip)
21
+ perm
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ #
2
+ # ixtlan-remote - helper sync data between miniapps or communicate wth realtime
3
+ # rest-services
4
+ # Copyright (C) 2012 Christian Meier
5
+ #
6
+ # This file is part of ixtlan-remote.
7
+ #
8
+ # ixtlan-remote is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Affero General Public License as
10
+ # published by the Free Software Foundation, either version 3 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # ixtlan-remote is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU Affero General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Affero General Public License
19
+ # along with ixtlan-remote. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ module Ixtlan
22
+ module Remote
23
+ module ConstantTimeCompare
24
+
25
+ # from https://github.com/django/django/blob/master/django/utils/crypto.py#L82
26
+ # time to compare is independent of the number of matching characters
27
+ def constant_time_compare(val1, val2)
28
+ if val1.size != val2.size
29
+ return false
30
+ end
31
+ result = 0
32
+ val1.unpack('*C').zip(val2.unpack('*C')).each do |a|
33
+ result |= a[0][0] ^ a[1][0]
34
+ end
35
+ result == 0
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ module Ixtlan
2
+ module Remote
3
+ module ConstantTimeCompare
4
+
5
+ # from https://github.com/django/django/blob/master/django/utils/crypto.py#L82
6
+ # time to compare is independent of the number of matching characters
7
+ def constant_time_compare(val1, val2)
8
+ if val1.size != val2.size
9
+ return false
10
+ end
11
+ result = 0
12
+ val1.chars.zip(val2.chars).each do |a|
13
+ result |= a[0][0] ^ a[1][0]
14
+ end
15
+ result == 0
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,80 @@
1
+ class Ixtlan::Remote::Heartbeat
2
+
3
+ class Summary
4
+ attr_reader :clazz
5
+ attr_accessor :count, :failures
6
+
7
+ def initialize(clazz)
8
+ @count = 0
9
+ @failures = 0
10
+ @clazz = clazz
11
+ end
12
+
13
+ def to_log
14
+ "update #{@clazz} - total: #{@count + @failures} success: #{@count} failures: #{@failures}"
15
+ end
16
+ end
17
+
18
+ def initialize(restserver)
19
+ @count = 0
20
+ @failures = 0
21
+ @restserver = restserver
22
+ end
23
+
24
+ def clazzes
25
+ @clazzes ||= []
26
+ end
27
+
28
+ def register(clazz)
29
+ c = clazz.to_s.pluralize.underscore
30
+ clazzes << c unless clazzes.member?(c)
31
+ end
32
+
33
+ if defined? ActiveRecord
34
+ def max(clazz)
35
+ @restserver.to_class(clazz).maximum(:updated_at)
36
+ end
37
+ else
38
+ def max(clazz)
39
+ @restserver.to_class(clazz).max(:updated_at)
40
+ end
41
+ end
42
+ private :max
43
+
44
+ def last_update(clazz)
45
+ @last_update ||= begin
46
+ last_date = (max(clazz) || 2000.years.ago).utc
47
+ p last_date
48
+ last_date.strftime('%Y-%m-%d %H:%M:%S.') + ("%06d" % (last_date.sec_fraction / Date::NANOSECONDS_IN_DAY / 1000))
49
+ end
50
+ end
51
+ private :last_update
52
+
53
+ def beat(set = clazzes)
54
+ @last_result = []
55
+ set.to_a.each do |clazz|
56
+ summary = Summary.new(clazz)
57
+ @last_result << summary
58
+ @restserver.retrieve(clazz, :last_changes, :updated_at => last_update(clazz)).each do |item|
59
+ p item
60
+ result = item.save
61
+ if result
62
+ summary.count = summary.count + 1
63
+ else
64
+ p item.new?
65
+ p item.errors
66
+ summary.failures = summary.failures + 1
67
+ end
68
+ end
69
+ end
70
+ to_log
71
+ end
72
+ def to_log
73
+ if @last_result
74
+ @last_result.collect { |r| r.to_log }.join("\n\t")
75
+ else
76
+ "no results yet"
77
+ end
78
+ end
79
+ alias :to_s :to_log
80
+ end
@@ -0,0 +1,245 @@
1
+ require 'rest-client'
2
+ module Ixtlan
3
+ module Remote
4
+ module ModelHelpers
5
+
6
+ def to_model_underscore(data)
7
+ to_model(data).to_s.underscore
8
+ end
9
+
10
+ def to_model_singular_underscore(data)
11
+ to_model.underscore.singular
12
+ end
13
+
14
+ def to_model(data)
15
+ case data
16
+ when Hash
17
+ data.keys.first
18
+ when Array
19
+ if data.empty?
20
+ #TODO
21
+ else
22
+ to_model_class(data[0].class)
23
+ end
24
+ when String
25
+ data
26
+ when Symbol
27
+ data
28
+ when Class
29
+ to_model_class(data)
30
+ else
31
+ to_model_class(data.class)
32
+ end
33
+ end
34
+
35
+ def to_model_class(obj)
36
+ obj.respond_to?(:model_name) ? obj.model_name.constantize : obj.class
37
+ end
38
+
39
+ def model_map
40
+ @_model_map ||= {}
41
+ end
42
+ end
43
+
44
+ class RestResourceConfig
45
+
46
+ def register(server_name, url, options = {})
47
+ models = options.delete(:models).dup
48
+ #model_map =
49
+ case models
50
+ when Array
51
+ map = {}
52
+ models.each do |model|
53
+ model_map[to_model_singular_underscore(model)] = model
54
+ end
55
+ map
56
+ when Hash
57
+ models
58
+ else
59
+ {}
60
+ end
61
+
62
+ s = servers[server_name.to_sym] = RestResourceFactory.new(url, model_map, options)
63
+
64
+ model_map.keys.each do |k|
65
+ model_to_servers[k] = s
66
+ end
67
+ s
68
+ end
69
+
70
+ def model_to_servers
71
+ @_models ||= {}
72
+ end
73
+
74
+ def new_resource(model)
75
+ model_to_servers[model].new
76
+ end
77
+
78
+ def to_class(model)
79
+ key = to_model_singular_underscore(model)
80
+ model_to_servers[key].to_class(key)
81
+ end
82
+
83
+ def servers
84
+ @_servers ||= {}
85
+ end
86
+
87
+ def server(name)
88
+ servers[name.to_sym]
89
+ end
90
+
91
+ def create(model, *args)
92
+ new_resource(model).create(model, *args).send
93
+ end
94
+
95
+ def retrieve(model, *args)
96
+ resource = new_resource(model)
97
+ if args.last.is_a? Hash
98
+ params = args.delete_at(args.size - 1)
99
+ resource.retrieve(model, *args).query_params(params)
100
+ else
101
+ resource.retrieve(model, *args)
102
+ end
103
+ resource.send
104
+ end
105
+
106
+ def update(model, *args)
107
+ new_resource(model).update(model, *args).send
108
+ end
109
+
110
+ def delete(model, *args)
111
+ new_resource(model).delete(model, *args).send
112
+ end
113
+
114
+ include ModelHelpers
115
+ end
116
+
117
+ class RestResource
118
+
119
+ include ModelHelpers
120
+
121
+ def initialize(resource, model_map = {})
122
+ @base = resource
123
+ @map = model_map
124
+ end
125
+
126
+ def send(&block)
127
+ raise "no method given - call any CRUD method first" unless @method
128
+ headers = {:content_type => :json, :accept => :json}
129
+ headers[:params] = @params if @params
130
+ result =
131
+ if @payload
132
+ @resource.send @method, @payload.to_json, headers, &block
133
+ else
134
+ @resource.send @method, headers, &block
135
+ end
136
+ result = JSON.load(result)
137
+ if @model
138
+ root = @model_key || @model.to_s.underscore
139
+ if result.is_a? Array
140
+ result.collect do |r|
141
+ first_or_new(r[root])
142
+ end
143
+ else
144
+ first_or_new(result[root])
145
+ end
146
+ else
147
+ result
148
+ end
149
+ end
150
+
151
+ def query_params(params)
152
+ @params = params
153
+ self
154
+ end
155
+
156
+ # retrieve(Locale) => GET /locales
157
+ # retrieve(Locale, 1) => GET /locales/1
158
+ # retrieve(:locales) => GET /locales
159
+ # retrieve(:locales, 1) => GET /locales/1
160
+ def retrieve(*args)
161
+ @method = :get
162
+ @payload = nil
163
+ @params = nil
164
+ @resource = @base[path(*args)]
165
+ self
166
+ end
167
+
168
+ # create(locale) => POST /locales
169
+ # create(:locale => {:code => 'en'}) => POST /locales
170
+ def create(*obj_or_hash)
171
+ @method = :post
172
+ @payload = nil#obj_or_hash.is_a?(Class)? nil : obj_or_hash
173
+ @params = nil
174
+ @resource = @base[path(*obj_or_hash)]
175
+ self
176
+ end
177
+
178
+ def update(*obj_or_hash)
179
+ @method = :put
180
+ @payload = nil#obj_or_hash.is_a?(Class)? nil : obj_or_hash
181
+ @params = nil
182
+ @resource = @base[path(*obj_or_hash)]
183
+ self
184
+ end
185
+
186
+ def delete(*obj_or_hash)
187
+ @method = :delete
188
+ @payload = nil #obj_or_hash.is_a?(Class)? nil : obj_or_hash
189
+ @params = nil
190
+ @resource = @base[path(*obj_or_hash)]
191
+ self
192
+ end
193
+
194
+ private
195
+
196
+ def first_or_new(attributes)
197
+ cond = {}
198
+ @model.key.each { |k| cond[k.name] = attributes[k.name.to_s] }
199
+ m = @model.first_or_new(cond)
200
+ m.attributes = attributes
201
+ m
202
+ end
203
+
204
+ def path(*parts)
205
+ parts.collect {|p| path_part(p)} * '/'
206
+ end
207
+
208
+ def model=(model)
209
+ m = @map[model.singularize]
210
+ m ||= model.singularize.camelize.constantize rescue nil
211
+ if m
212
+ @model = m
213
+ @model_key = model.singularize.underscore
214
+ end
215
+ end
216
+
217
+ def path_part(data)
218
+ model = to_model_underscore(data)
219
+ case data
220
+ when Hash
221
+ @payload = data
222
+ self.model = model
223
+ model = model.pluralize
224
+ when Array
225
+ @payload = data
226
+ self.model = model
227
+ model = model.pluralize
228
+ when String
229
+ self.model = model
230
+ when Fixnum
231
+ when Symbol
232
+ self.model = model
233
+ when Class
234
+ model = model.pluralize
235
+ else
236
+ @payload = data
237
+ self.model = model
238
+ model = model.pluralize
239
+ end
240
+ model.underscore
241
+ end
242
+
243
+ end
244
+ end
245
+ end