Class: Nested::Resource

Inherits:
Object
  • Object
show all
Defined in:
lib/nested.rb

Constant Summary collapse

SERIALIZE =
->(obj, ctrl, resource) do
  obj
end
FETCH =
->(resource, ctrl) do
  raise "implement fetch for resource #{resource.name}"  unless resource.parent
  raise "implement fetch for singleton #{resource.name}" if resource.singleton?

  parent_resource = resource.parent
  parent_obj = ctrl.instance_variable_get("@#{parent_resource.instance_variable_name}")

  if resource.name
    scope = parent_obj.send(resource.name.to_s.pluralize.to_sym)
    resource.collection? ? scope : scope.where(id: ctrl.params["#{resource.name.to_s.singularize}_id"]).first
  else
    parent_obj.where(id: ctrl.params["#{parent_resource.name.to_s.singularize}_id"]).first
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sinatra, name, singleton, collection, parent) ⇒ Resource

Returns a new instance of Resource.



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/nested.rb', line 39

def initialize(sinatra, name, singleton, collection, parent)
  raise SingletonAndCollectionError.new if singleton && collection
  raise NameMissingError.new if (singleton || collection) && !name

  @sinatra = sinatra
  @name = name
  @singleton = singleton
  @collection = collection
  @parent = parent
  @resources = []
  @actions = []
end

Instance Attribute Details

#actionsObject (readonly)

Returns the value of attribute actions.



37
38
39
# File 'lib/nested.rb', line 37

def actions
  @actions
end

#nameObject (readonly)

Returns the value of attribute name.



37
38
39
# File 'lib/nested.rb', line 37

def name
  @name
end

#parentObject (readonly)

Returns the value of attribute parent.



37
38
39
# File 'lib/nested.rb', line 37

def parent
  @parent
end

#resourcesObject (readonly)

Returns the value of attribute resources.



37
38
39
# File 'lib/nested.rb', line 37

def resources
  @resources
end

Instance Method Details

#after_fetch(&block) ⇒ Object



66
# File 'lib/nested.rb', line 66

def after_fetch(&block);    @__after_fetch  = block;  end

#before_fetch(&block) ⇒ Object



64
# File 'lib/nested.rb', line 64

def before_fetch(&block);   @__before_fetch = block;  end

#child_resource(name, singleton, collection, &block) ⇒ Object



125
126
127
128
129
# File 'lib/nested.rb', line 125

def child_resource(name, singleton, collection, &block)
   Resource.new(@sinatra, name, singleton, collection, self)
    .tap{|r| r.instance_eval(&block)}
    .tap{|r| @resources << r}
end

#collection?Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/nested.rb', line 60

def collection?
  @collection == true
end

#create_sinatra_route(method, action, &block) ⇒ Object



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/nested.rb', line 228

def create_sinatra_route(method, action, &block)
  @actions << {method: method, action: action}

  resource = self

  route = resource.route({}, action)
  puts "sinatra router [#{method}] #{@sinatra.nested_config[:prefix]}#{route}"

  @sinatra.send(method, route) do
    content_type :json

    resource.self_and_parents.reverse.each do |res|
      res.sinatra_init(self)
    end

    resource.send(:"sinatra_exec_#{method}_block", self, &block)

    resource.sinatra_response(self)
  end
end

#delete(action = nil, &block) ⇒ Object



107
108
109
# File 'lib/nested.rb', line 107

def delete(action=nil, &block)
  create_sinatra_route :delete, action, &block
end

#fetch(&block) ⇒ Object



65
# File 'lib/nested.rb', line 65

def fetch(&block);          @__fetch        = block;  end

#fetcherObject



149
150
151
# File 'lib/nested.rb', line 149

def fetcher
  @__fetch || FETCH
end

#get(action = nil, &block) ⇒ Object



95
96
97
# File 'lib/nested.rb', line 95

def get(action=nil, &block)
  create_sinatra_route :get, action, &(block||proc {})
end

#instance_variable_nameObject



131
132
133
134
135
136
137
138
139
# File 'lib/nested.rb', line 131

def instance_variable_name
  if @name
    @name.to_s.send(collection? ? :pluralize : :singularize).to_sym
  elsif member? && @parent
    @parent.name.to_s.singularize.to_sym
  else
    nil
  end
end

#many(name, &block) ⇒ Object



115
116
117
118
# File 'lib/nested.rb', line 115

def many(name, &block)
  raise ManyInManyError.new "do not nest many in many" if collection?
  child_resource(name, false, true, &block)
end

#member?Boolean

Returns:

  • (Boolean)


56
57
58
# File 'lib/nested.rb', line 56

def member?
  !singleton? && !collection?
end

#one(name = nil, &block) ⇒ Object



120
121
122
123
# File 'lib/nested.rb', line 120

def one(name=nil, &block)
  raise OneWithNameInManyError.new("call one (#{name}) without name argument when nested in a many (#{@name})") if name && collection?
  child_resource(name, false, false, &block)
end

#parentsObject



141
142
143
# File 'lib/nested.rb', line 141

def parents
  (@parent ? @parent.parents + [@parent] : [])
end

#post(action = nil, &block) ⇒ Object



99
100
101
# File 'lib/nested.rb', line 99

def post(action=nil, &block)
  create_sinatra_route :post, action, &block
end

#put(action = nil, &block) ⇒ Object



103
104
105
# File 'lib/nested.rb', line 103

def put(action=nil, &block)
  create_sinatra_route :put, action, &block
end

#route(args = {}, action = nil) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/nested.rb', line 69

def route(args={}, action=nil)
  "".tap do |r|
    r << @parent.route(args) if @parent

    if singleton?
      r << "/" + @name.to_s.singularize
    elsif collection?
      r << "/" + @name.to_s.pluralize
    else
      if @name
        r << "/" + @name.to_s.pluralize
      end
      r << "/"
      key = ((@name || @parent.name).to_s.singularize + "_id").to_sym

      if args.key?(key)
        r << args[key].to_s
      else
        r << ":#{key}"
      end
    end

    r << "/#{action}" if action
  end
end

#self_and_parentsObject



145
146
147
# File 'lib/nested.rb', line 145

def self_and_parents
  (self.parents + [self]).reverse
end

#serialize(&block) ⇒ Object



67
# File 'lib/nested.rb', line 67

def serialize(&block);      @__serialize    = block;  end

#serializerObject



153
154
155
# File 'lib/nested.rb', line 153

def serializer
  @__serialize || SERIALIZE
end

#sinatra_exec_delete_block(sinatra, &block) ⇒ Object



173
174
175
# File 'lib/nested.rb', line 173

def sinatra_exec_delete_block(sinatra, &block)
  sinatra.instance_exec(self, &block)
end

#sinatra_exec_get_block(sinatra, &block) ⇒ Object



169
170
171
# File 'lib/nested.rb', line 169

def sinatra_exec_get_block(sinatra, &block)
  sinatra.instance_exec(self, &block)
end

#sinatra_exec_post_block(sinatra, &block) ⇒ Object



187
188
189
190
191
# File 'lib/nested.rb', line 187

def sinatra_exec_post_block(sinatra, &block)
  data = sinatra_read_json_body(sinatra)
  res = sinatra.instance_exec(data, self, &block)
  sinatra.instance_variable_set("@#{self.instance_variable_name}", res)
end

#sinatra_exec_put_block(sinatra, &block) ⇒ Object



182
183
184
185
# File 'lib/nested.rb', line 182

def sinatra_exec_put_block(sinatra, &block)
  data = sinatra_read_json_body(sinatra)
  sinatra.instance_exec(data, self, &block)
end

#sinatra_init(sinatra) ⇒ Object




159
160
161
162
163
164
165
166
167
# File 'lib/nested.rb', line 159

def sinatra_init(sinatra)
  @__before_fetch.call(self, sinatra) if @__before_fetch
  resource_obj = fetcher.call(self, sinatra)

  puts "set @#{self.instance_variable_name} to #{resource_obj.inspect} for #{sinatra}"
  sinatra.instance_variable_set("@#{self.instance_variable_name}", resource_obj)

  @__after_fetch.call(self, sinatra) if @__after_fetch
end

#sinatra_read_json_body(sinatra) ⇒ Object



177
178
179
180
# File 'lib/nested.rb', line 177

def sinatra_read_json_body(sinatra)
  sinatra.request.body.rewind
  HashWithIndifferentAccess.new JSON.parse(sinatra.request.body.read)
end

#sinatra_response(sinatra) ⇒ Object



197
198
199
200
201
202
203
204
205
# File 'lib/nested.rb', line 197

def sinatra_response(sinatra)
  response = sinatra.instance_variable_get("@#{self.instance_variable_name}")
  response = self.send(:"sinatra_response_create_#{sinatra_response_type(response)}", sinatra, response)

  case response
    when String then  response
    else              response.to_json
  end
end

#sinatra_response_create_data(sinatra, response) ⇒ Object



207
208
209
210
211
212
213
214
215
# File 'lib/nested.rb', line 207

def sinatra_response_create_data(sinatra, response)
  data = if response.respond_to?(:to_a)
    response.to_a.map{|e| serializer.call(e, sinatra, self)}
  else
    serializer.call(response, sinatra, self)
  end

  {data: data, ok: true}
end

#sinatra_response_create_error(sinatra, response) ⇒ Object



217
218
219
220
221
222
223
224
225
226
# File 'lib/nested.rb', line 217

def sinatra_response_create_error(sinatra, response)
  errors = response.is_a?(ActiveModel::Errors) ? response : response.errors

  data = errors.to_hash.inject({}) do |memo, e|
    memo[e[0]] = e[1][0]
    memo
  end

  {data: data, ok: false}
end

#sinatra_response_type(response) ⇒ Object



193
194
195
# File 'lib/nested.rb', line 193

def sinatra_response_type(response)
  (response.is_a?(ActiveModel::Errors) || (response.respond_to?(:errors) && !response.errors.empty?)) ? :error : :data
end

#singleton(name, &block) ⇒ Object



111
112
113
# File 'lib/nested.rb', line 111

def singleton(name, &block)
  child_resource(name, true, false, &block)
end

#singleton?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/nested.rb', line 52

def singleton?
  @singleton == true
end