flexible_api 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/flexible_api/no_such_request_level_error.rb +15 -0
- data/lib/flexible_api/request_level.rb +118 -0
- data/lib/flexible_api/version.rb +5 -0
- data/lib/flexible_api.rb +86 -0
- data/spec/spec_helper.rb +9 -0
- metadata +112 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
module FlexibleApi
|
2
|
+
|
3
|
+
class RequestLevel
|
4
|
+
|
5
|
+
def initialize(name, klass)
|
6
|
+
@name = name
|
7
|
+
@klass = klass
|
8
|
+
@display_fields = Set.new
|
9
|
+
@select_fields = Set.new
|
10
|
+
@includes = []
|
11
|
+
@notations = {}
|
12
|
+
@eaten_levels = []
|
13
|
+
@select_fields << "`#{@klass.table_name}`.#{@klass.primary_key}" # auto-select primary key
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :display_fields, :name, :notations
|
17
|
+
|
18
|
+
def to_hash
|
19
|
+
{
|
20
|
+
:name => @name,
|
21
|
+
:fields => select_field + notations.keys,
|
22
|
+
:includes => @includes.map do |inc|
|
23
|
+
{ :name => inc[:name], :type => inc[:association].name.to_s.pluralize.underscore.downcase, :request_level => inc[:request_level].name }
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def eat_level(name)
|
29
|
+
@eaten_levels << @klass.find_level(name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def notation(notation_name, options = {}, &block)
|
33
|
+
options.assert_valid_keys :requires
|
34
|
+
requires *options[:requires]
|
35
|
+
@notations[notation_name] = block
|
36
|
+
end
|
37
|
+
|
38
|
+
def includes(association_name, options = {})
|
39
|
+
options.assert_valid_keys :request_level, :as, :requires
|
40
|
+
association = @klass.reflect_on_all_associations.detect { |a| a.name == association_name.to_sym }
|
41
|
+
raise "No such association on #{@klass.name}: #{association_name}" if association.nil? # TODO
|
42
|
+
# Allow requires to pass in
|
43
|
+
requires *options[:requires] if options.has_key?(:requires)
|
44
|
+
# Set the include options
|
45
|
+
@includes << {
|
46
|
+
:name => options[:as] || association_name,
|
47
|
+
:association => association,
|
48
|
+
:request_level => association.klass.find_level(options[:request_level])
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def requires(*requires_array)
|
53
|
+
requires_array.each do |field|
|
54
|
+
if field.is_a?(String)
|
55
|
+
@select_fields << field
|
56
|
+
else
|
57
|
+
@select_fields << "`#{@klass.table_name}`.#{field}" if @klass.columns_hash.keys.include?(field.to_s)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def all_fields
|
63
|
+
fields *@klass.columns_hash.keys.map(&:to_sym)
|
64
|
+
end
|
65
|
+
|
66
|
+
def fields(*field_array)
|
67
|
+
field_array.each do |field|
|
68
|
+
if field.is_a?(String)
|
69
|
+
@display_fields << field.split('.').last.to_sym
|
70
|
+
@select_fields << field
|
71
|
+
else
|
72
|
+
@display_fields << field
|
73
|
+
@select_fields << "`#{@klass.table_name}`.#{field}" if @klass.columns_hash.keys.include?(field.to_s)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#################################################
|
79
|
+
|
80
|
+
def select_field
|
81
|
+
@select_field_array ||= begin
|
82
|
+
selects = @select_fields.to_a
|
83
|
+
@eaten_levels.each { |l| selects.concat l.select_field }
|
84
|
+
selects
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def include_field
|
89
|
+
@include_field_array ||= begin
|
90
|
+
includes = @includes.map { |i| i[:association].name }
|
91
|
+
@eaten_levels.each { |l| includes.concat l.include_field }
|
92
|
+
includes
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def receive(item)
|
97
|
+
return nil if item.nil? # method may be nil
|
98
|
+
attributes = {}
|
99
|
+
@eaten_levels.each do |level|
|
100
|
+
attributes.merge! level.receive(item)
|
101
|
+
end
|
102
|
+
@display_fields.each do |field|
|
103
|
+
attributes[field] = item.send(field)
|
104
|
+
end
|
105
|
+
@includes.each do |include|
|
106
|
+
value = item.send(include[:association].name)
|
107
|
+
value = value.is_a?(Enumerable) ? value.map { |e| include[:request_level].receive(e) } : include[:request_level].receive(value)
|
108
|
+
attributes[include[:name]] = value
|
109
|
+
end
|
110
|
+
@notations.each do |name, block|
|
111
|
+
attributes[name] = item.instance_eval(&block)
|
112
|
+
end
|
113
|
+
attributes
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
data/lib/flexible_api.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module FlexibleApi
|
2
|
+
|
3
|
+
autoload :RequestLevel, File.dirname(__FILE__) + '/flexible_api/request_level'
|
4
|
+
autoload :NoSuchRequestLevelError, File.dirname(__FILE__) + '/flexible_api/no_such_request_level_error'
|
5
|
+
|
6
|
+
@@flexible_models = []
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.send(:extend, ClassMethods)
|
10
|
+
base.send(:include, InstanceMethods)
|
11
|
+
@@flexible_models << base
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.flexible_models
|
15
|
+
@@flexible_models
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
def request_levels
|
21
|
+
@levels.nil? ? [] : @levels.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
# Define a request level for this class
|
25
|
+
# Takes a name, and a block which defined the request level
|
26
|
+
def define_request_level(name, &block)
|
27
|
+
level = RequestLevel.new(name, self)
|
28
|
+
level.instance_eval &block
|
29
|
+
@levels ||= {}
|
30
|
+
@levels[name] = level
|
31
|
+
end
|
32
|
+
|
33
|
+
# Find a single element and load it at the given request level
|
34
|
+
def find_hash(id, options = {})
|
35
|
+
options.assert_valid_keys(:request_level)
|
36
|
+
level = find_level(options[:request_level])
|
37
|
+
record = self.find(id, :select => level.select_field.join(', '), :include => level.include_field)
|
38
|
+
level.receive record
|
39
|
+
end
|
40
|
+
|
41
|
+
# Find all of an element (or association) and load it at the given request level
|
42
|
+
def find_all_hash(options = {})
|
43
|
+
options.assert_valid_keys(:request_level)
|
44
|
+
level = find_level(options[:request_level])
|
45
|
+
records = self.all(:select => level.select_field.join(', '), :include => level.include_field)
|
46
|
+
records.map { |r| level.receive(r) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_request_level(level_name)
|
50
|
+
@default_request_level_name = level_name
|
51
|
+
end
|
52
|
+
|
53
|
+
# Find a given level by name and return the request level
|
54
|
+
def find_level(name = nil)
|
55
|
+
@levels ||= {}
|
56
|
+
level = name.nil? ? load_default_request_level : @levels[name.to_sym]
|
57
|
+
raise NoSuchRequestLevelError.new(name, self.name) if level.nil?
|
58
|
+
level
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def load_default_request_level
|
64
|
+
@default_request_level ||=
|
65
|
+
if @default_request_level_name.nil?
|
66
|
+
level = RequestLevel.new(:default, self)
|
67
|
+
level.all_fields
|
68
|
+
level
|
69
|
+
else
|
70
|
+
self.find_level(@default_request_level_name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
module InstanceMethods
|
77
|
+
|
78
|
+
# Return a hash of this element at the given request level (by name)
|
79
|
+
def to_hash(level_name)
|
80
|
+
level = self.class.find_level level_name
|
81
|
+
level.receive self
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
ActiveRecord::Base.establish_connection(:database => 'spec/test.db', :adapter => 'sqlite3')
|
5
|
+
|
6
|
+
# require 'logger'
|
7
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
8
|
+
|
9
|
+
require File.dirname(__FILE__) + '/../lib/flexible_api'
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flexible_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- John Crepezzi
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-28 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: sqlite3
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: activerecord
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
description: API for making APIs
|
64
|
+
email: john.crepezzi@gmail.com
|
65
|
+
executables: []
|
66
|
+
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files: []
|
70
|
+
|
71
|
+
files:
|
72
|
+
- lib/flexible_api/no_such_request_level_error.rb
|
73
|
+
- lib/flexible_api/request_level.rb
|
74
|
+
- lib/flexible_api/version.rb
|
75
|
+
- lib/flexible_api.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://github.com/seejohnrun/flexible_api
|
79
|
+
licenses: []
|
80
|
+
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project: flexible_api
|
107
|
+
rubygems_version: 1.6.2
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: A flexible API for making APIs
|
111
|
+
test_files:
|
112
|
+
- spec/spec_helper.rb
|