immanence 0.0.2 → 0.1.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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MDcyOTJhNmQ0NTc5MDU4MjhkYTVmMGJiNTRmZjU5MWU4NTRmNzY3ZQ==
4
+ MTZkMWFhOGNhODAxOGMyNzYyNTcwNDBhMmEzZWVjNTc3ZTk4ODJlYQ==
5
5
  data.tar.gz: !binary |-
6
- MDJkZmNlNGQ1ZDQ5MDJiMjRhMTIxMjAyNTdmMGM0NWU0ZmYyN2RlOQ==
6
+ YTA5MWZmZDk3NjY2NTc4ZmVhMmNkMjk0Yzg1ZGIwMzVhMDVkMDMzNg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- N2IzMTc4Mjg4Y2Y4MDM3MTBjYzYyOTA0MGI3NDZkN2RkOWE4NWFmYjcxNzNk
10
- ZDFmMzQyNmUzYzQ0ZDQ2ZGIxYTE1MjliZDZiZjYyNGUwYjhiNDA3Mzg1Y2E1
11
- MmIwODhhMzI2MTliZTgwYjE0NGU0MmNiNjNhZDNkZjQ0YzMxYjY=
9
+ MTUzNjdjMTQ1NmI5NmNiYjdlYTdkNmVhNWJlMzVkNjMzYjEwMGY0MjdlNDk5
10
+ NzQwYzU5ODZkNmIxYTM2N2I5MWRhMjM3OGYwODExYzJhZmQ4ZWZmZWZmZjA4
11
+ Yzg3NjI4NDc5M2YwYjA0N2ZiYjZkZDQ3NjBiNmNmYjZiOGFhNDk=
12
12
  data.tar.gz: !binary |-
13
- NjE0MGRkN2I2NDhjZGRjNmU4YzM1ODVjNDYzMWIwNDAzNmUxN2M1NjliMjE2
14
- MmNjYzNkMWQ5ZWE0ZTg0NzRkOTQyYmJhYTRhYmZkY2NkZjk3ZmQ3M2YwOTIx
15
- ZDQwZDIzYmY2MTI2OWY1NDJlOGUyNTJjNTQwZDQ4NDc2NTJkNzU=
13
+ Zjk3ZGJmZmI4YjA3OTljMjBkNmU2ZDBmNjJlNzgwNjE4ZGJmZGI0NDEwNzQy
14
+ YjFiMzE1MDgyNWRmNzE0Mjc5ZjYyMzcxYTg2YWQ3OWY3MTg2OWY2ZWM0NjEw
15
+ OGI4MjUzYjU1MTlhMzVlZDdlZmJjNjYwNGQ0NmEyZDMyOTc0NDE=
data/README.md CHANGED
@@ -9,18 +9,20 @@
9
9
  >
10
10
  > —Arakawa and Madeline Gins
11
11
 
12
- **Immanence** is a DSL for handling web requests in Ruby, built on top of Rack. To execute routes it calculates the [Levenshtein distance](http://en.wikipedia.org/wiki/Levenshtein_distance) between incoming requests and routes defined in the DSL then executes the most likely candidate. *Something will always be executed.* Objects are rendered by calling `>>` on `self` with the object to be rendered as the argument. Objects are serialized into JSON via Oj. Incoming JSON is automatically parsed and available in the `@request` object.
12
+ **Immanence** is a DSL for handling web requests in Ruby, built on top of Rack. To execute routes it calculates the [Levenshtein distance](http://en.wikipedia.org/wiki/Levenshtein_distance) between incoming requests and routes defined through the DSL then executes the most likely candidate. *Something will always be executed.* Objects are rendered by calling `→` (alias `render`) with the object to be rendered as the argument. Object responses are encoded as JSON. Incoming JSON is automatically parsed and available in as the `input` object.
13
13
 
14
- This is less a useful piece of software and more of a stylistic exercise.
14
+ What does it mean to discard transparent precision in favor of immanent promiscuity?
15
15
 
16
16
  ```ruby
17
- class Application < Immanence::Control
17
+ # encoding: UTF-8
18
+
19
+ class Application < Immanent::Control
18
20
  route :get, "/fields/:id" do
19
- self >> Field.find(@params[:id])
21
+ Field.find(params[:id])
20
22
  end
21
23
 
22
24
  route :post, "/fields" do
23
- self >> Field.create(@request.input)
25
+ Field.create(input)
24
26
  end
25
27
  end
26
28
  ```
data/Rakefile CHANGED
@@ -4,3 +4,12 @@ desc "Open an irb session preloaded with this library"
4
4
  task :console do
5
5
  sh "irb -rubygems -I lib -r immanence.rb"
6
6
  end
7
+
8
+ desc "Run specs"
9
+ task :spec do
10
+ require "rspec/core/rake_task"
11
+
12
+ RSpec::Core::RakeTask.new(:spec) do |spec|
13
+ spec.rspec_opts = %w{--colour --format progress}
14
+ end
15
+ end
@@ -8,77 +8,98 @@ require "core_ext/hash"
8
8
  require "core_ext/object"
9
9
 
10
10
  module Immanence
11
- class Request < Struct.new(:verb, :path, :input); end
11
+ class Request
12
+ attr_reader :input
13
+
14
+ def initialize(request, input)
15
+ @__request__ = request
16
+ @input = input
17
+ end
18
+
19
+ def method_missing(method, *args, &block)
20
+ @__request__.send(method, *args, &block)
21
+ end
22
+ end # Request
12
23
 
13
24
  class Control
14
25
  I = λ { |i| Oj.load(i) }
15
26
  O = λ { |o| Oj.dump(o, mode: :compat) }
16
27
 
17
- LEVENSHTEIN = λ { |a, b|
18
- mx = [(0..a.size).to_a]
19
-
20
- (1..b.size).each do |j|
21
- mx << [j] + [0] * (a.size)
22
- end
28
+ LEVENSHTEIN = λ do |a, b|
29
+ [(0..a.size).to_a].tap { |mx|
30
+ (1..b.size).each do |j|
31
+ mx << [j] + [0] * (a.size)
32
+ end
23
33
 
24
- (1..b.size).each do |i|
25
- (1..a.size).each do |j|
26
- if a[j-1] == b[i-1]
27
- mx[i][j] = mx[i-1][j-1]
28
- else
29
- mx[i][j] = [
30
- mx[i-1][j],
31
- mx[i][j-1],
32
- mx[i-1][j-1]
33
- ].min + 1
34
+ (1..b.size).each do |i|
35
+ (1..a.size).each do |j|
36
+ if a[j-1] == b[i-1]
37
+ mx[i][j] = mx[i-1][j-1]
38
+ else
39
+ mx[i][j] = [
40
+ mx[i-1][j],
41
+ mx[i][j-1],
42
+ mx[i-1][j-1]
43
+ ].min + 1
44
+ end
34
45
  end
35
46
  end
36
- end
37
-
38
- mx[-1][-1]
39
- }
47
+ }[-1][-1]
48
+ end
40
49
 
41
50
  class << self
42
- def >>(body=:ok, options={}, headers={})
43
- body = O[body]
51
+ alias_method :delegate_to, :send
44
52
 
45
- options.reverse_merge!({ status: 200 })
53
+ # Convenience methods
54
+ def request() @request end
55
+ def input() request.input end
56
+ def params() ascertain(receiver, request.path).reverse_merge!(request.params) end
57
+
58
+ # Naive implementation
59
+ def before(&blk) yield end
46
60
 
47
- headers.reverse_merge!({
48
- "Content-Type" => "text/json",
49
- "Content-Length" => ("#{body.size}" rescue "0")
50
- })
61
+ # Interface with Rack
62
+ def call(env)
63
+ @request = Request.new(Rack::Request.new(env), I[env["rack.input"]])
51
64
 
52
- [options[:status], headers, [body]]
65
+ delegate_to receiver
66
+ rescue => ε
67
+ → ({ error: ε }),
68
+ { status: 500, headers:
69
+ { "X-Message" => "[...] from a problem to the accidents that condition and resolve it." } }
53
70
  end
54
71
 
72
+ # Define routes in the application
55
73
  def route(verb, path, &blk)
56
74
  meta_def(conjugate(verb, path)) { instance_eval &blk }
57
75
  end
58
76
 
59
- def call(e)
60
- @request = Request.new e["REQUEST_METHOD"].downcase, e["PATH_INFO"], I[e["rack.input"].read]
61
- @params = ascertain receiver, @request.path
77
+ # Render responses
78
+ def render(body=:ok, options={})
79
+ options.reverse_merge!({ status: 200 })
62
80
 
63
- send receiver
64
- rescue => ε
65
- # [...] from a problem to the accidents that condition and resolve it.
66
- self >> { error: ε }
81
+ options[:headers] ||= {}
82
+ options[:headers].reverse_merge!({
83
+ "Content-Type" => "text/json", "X-Framework" => "Immanence" })
84
+
85
+ Rack::Response.new(O[body], options[:status], options[:headers]).finish
67
86
  end
68
87
 
88
+ alias_method :→, :render
89
+
69
90
  private
70
91
 
71
92
  def conjugate(verb, path) # :nodoc:
72
93
  "immanent_#{verb}_#{path}"
73
94
  end
74
95
 
75
- # @return [Hash] Hash of request parameters
96
+ # @return [Hash] Hash of parameters gleaned from request signature
76
97
  def ascertain(method, path)
77
98
  [method.to_s.gsub(/immanent_\w*_/, ""), path].
78
99
 
79
100
  map { |path| path.split("/")[1..-1] }.
80
101
  let { |x, y| x.zip(y) }.
81
- select { |x, y| x[0] == ":" }.
102
+ select { |x, _| x[0] == ":" }.
82
103
  map { |x, y| { x[1..-1] => y } }.
83
104
 
84
105
  reduce({}, :merge).
@@ -88,9 +109,11 @@ module Immanence
88
109
  # @return [String] most likely candidate method to invoke based on incoming route
89
110
  def receiver
90
111
  @receiver ||= methods.grep(/immanent_/).map { |method|
91
- { method: method, Δ: LEVENSHTEIN[method, conjugate(@request.verb, @request.path)] }
112
+ { method: method, Δ: LEVENSHTEIN[method, conjugate(request.request_method, request.path)] }
92
113
  }.min_by { |x| x[:Δ] }[:method]
93
114
  end
94
- end
95
- end
96
- end
115
+ end # self
116
+ end # Control
117
+ end # Immanence
118
+
119
+ Immanent = Immanence
@@ -1,3 +1,3 @@
1
1
  module Immanence
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
4
+
5
+ describe "Immanent" do
6
+ before do
7
+ class Application < Immanent::Control
8
+ route :get, "/resource/:id" do
9
+ render ({ no: :future })
10
+ end
11
+ end
12
+ end
13
+
14
+ it "should calculate the Levenshtein difference between two strings (0)" do
15
+ Immanent::Control::LEVENSHTEIN["kitten", "sitten"].should eq 1
16
+ end
17
+
18
+ it "should calculate the Levenshtein difference between two strings (1)" do
19
+ Immanent::Control::LEVENSHTEIN["sitten", "sittin"].should eq 1
20
+ end
21
+
22
+ it "should calculate the Levenshtein difference between two strings (2)" do
23
+ Immanent::Control::LEVENSHTEIN["sittin", "sitting"].should eq 1
24
+ end
25
+
26
+ it "should conjugate a verb and path into a method name" do
27
+ Immanent::Control.send(:conjugate, :get, "/resource/:id").should eq "immanent_get_/resource/:id"
28
+ end
29
+
30
+ it "should extract parameters out of the request path" do
31
+ Immanent::Control.send(:ascertain, "immanent_get_/resource/:id", "/resource/1000").should eq({ id: "1000" })
32
+ end
33
+
34
+ it "should be able to extract multiple parameters out of the request path" do
35
+ Immanent::Control.send(:ascertain, "immanent_get_/resource/:resource_id/child/:id", "/resource/1000/child/10").should eq({ resource_id: "1000", id: "10" })
36
+ end
37
+
38
+ it "should provide a DSL for defining routes" do
39
+ Application.methods.include?(:"immanent_get_/resource/:id").should be_true
40
+ end
41
+
42
+ it "should have a default response" do
43
+ Application.render[0].should eq(Rack::Response.new("\"ok\"").finish[0])
44
+ end
45
+
46
+ it "should respond with the route definition, a JSON string" do
47
+ Application.send("immanent_get_/resource/:id").last.body.should eq(["{\"no\":\"future\"}"])
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ require "rspec"
2
+
3
+ require "./lib/immanence"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: immanence
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - dzucconi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-25 00:00:00.000000000 Z
11
+ date: 2013-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -97,6 +97,8 @@ files:
97
97
  - lib/core_ext/object.rb
98
98
  - lib/immanence.rb
99
99
  - lib/immanence/version.rb
100
+ - spec/immanence_spec.rb
101
+ - spec/spec_helper.rb
100
102
  homepage: http://github.com/dzucconi/immanence
101
103
  licenses:
102
104
  - CC0
@@ -121,5 +123,7 @@ rubygems_version: 2.0.6
121
123
  signing_key:
122
124
  specification_version: 4
123
125
  summary: A framework
124
- test_files: []
126
+ test_files:
127
+ - spec/immanence_spec.rb
128
+ - spec/spec_helper.rb
125
129
  has_rdoc: