Class: Nested::Resource

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

Constant Summary collapse

FETCH =
-> 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 = 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: params["#{@__resource.name.to_s.singularize}_id"]).first
  else
    parent_obj.where(id: 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.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/nested.rb', line 35

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 = []

  init &-> do
    fetched = instance_exec(&FETCH)

    puts "set @#{@__resource.instance_variable_name} to #{fetched.inspect} for #{self}"
    self.instance_variable_set("@#{@__resource.instance_variable_name}", fetched)
  end

  serialize &->(obj) do
    obj
  end
end

Instance Attribute Details

#actionsObject (readonly)

Returns the value of attribute actions.



33
34
35
# File 'lib/nested.rb', line 33

def actions
  @actions
end

#nameObject (readonly)

Returns the value of attribute name.



33
34
35
# File 'lib/nested.rb', line 33

def name
  @name
end

#parentObject (readonly)

Returns the value of attribute parent.



33
34
35
# File 'lib/nested.rb', line 33

def parent
  @parent
end

#resourcesObject (readonly)

Returns the value of attribute resources.



33
34
35
# File 'lib/nested.rb', line 33

def resources
  @resources
end

Instance Method Details

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



142
143
144
145
146
# File 'lib/nested.rb', line 142

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)


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

def collection?
  @collection == true
end

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



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/nested.rb', line 235

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



124
125
126
# File 'lib/nested.rb', line 124

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

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



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

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

#init(&block) ⇒ Object



82
83
84
# File 'lib/nested.rb', line 82

def init(&block)
  @__init = block
end

#instance_variable_nameObject



148
149
150
151
152
153
154
155
156
# File 'lib/nested.rb', line 148

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



132
133
134
135
# File 'lib/nested.rb', line 132

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)


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

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

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



137
138
139
140
# File 'lib/nested.rb', line 137

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



158
159
160
# File 'lib/nested.rb', line 158

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

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



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

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

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



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

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

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



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/nested.rb', line 86

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



162
163
164
# File 'lib/nested.rb', line 162

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

#serialize(*args, &block) ⇒ Object



71
72
73
74
75
76
77
78
79
80
# File 'lib/nested.rb', line 71

def serialize(*args, &block)
  raise "pass either *args or &block" if args.empty? && !block

  @__serialize = ->(obj) do
    obj = self.instance_exec(obj, &block) if block
    obj = obj.attributes if obj.is_a?(ActiveRecord::Base)
    obj = obj.symbolize_keys.slice(*args) unless args.empty?
    obj
  end
end

#sinatra_exec_delete_block(sinatra, &block) ⇒ Object



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

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

#sinatra_exec_get_block(sinatra, &block) ⇒ Object



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

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

#sinatra_exec_post_block(sinatra, &block) ⇒ Object



194
195
196
197
198
# File 'lib/nested.rb', line 194

def sinatra_exec_post_block(sinatra, &block)
  sinatra_init_data(sinatra, &block)
  res = sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
  sinatra.instance_variable_set("@#{self.instance_variable_name}", res)
end

#sinatra_exec_put_block(sinatra, &block) ⇒ Object



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

def sinatra_exec_put_block(sinatra, &block)
  sinatra_init_data(sinatra, &block)
  sinatra.instance_exec(*sinatra.instance_variable_get("@__data"), &block)
end

#sinatra_init(sinatra) ⇒ Object




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

def sinatra_init(sinatra)
  sinatra.instance_variable_set("@__resource", self)
  sinatra.instance_exec(&@__init)
end

#sinatra_init_data(sinatra, &block) ⇒ Object



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

def sinatra_init_data(sinatra, &block)
  sinatra.request.body.rewind
  raw_data = HashWithIndifferentAccess.new(JSON.parse(sinatra.request.body.read))

  sinatra.instance_variable_set("@__raw_data", raw_data)
  sinatra.instance_variable_set("@__data", raw_data.values_at(*block.parameters.map(&:last)))
end

#sinatra_response(sinatra) ⇒ Object



204
205
206
207
208
209
210
211
212
# File 'lib/nested.rb', line 204

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



214
215
216
217
218
219
220
221
222
# File 'lib/nested.rb', line 214

def sinatra_response_create_data(sinatra, response)
  data = if response && collection?
    response.to_a.map{|e| sinatra.instance_exec(e, &@__serialize) }
  else
    sinatra.instance_exec(response, &@__serialize)
  end

  {data: data, ok: true}
end

#sinatra_response_create_error(sinatra, response) ⇒ Object



224
225
226
227
228
229
230
231
232
233
# File 'lib/nested.rb', line 224

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



200
201
202
# File 'lib/nested.rb', line 200

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

#singleton(name, &block) ⇒ Object



128
129
130
# File 'lib/nested.rb', line 128

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

#singleton?Boolean

Returns:

  • (Boolean)


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

def singleton?
  @singleton == true
end