serviceable 0.2 → 0.4
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.
- data/lib/serviceable.rb +100 -22
- metadata +4 -4
data/lib/serviceable.rb
CHANGED
@@ -15,50 +15,58 @@ module Serviceable
|
|
15
15
|
#
|
16
16
|
def acts_as_service(object,options={})
|
17
17
|
|
18
|
-
before_filter :assign_new_instance, :
|
19
|
-
before_filter :assign_existing_instance, :
|
20
|
-
before_filter :
|
18
|
+
before_filter :assign_new_instance, only: :create
|
19
|
+
before_filter :assign_existing_instance, only: [ :show, :update, :destroy ]
|
20
|
+
before_filter :assign_collection, only: [ :index, :count ]
|
21
|
+
before_filter :did_assign_collection, only: [ :index, :count ]
|
21
22
|
|
22
23
|
define_method("index") do
|
23
24
|
respond_to do |format|
|
24
|
-
format.json {
|
25
|
-
format.xml {
|
25
|
+
format.json { render json: @collection.to_json(merge_options(options[:index])) }
|
26
|
+
format.xml { render xml: @collection.to_xml(merge_options(options[:index])) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
define_method("count") do
|
31
|
+
respond_to do |format|
|
32
|
+
format.json { render json: @collection.count }
|
33
|
+
format.xml { render xml: @collection.count }
|
26
34
|
end
|
27
35
|
end
|
28
36
|
|
29
37
|
define_method("create") do
|
30
38
|
respond_to do |format|
|
31
|
-
if
|
32
|
-
format.json {
|
33
|
-
format.xml {
|
39
|
+
if @instance.save
|
40
|
+
format.json { render json: @instance }
|
41
|
+
format.xml { render xml: @instance }
|
34
42
|
else
|
35
|
-
format.json {
|
36
|
-
format.xml {
|
43
|
+
format.json { render json: { errors: @instance.errors.full_messages }, status: :unprocessable_entity }
|
44
|
+
format.xml { render xml: { errors: @instance.errors.full_messages }, status: :unprocessable_entity }
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
40
48
|
|
41
49
|
define_method("show") do
|
42
50
|
respond_to do |format|
|
43
|
-
format.json {
|
44
|
-
format.xml {
|
51
|
+
format.json { render json: @instance.to_json(merge_options(options[:show])) }
|
52
|
+
format.xml { render xml: @instance.to_xml(merge_options(options[:show])) }
|
45
53
|
end
|
46
54
|
end
|
47
55
|
|
48
56
|
define_method("update") do
|
49
57
|
respond_to do |format|
|
50
|
-
if
|
58
|
+
if @instance.update_attributes(params[object])
|
51
59
|
format.json { head :ok }
|
52
60
|
format.xml { head :ok }
|
53
61
|
else
|
54
|
-
format.json {
|
55
|
-
format.xml {
|
62
|
+
format.json { render json: { errors: @instance.errors.full_messages }, status: :unprocessable_entity }
|
63
|
+
format.xml { render xml: { errors: @instance.errors.full_messages }, status: :unprocessable_entity }
|
56
64
|
end
|
57
65
|
end
|
58
66
|
end
|
59
67
|
|
60
68
|
define_method("destroy") do
|
61
|
-
|
69
|
+
@instance.destroy
|
62
70
|
|
63
71
|
respond_to do |format|
|
64
72
|
format.json { head :no_content }
|
@@ -66,27 +74,97 @@ module Serviceable
|
|
66
74
|
end
|
67
75
|
end
|
68
76
|
|
77
|
+
# query string params can be given in the following formats:
|
78
|
+
# only=field1,field2
|
79
|
+
# except=field1,field2
|
80
|
+
# include=assoc1
|
81
|
+
#
|
82
|
+
# if an included association is present, only and except params can be nested
|
83
|
+
# include[user][except]=encrypted_password
|
84
|
+
# include[user][only][]=first_name&include[user][only][]=last_name
|
85
|
+
# include[user][only]=first_name,last_name
|
69
86
|
define_method("merge_options") do |options={}|
|
70
87
|
merged_options = options || {}
|
71
88
|
for key in [:only, :except, :include]
|
72
|
-
|
89
|
+
opts = {key => params[key]} if params[key]
|
90
|
+
merged_options = merged_options.merge(opts) if opts
|
73
91
|
end
|
92
|
+
merged_options = deep_split(merged_options)
|
74
93
|
return merged_options
|
75
94
|
end
|
76
95
|
|
77
96
|
define_method("assign_existing_instance") do
|
78
|
-
|
97
|
+
@instance = object.to_s.camelize.constantize
|
98
|
+
if params[:include].kind_of?(Hash)
|
99
|
+
@instance = @instance.includes(params[:include].keys)
|
100
|
+
end
|
101
|
+
if params[:include].kind_of?(String)
|
102
|
+
@instance = @instance.includes(params[:include].split(",").map(&:to_sym))
|
103
|
+
end
|
104
|
+
@instance = @instance.find(params[:id])
|
79
105
|
end
|
80
106
|
|
81
107
|
define_method("assign_new_instance") do
|
82
|
-
|
108
|
+
@instance = object.to_s.camelize.constantize.new(params[object])
|
109
|
+
end
|
110
|
+
|
111
|
+
# query string params can be used to filter collections
|
112
|
+
#
|
113
|
+
# filters apply on associated collections using the following conventions:
|
114
|
+
# where[user][category]=Expert
|
115
|
+
# where[user][created_at][gt]=20130807T12:34:56.789Z
|
116
|
+
#
|
117
|
+
# filters can be constructed with AND and OR behavior
|
118
|
+
# where[tags][id][in]=123,234,345 (OR)
|
119
|
+
# where[tags][id]=123&where[tags][id]=234 (AND)
|
120
|
+
define_method("assign_collection") do
|
121
|
+
@collection = object.to_s.camelize.constantize
|
122
|
+
if params[:include].kind_of?(Hash)
|
123
|
+
for assoc in params[:include].keys
|
124
|
+
@collection = @collection.includes(assoc.to_sym)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
if params[:include].kind_of?(String)
|
128
|
+
@collection = @collection.includes(params[:include].split(",").map(&:to_sym))
|
129
|
+
end
|
130
|
+
for assoc in (params[:where].keys rescue [])
|
131
|
+
attrs = params[:where][assoc]
|
132
|
+
if attrs.kind_of?(Hash)
|
133
|
+
for target_column in attrs.keys
|
134
|
+
op = :eq if attrs[target_column].kind_of?(String)
|
135
|
+
op ||= attrs[target_column].keys[0].to_sym
|
136
|
+
value = attrs[target_column] if op==:eq
|
137
|
+
value ||= is_time_column?(target_column) ? Time.parse(attrs[target_column][op]) : attrs[target_column][op]
|
138
|
+
unless assoc.to_sym==object.to_s.pluralize.to_sym
|
139
|
+
@collection = @collection.includes(assoc)
|
140
|
+
end
|
141
|
+
puts "op: #{op}"
|
142
|
+
if op==:gt
|
143
|
+
@collection = @collection.where("#{assoc}.#{target_column} > ?",value)
|
144
|
+
elsif op==:lt
|
145
|
+
@collection = @collection.where("#{assoc}.#{target_column} < ?",value)
|
146
|
+
elsif op==:in
|
147
|
+
@collection = @collection.where("#{assoc}.#{target_column} IN (?)",value)
|
148
|
+
elsif op==:eq
|
149
|
+
@collection = @collection.where(assoc => { target_column => value })
|
150
|
+
end
|
151
|
+
end
|
152
|
+
else
|
153
|
+
@collection = @collection.includes(assoc).where(assoc => attrs)
|
154
|
+
end
|
155
|
+
end
|
83
156
|
end
|
84
157
|
|
85
|
-
|
86
|
-
|
158
|
+
# designed to traverse an entire hash, replacing delimited strings with arrays of symbols
|
159
|
+
define_method("deep_split") do |hash={},pivot=','|
|
160
|
+
Hash[hash.map {|k,v| [k.to_sym,v.kind_of?(String) ? v.split(pivot).map(&:to_sym) : (v.kind_of?(Hash) ? deep_split(v,pivot) : v)]}]
|
87
161
|
end
|
88
|
-
end
|
89
162
|
|
163
|
+
define_method("is_time_column?") do |column|
|
164
|
+
!!column[-3,3]=='_at'
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
90
168
|
end
|
91
169
|
|
92
170
|
end
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serviceable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
8
|
+
- 4
|
9
|
+
version: "0.4"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Aubrey Goodman
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2013-
|
17
|
+
date: 2013-08-08 00:00:00 Z
|
18
18
|
dependencies: []
|
19
19
|
|
20
20
|
description: Decorate your controller classes with acts_as_service :model_name, and instantly support JSON/XML CRUD interface. Override any callback method to customize your endpoint. Allow client to specify response contents using query string filter parameters.
|