datapathy 0.6.0

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,6 @@
1
+ class Service
2
+ include Datapathy::Model
3
+
4
+
5
+ end
6
+
@@ -0,0 +1,141 @@
1
+ require 'active_support/basic_object'
2
+ require 'active_support/core_ext/module/delegation'
3
+
4
+ class Datapathy::Query
5
+
6
+ attr_reader :model, :conditions,
7
+ :offset, :count, :instrumenter
8
+
9
+ def initialize(model, conditions = {}, &blk)
10
+ @model = model
11
+ @conditions = ConditionSet.new
12
+ @blocks = []
13
+ @instrumenter = ActiveSupport::Notifications.instrumenter
14
+ add(conditions, &blk)
15
+ end
16
+
17
+ def add(conditions = {}, &blk)
18
+ add_conditions_hash(conditions)
19
+ add_conditions(&blk) if block_given?
20
+ end
21
+
22
+ def add_conditions(&blk)
23
+ @blocks << blk
24
+ yield @conditions
25
+ end
26
+
27
+ def add_conditions_hash(conditions = {})
28
+ conditions.each do |k,v|
29
+ add_conditions { |q| q.send(k) == v }
30
+ end
31
+ end
32
+
33
+ def key_lookup?
34
+ @conditions.size == 1 &&
35
+ (@conditions.first.operation == :key || @conditions.first.operation == model.key) &&
36
+ @conditions.first.then.operation == :==
37
+ end
38
+
39
+ def key
40
+ @conditions.first.then.arguments.first
41
+ end
42
+
43
+ def initialize_and_filter(records)
44
+ filter(initialize_resources(records))
45
+ end
46
+
47
+ def initialize_resources(records)
48
+ return records if records.first.is_a?(Datapathy::Model)
49
+ records.map { |record|
50
+ resource = model.new(record)
51
+ resource.new_record = false
52
+ resource
53
+ }
54
+ end
55
+
56
+ def filter(resources)
57
+ resources = match_resources(resources)
58
+ resources = order_resources(resources)
59
+ resources = limit_resources(resources)
60
+
61
+ resources
62
+ end
63
+
64
+ def match_resources(resources)
65
+ resources.select do |record|
66
+ @blocks.all? do |block|
67
+ block.call(record)
68
+ end
69
+ end
70
+ end
71
+
72
+ def order_resources(resources)
73
+ resources
74
+ end
75
+
76
+ def limit_resources(resources)
77
+ return resources unless @offset || @count
78
+ resources.slice(@offset || 0, @count)
79
+ end
80
+
81
+ def limit(count, offset = 0)
82
+ @count, @offset = count, offset
83
+ end
84
+
85
+ def to_s
86
+ string = ""
87
+ string << @conditions.inspect
88
+ string << " limit #{@limit}" if @limit
89
+ string << " offset #{@offset}" if @offset && @offset > 0
90
+ string
91
+ end
92
+
93
+
94
+ class ConditionSet < ActiveSupport::BasicObject
95
+
96
+ delegate :size, :first, :to => :@conditions
97
+
98
+ def initialize
99
+ @conditions = []
100
+ end
101
+
102
+ def method_missing(method_name, *args, &blk)
103
+ condition = Condition.new(method_name, *args, &blk)
104
+ @conditions << condition
105
+ condition
106
+ end
107
+
108
+ def inspect
109
+ @conditions.inspect
110
+ end
111
+ end
112
+
113
+ class Condition < ActiveSupport::BasicObject
114
+
115
+ attr_reader :then
116
+ attr_accessor :operation, :arguments, :block
117
+
118
+ def initialize(operation, *arguments, &block)
119
+ @operation = operation
120
+ @arguments = arguments unless arguments.empty?
121
+ @block = block if block
122
+ end
123
+
124
+ def method_missing(method_name, *args, &blk)
125
+ @then = Condition.new(method_name, *args, &blk)
126
+ end
127
+
128
+ def respond_to?(arg)
129
+ false
130
+ end
131
+
132
+ def inspect
133
+ string = operation.to_s
134
+ string << "#{@arguments.inspect}" unless @arguments.nil?
135
+ string << " #{@then.inspect}" if @then
136
+ string
137
+ end
138
+
139
+ end
140
+
141
+ end
@@ -0,0 +1,48 @@
1
+ require 'rails'
2
+ require 'active_model/railtie'
3
+
4
+ require 'action_controller/railtie'
5
+
6
+ module Datapathy
7
+ class Railtie < Rails::Railtie
8
+
9
+ initializer "datapathy.log_runtime" do |app|
10
+ ActiveSupport.on_load(:action_controller) do
11
+ include Datapathy::Railties::ControllerRuntime
12
+ end
13
+ end
14
+ end
15
+
16
+ require 'active_support/core_ext/module/attr_internal'
17
+ module Railties
18
+ module ControllerRuntime
19
+ extend ActiveSupport::Concern
20
+
21
+ attr_internal :query_runtime
22
+
23
+ def cleanup_view_runtime
24
+ runtime_before_render = Datapathy::LogSubscriber.reset_runtime
25
+ runtime = super
26
+ runtime_after_render = Datapathy::LogSubscriber.reset_runtime
27
+ self.query_runtime = runtime_before_render + runtime_after_render
28
+ runtime - runtime_after_render
29
+ end
30
+
31
+ def append_info_to_payload(payload)
32
+ super
33
+ payload[:query_runtime] = self.query_runtime
34
+ end
35
+
36
+ module ClassMethods
37
+ def log_process_action(payload)
38
+ messages, query_runtime = super, payload[:query_runtime]
39
+ messages << ("Datapathy: %.1fms" % query_runtime.to_f) if query_runtime
40
+ messages
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+
47
+ end
48
+
@@ -0,0 +1,62 @@
1
+ events: process_time
2
+
3
+ fl=/home/rando/development/api/datapathy/(eval)
4
+ fn=DatapathyModel::text=
5
+ 5 110000
6
+
7
+ fl=/home/rando/development/api/datapathy/(eval)
8
+ fn=DatapathyModel::title=
9
+ 5 60000
10
+
11
+ fl=/home/rando/development/api/datapathy/(eval)
12
+ fn=DatapathyModel::id=
13
+ 5 80000
14
+
15
+ fl=/home/rando/development/api/datapathy/lib/datapathy/model.rb
16
+ fn=Datapathy::Model::initialize
17
+ 8 390000
18
+ cfl=/home/rando/development/api/datapathy/(eval)
19
+ cfn=DatapathyModel::id=
20
+ calls=100000 12
21
+ 12 80000
22
+ cfl=/home/rando/development/api/datapathy/(eval)
23
+ cfn=DatapathyModel::title=
24
+ calls=100000 13
25
+ 13 60000
26
+ cfl=/home/rando/development/api/datapathy/(eval)
27
+ cfn=DatapathyModel::text=
28
+ calls=100000 14
29
+ 14 110000
30
+
31
+ fl=/home/rando/development/api/datapathy/ruby_runtime
32
+ fn=<Class::BasicObject>::allocate
33
+ 0 50000
34
+
35
+ fl=/home/rando/development/api/datapathy/ruby_runtime
36
+ fn=Class::new
37
+ 0 120000
38
+ cfl=/home/rando/development/api/datapathy/ruby_runtime
39
+ cfn=<Class::BasicObject>::allocate
40
+ calls=100000 19
41
+ 19 50000
42
+ cfl=/home/rando/development/api/datapathy/lib/datapathy/model.rb
43
+ cfn=Datapathy::Model::initialize
44
+ calls=100000 19
45
+ 19 640000
46
+
47
+ fl=/home/rando/development/api/datapathy/ruby_runtime
48
+ fn=Integer::times
49
+ 0 80000
50
+ cfl=/home/rando/development/api/datapathy/ruby_runtime
51
+ cfn=Class::new
52
+ calls=100000 19
53
+ 19 810000
54
+
55
+ fl=/home/rando/development/api/datapathy/profile/initialize.rb
56
+ fn=Global::[No method]
57
+ 18 0
58
+ cfl=/home/rando/development/api/datapathy/ruby_runtime
59
+ cfn=Integer::times
60
+ calls=1 18
61
+ 18 890000
62
+
@@ -0,0 +1,28 @@
1
+
2
+ require 'rubygems'
3
+
4
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
5
+ require 'datapathy'
6
+ class DatapathyModel
7
+ include Datapathy::Model
8
+
9
+ persists :id, :title, :text
10
+ end
11
+
12
+ ATTRS = {:id => 1, :title => "Foo", :text => "Bar"}
13
+
14
+ require 'ruby-prof'
15
+
16
+ # Profile the code
17
+ result = RubyProf.profile do
18
+ 100_000.times do
19
+ DatapathyModel.new(ATTRS)
20
+ end
21
+ end
22
+
23
+ # Print a graph profile to text
24
+ printer = RubyProf::CallTreePrinter.new(result)
25
+ File.open(File.dirname(__FILE__) + "/" + File.basename(__FILE__, ".rb") + ".calltree", "w") do |file|
26
+ printer.print(file, 0)
27
+ end
28
+
@@ -0,0 +1,146 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'Adapter API' do
4
+
5
+ share_as :ArrayOfHashes do
6
+ it 'should return an Array(Hash) of records' do
7
+ @results.should be_an(Array)
8
+ @results.should have(1).item
9
+
10
+ result = @results.first
11
+ result.should be_a(Hash)
12
+ end
13
+ end
14
+
15
+
16
+ before do
17
+ @adapter = Datapathy::Adapters::MemoryAdapter.new
18
+ end
19
+
20
+ describe "#create(collection)" do
21
+ before do
22
+ @article = Article.new(:id => new_uuid,
23
+ :title => "Whee!",
24
+ :text => "This is fun!")
25
+ @collection = Datapathy::Collection.new(@article)
26
+
27
+ @results = @adapter.create(@collection)
28
+ @result = @results.first
29
+ end
30
+
31
+ it_should_behave_like ArrayOfHashes
32
+
33
+ it 'should return the populated attributes' do
34
+ @result.should == @article.persisted_attributes
35
+ end
36
+ end
37
+
38
+ describe "#read(query)" do
39
+ before do
40
+ @article = Article.new(:id => new_uuid,
41
+ :title => "Whee!",
42
+ :text => "This is fun!")
43
+
44
+ @adapter.datastore[Article][@article.id] = @article.persisted_attributes
45
+
46
+ @query = Datapathy::Query.new(Article) { |q| q.title == @article.title }
47
+ @collection = Datapathy::Collection.new(@query)
48
+
49
+ @results = @adapter.read(@collection)
50
+ @result = @results.first
51
+ end
52
+
53
+ it_should_behave_like ArrayOfHashes
54
+
55
+ it 'should have the attributes' do
56
+ @result.should == @article.persisted_attributes
57
+ end
58
+ end
59
+
60
+ describe "#update(attributes, collection)" do
61
+ before do
62
+ @article = Article.new(:id => new_uuid,
63
+ :title => "Whee!",
64
+ :text => "This is fun!")
65
+
66
+ @adapter.datastore[Article][@article.id] = @article.persisted_attributes
67
+ end
68
+
69
+ describe "with loaded collection (update already retrived records)" do
70
+ before do
71
+ @collection = Datapathy::Collection.new(@article)
72
+ @collection.should be_loaded
73
+ @results = @adapter.update({:title => "Boo"}, @collection)
74
+ @result = @results.first
75
+ end
76
+
77
+ it_should_behave_like ArrayOfHashes
78
+
79
+ it 'should return the update attribute values' do
80
+ @result[:title].should == "Boo"
81
+ end
82
+ end
83
+
84
+ describe "with empty collection (update in bulk, without retrieval)" do
85
+ before do
86
+ @query = Datapathy::Query.new(Article) { |q| q.title == @article.title }
87
+ @collection = Datapathy::Collection.new(@query)
88
+ @collection.should_not be_loaded
89
+
90
+ @results = @adapter.update({:title => "Boo"}, @collection)
91
+ @result = @results.first
92
+ end
93
+
94
+ it_should_behave_like ArrayOfHashes
95
+
96
+ it 'should return an updated attribute values' do
97
+ @result[:title].should == "Boo"
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ describe "#delete(attributes)" do
104
+ before do
105
+ @article = Article.new(:id => new_uuid,
106
+ :title => "Whee!",
107
+ :text => "This is fun!")
108
+
109
+ @adapter.datastore[Article][@article.id] = @article.persisted_attributes
110
+ end
111
+
112
+ describe "with loaded collection (delete already retrived records)" do
113
+ before do
114
+ @collection = Datapathy::Collection.new(@article)
115
+ @collection.should be_loaded
116
+ @results = @adapter.delete(@collection)
117
+ @result = @results.first
118
+ end
119
+
120
+ it_should_behave_like ArrayOfHashes
121
+
122
+ it 'should return the remove attributes' do
123
+ @result.should == @article.persisted_attributes
124
+ end
125
+ end
126
+
127
+ describe "with empty collection (delete in bulk, without retrieval)" do
128
+ before do
129
+ @query = Datapathy::Query.new(Article) { |q| q.title == @article.title }
130
+ @collection = Datapathy::Collection.new(@query)
131
+ @collection.should_not be_loaded
132
+
133
+ @results = @adapter.delete(@collection)
134
+ @result = @results.first
135
+ end
136
+
137
+ it_should_behave_like ArrayOfHashes
138
+
139
+ it 'should return the removed attribute values' do
140
+ @result.should == @article.persisted_attributes
141
+ end
142
+ end
143
+
144
+ end
145
+
146
+ end
@@ -0,0 +1,71 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Creating models" do
4
+
5
+ share_as :CreatingARecord do
6
+ it 'should create a record' do
7
+ test_adapter.datastore[Article].should have(1).key
8
+ end
9
+
10
+ it 'should store persistable attributes' do
11
+ record = test_adapter.datastore[Article][@article.id]
12
+
13
+ record[:id].should eql(@article.id)
14
+ record[:title].should eql(@article.title)
15
+ record[:text].should eql(@article.text)
16
+ end
17
+
18
+ it 'should not store other attributes' do
19
+ record = test_adapter.datastore[Article][@article.id]
20
+
21
+ @article.should respond_to(:summary)
22
+ record.should_not have_key("summary")
23
+ end
24
+ end
25
+
26
+ describe 'Model.create' do
27
+ before do
28
+ pending
29
+ @article = Article.create(:id => new_uuid,
30
+ :title => "Datapathy is awesome!",
31
+ :text => "It just is!")
32
+ end
33
+
34
+ it_should_behave_like CreatingARecord
35
+
36
+ end
37
+
38
+ describe 'Model.new; #save' do
39
+ before do
40
+ pending
41
+ @article = Article.new(:id => new_uuid,
42
+ :title => "Datapathy is awesome!",
43
+ :text => "It just is!")
44
+ @article.save
45
+ end
46
+
47
+ it_should_behave_like CreatingARecord
48
+
49
+ end
50
+
51
+ describe 'Bulk Model.create' do
52
+ before do
53
+ pending
54
+ @articles = Article.create([
55
+ { :id => new_uuid, :title => "Article A", :text => "Foo"},
56
+ { :id => new_uuid, :title => "Article B", :text => "Bar"},
57
+ { :id => new_uuid, :title => "Article C", :text => "Baz"}
58
+ ])
59
+ end
60
+
61
+ it 'should create the records' do
62
+ test_adapter.datastore[Article].should have(@articles.size).keys
63
+ end
64
+
65
+ end
66
+
67
+ after do
68
+ test_adapter.clear!
69
+ end
70
+
71
+ end