flexible_api 0.0.1

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.
@@ -0,0 +1,15 @@
1
+ module FlexibleApi
2
+
3
+ class NoSuchRequestLevelError < StandardError
4
+
5
+ def initialize(rl_name, klass_name)
6
+ @message = "There is no request level '#{rl_name}' for #{klass_name}"
7
+ end
8
+
9
+ def message
10
+ @message
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -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
@@ -0,0 +1,5 @@
1
+ module FlexibleApi
2
+
3
+ VERSION = '0.0.1'
4
+
5
+ end
@@ -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
@@ -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