picatrix 0.2.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,19 +1,22 @@
1
1
  module Picatrix
2
2
  # eats dots :)
3
+ # and gives you back nodes and edges
3
4
  class Pacman
4
- attr_accessor :graph, :hash
5
+ attr_accessor :hash
6
+ attr_accessor :nodes, :edges
5
7
 
6
8
  def initialize(file_name)
7
- @graph = GraphViz.parse(
8
- file_name, :path => "/usr/local/bin"
9
- )
10
9
  @hash = TransformToHash.new.apply(
11
- DigraphParser.new.parse_with_debug(
10
+ DigraphParser.new.parse(
12
11
  File.read(file_name)
13
12
  )
14
13
  ).flat_map(&:entries).
15
14
  group_by(&:first).
16
15
  map {|k,v| Hash[k, v.map(&:last)]}
16
+
17
+ grouped = @hash.group_by {|e| e.keys.first}
18
+ @nodes = grouped[:node].flat_map {|e| e[:node]}.reduce({}, :merge)
19
+ @edges = grouped[:edge].flat_map {|e| e[:edge]}
17
20
  end
18
21
  end
19
22
  end
@@ -0,0 +1,40 @@
1
+ module Picatrix
2
+ # this class is the data structure you'll construct the Sinatra
3
+ # routes and their corresponding handling method bodies
4
+ class Routes
5
+ attr_accessor :unique_routes, :root_path
6
+
7
+ def initialize(edges, control_templates)
8
+ @unique_routes = edges.collect do |edge|
9
+ source = edge.keys.first
10
+ target = edge.values.first[:target]
11
+ properties = edge[source]
12
+
13
+ link_relation = properties[:link_relation].delete('"')
14
+
15
+ if source == "root"
16
+ #TODO right now app is using this in place of
17
+ # figuring out parent collections of collections
18
+ @root_path = target
19
+ end
20
+
21
+ templates = {}
22
+ if (control = control_templates[target])
23
+ templates.merge!(control.to_hash)
24
+ end
25
+
26
+ {
27
+ path: target,
28
+ link_relation: link_relation,
29
+ method: properties[:method],
30
+ resource: {
31
+ name: target.camelcase.constantize,
32
+ collection: (target == target.pluralize)
33
+ },
34
+ control_templates: templates
35
+ }
36
+ # make sure routes are not duplicated
37
+ end.compact.uniq {|e| [e[:path], e[:method]]}
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,6 @@
1
1
  require 'sinatra'
2
2
  require 'sinatra/json'
3
+ require 'protobuf'
3
4
  require 'require_all'
4
5
 
5
6
  require_all("./lib/picatrix/generated/*.rb")
@@ -12,37 +13,152 @@ class <%= @app_name.camelize %> < Sinatra::Base
12
13
  end
13
14
 
14
15
  get '/' do
15
- status, headers, body = call env.merge("PATH_INFO" => "/<%= @root_path %>")
16
+ status, headers, body = call env.merge("PATH_INFO" => "/<%= @routes.root_path %>")
16
17
  [status, headers, body]
17
18
  end
18
19
 
19
- <% @routes.uniq.each do |route| %>
20
- <% if return_mapping_of(route[:method]) == :collection_return %>
20
+ def json(hash = {})
21
+ # anything set here will be sent in all responses
22
+ super(<%= @namespaces %>.merge!(hash))
23
+ end
24
+
25
+ <% @routes.unique_routes.each do |route| %>
26
+ <% if route[:resource][:collection] %>
21
27
  <%= route[:method] %> "/<%= route[:path] %>" do
22
- # get collection
23
- items = <%= route[:collection][:resource_name].constantize %>.all
24
- # render collection into templates
25
- hashed_items = items.collect do |item|
26
- <%= route[:collection][:template].to_s.delete("\\") %>
28
+ items = <%= route[:resource][:name] %>Resource.all.collection.collect do |r|
29
+ r.to_hash
27
30
  end
28
- # return that collection
29
- json(<%= route[:body] %>.merge({"<%= route[:collection][:name] %>" => hashed_items}))
31
+
32
+ <% if route[:control_templates][route[:path]][route[:method]] %>
33
+ controls = send(:<%= route[:control_templates][route[:path]][route[:method]] %>, <%= route[:resource][:name] %>).call("<%= route[:path] %>", params)
34
+ <% else %>
35
+ controls = {}
36
+ <% end %>
37
+
38
+ json(
39
+ {:@controls => controls}.
40
+ merge!({"<%= route[:resource][:name].to_s.underscore %>" => items})
41
+ )
30
42
  end
31
43
  <% else %>
32
- <%= route[:method] %> "/<%= route[:path] %>" do
33
- # create item
34
- item = <%= route[:collection][:resource_name].constantize %>.<%= send_method_for(route[:method]).to_s %>(params)
35
- # return new item
36
- json(<%= route[:body] %>)
44
+ <%= route[:method] %> "/<%= route[:path] %>/:item_id" do
45
+ item = <%= route[:resource][:name].to_s.pluralize.constantize %>Resource.<%= send_method_for(route[:method]).to_s %>(params[:item_id])
46
+
47
+ <% if route[:control_templates][route[:path]][route[:method]] %>
48
+ controls = send(:<%= route[:control_templates][route[:path]][route[:method]] %>, <%= route[:resource][:name] %>).call("<%= route[:path] %>", params)
49
+ <% else %>
50
+ controls = {}
51
+ <% end %>
52
+
53
+ json(
54
+ {:@controls => controls}.
55
+ merge!(item.to_hash.except(:controls))
56
+ )
37
57
  end
38
58
  <% end %>
39
59
  <% end %>
40
60
 
41
- <% @addressable_items.each do |item| %>
42
- get "/<%= item[:path] %>" do
43
- item_id = params[:item_id]
44
- item = <%= item[:type].camelcase.constantize %>.find(item_id)
45
- json(<%= item[:body] %>)
46
- end
47
- <% end %>
61
+ def mason_up(type)
62
+ if type == collection
63
+ proc do |path, params|
64
+ {
65
+ up: Mason::Up.new(
66
+ {
67
+ href: "http://localhost:9292/#{<%= @root_path %>}"
68
+ }
69
+ ).to_hash
70
+ }
71
+ end
72
+ else
73
+ proc do |path, params|
74
+ {
75
+ up: Mason::Up.new(
76
+ {
77
+ href: "http://localhost:9292/#{path}"
78
+ }
79
+ ).to_hash
80
+ }
81
+ end
82
+ end
83
+ end
84
+ def mason_self(item)
85
+ proc do |path, params|
86
+ if params[:item_id]
87
+ {
88
+ self: Mason::Self.new(
89
+ {
90
+ href: "http://localhost:9292/#{path}/#{params[:item_id]}"
91
+ }
92
+ ).to_hash
93
+ }
94
+ else
95
+ {
96
+ self: Mason::Self.new(
97
+ {
98
+ href: "http://localhost:9292/#{path}"
99
+ }
100
+ ).to_hash
101
+ }
102
+ end
103
+ end
104
+ end
105
+ def edit(item)
106
+ proc do |path, params|
107
+ {
108
+ edit: ("Edit" + item.to_s.singularize.camelize).constantize.new(
109
+ {
110
+ form: Mason::Edit.new(
111
+ href: "http://localhost:9292/#{path}/#{params[:item_id]}"
112
+ )
113
+ }
114
+ ).to_hash
115
+ }
116
+ end
117
+ end
118
+ def remove(item)
119
+ proc do |path, params|
120
+ {
121
+ remove: Mason::Remove.new(
122
+ {
123
+ href: "http://localhost:9292/#{path}/#{params[:item_id]}"
124
+ }
125
+ ).to_hash
126
+ }
127
+ end
128
+ end
129
+ def create(item)
130
+ proc do |path, params|
131
+ {
132
+ create: ("Create" + item.to_s.singularize.camelize).constantize.new(
133
+ {
134
+ form: Mason::Create.new(
135
+ href: ""
136
+ )
137
+ }
138
+ )
139
+ }
140
+ end
141
+ end
142
+ def filter(collection)
143
+ # TODO this prob cant really work here
144
+ proc do |path, params|
145
+ if params[:item_id]
146
+ {
147
+ "<%= @prefix %>filter": Mason::Filter.new(
148
+ {
149
+ href: "http://localhost:9292/#{path}/#{params[:item_id]}"
150
+ }
151
+ ).to_hash
152
+ }
153
+ else
154
+ {
155
+ "<%= @prefix %>filter": Mason::Filter.new(
156
+ {
157
+ href: "http://localhost:9292/#{path}"
158
+ }
159
+ ).to_hash
160
+ }
161
+ end
162
+ end
163
+ end
48
164
  end
@@ -0,0 +1,15 @@
1
+ require 'ostruct'
2
+ class <%= @node_name.camelize %>
3
+ def self.attributes
4
+ {
5
+ namespace: "s",
6
+ action_name: "filter",
7
+ href: "http://localhost:9292/view_submissions/filter?category={category}&has_photo={has_photo}",
8
+ method: "",
9
+ template: {},
10
+ isHrefTemplate: false,
11
+ title: "filter submissions by category",
12
+ description: "in addition to category, you can ensure the submission has a photo"
13
+ }
14
+ end
15
+ end
@@ -1,27 +1,41 @@
1
- require 'ostruct'
2
- class <%= @node_name.camelize %>
3
- def self.find(item_id)
4
- OpenStruct.new(
5
- category: "cats",
6
- photo: "http://imgur.com/r/cats/myEqIZx",
7
- id: item_id
1
+ # you can remove this if you don't use faker
2
+ # for your fixture data
3
+ require 'faker'
4
+
5
+ # the resource is the thing actually persisted
6
+ # technically, this is just an interface to it
7
+ # so long as your persistance layer is able to implement
8
+ # the methods in this fixture, the app will generate
9
+ class <%= @resource_name %>Resource
10
+ # method used to return single item from a collection
11
+ # creates an instance on the fly
12
+ # both the find and collection methods use this
13
+ def self.create(item_id)
14
+ <%= @resource_name.singularize %>.new(
15
+ {
16
+ # any "bare" Proto type will be given an associated Faker
17
+ <% @fields.keys.each do |field_key| %>
18
+ :<%= field_key %> => <%= @fields[field_key] %>,
19
+ <% end %>
20
+ }
8
21
  )
9
22
  end
10
- def self.create(params)
11
- self.find(rand(10))
23
+ def self.find(params)
24
+ self.create(params)
12
25
  end
13
26
  def self.all
14
- [
15
- OpenStruct.new(
16
- category: "cats",
17
- photo: "http://imgur.com/r/cats/myEqIZx",
18
- id: 1
19
- ),
20
- OpenStruct.new(
21
- category: "cats",
22
- photo: "http://imgur.com/r/cats/myEqIZx",
23
- id: 2
24
- )
25
- ]
27
+ collection = []
28
+ rand(10).times {
29
+ collection << self.find(rand(1000))
30
+ }
31
+ <%= @resource_name %>.new(collection: collection)
32
+ end
33
+
34
+ def self.minimal_controls_for(item_id)
35
+ host = "http://localhost:9292"
36
+ {
37
+ self: Mason::Self.new(href: "#{host}/<%= @resource_name.singularize.underscore %>/#{item_id}"),
38
+ up: Mason::Up.new(href: "#{host}/<%= @resource_name.underscore %>")
39
+ }
26
40
  end
27
41
  end
@@ -1,19 +1,10 @@
1
1
  module Picatrix
2
2
  class TransformToHash < Parslet::Transform
3
3
  rule(:node => subtree(:node)) do
4
- types = {
5
- note: "template",
6
- folder: "collection",
7
- rectangle: "item"
8
- }
9
- shape = String(node[:shape]).split("=").last.to_sym
10
- type_of_node_from_shape = types[shape]
11
-
12
4
  {
13
5
  node: {
14
6
  String(node[:name]) => {
15
7
  label: String(node[:label]),
16
- type: type_of_node_from_shape
17
8
  }
18
9
  }
19
10
  }
@@ -36,7 +27,6 @@ module Picatrix
36
27
  String(edge[:outbound]) => {
37
28
  target: String(edge[:inbound]).delete(" {} "),
38
29
  link_relation: String(edge[:link_relation]),
39
- inline: String(edge[:style]).include?("dotted"),
40
30
  method: method
41
31
  }
42
32
  }
@@ -1,3 +1,3 @@
1
1
  module Picatrix
2
- VERSION = "0.2.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/picatrix.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "picatrix/version"
2
+ require "protobuf"
2
3
  require "parslet"
3
4
  require "graphviz"
4
5
  require "active_support"
@@ -8,6 +9,10 @@ require "thor"
8
9
  require "picatrix/pacman"
9
10
  require "picatrix/digraph_parser"
10
11
  require "picatrix/transform_to_hash"
12
+ require "picatrix/link_relation_index"
13
+ require "picatrix/routes"
14
+ require "picatrix/cruddy"
15
+ require "picatrix/buffy"
11
16
  require "picatrix/jenny"
12
17
 
13
18
  module Picatrix
@@ -20,9 +25,25 @@ module Picatrix
20
25
  puts "output: #{output}" if output
21
26
 
22
27
  Jenny.new(
23
- Pacman.new(dot_file_name).hash,
28
+ Pacman.new(
29
+ dot_file_name
30
+ ).hash,
24
31
  options
25
32
  ).just_do_it
26
33
  end
34
+
35
+ desc "prep <dot file name>", "outputs an image and sets up fixtures necessary to generate your app"
36
+ def prep(dot_file_name)
37
+ GraphViz.parse(dot_file_name, :path => "/usr/local/bin").
38
+ output(:png => "#{dot_file_name}.png")
39
+ puts "output to #{dot_file_name}.png"
40
+
41
+ Buffy.new(
42
+ Pacman.new(
43
+ dot_file_name
44
+ ).nodes
45
+ )
46
+ puts "output to generated/"
47
+ end
27
48
  end
28
49
  end
data/picatrix.gemspec CHANGED
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.add_dependency "parslet"
23
+ spec.add_dependency "protobuf"
24
+ spec.add_dependency "faker"
23
25
  spec.add_dependency "ruby-graphviz"
24
26
  spec.add_dependency "thor"
25
27
  spec.add_dependency "activesupport"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: picatrix
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Moore-Niemi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-11-10 00:00:00.000000000 Z
11
+ date: 2015-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: protobuf
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faker
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: ruby-graphviz
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -224,18 +252,24 @@ files:
224
252
  - bin/console
225
253
  - bin/picapica
226
254
  - bin/setup
255
+ - definitions/app.proto
256
+ - definitions/mason.proto
227
257
  - lib/picatrix.rb
258
+ - lib/picatrix/buffy.rb
259
+ - lib/picatrix/cruddy.rb
228
260
  - lib/picatrix/digraph_parser.rb
261
+ - lib/picatrix/fixtures/view_submission.rb
262
+ - lib/picatrix/generated/app.pb.rb
229
263
  - lib/picatrix/generated/app.rb
230
- - lib/picatrix/generated/view_submission.rb
264
+ - lib/picatrix/generated/mason.pb.rb
265
+ - lib/picatrix/generated/mason/namespaces.json
266
+ - lib/picatrix/generated/view_submissions.rb
231
267
  - lib/picatrix/jenny.rb
268
+ - lib/picatrix/link_relation_index.rb
232
269
  - lib/picatrix/pacman.rb
233
- - lib/picatrix/schemas/view_submission_item.json
270
+ - lib/picatrix/routes.rb
234
271
  - lib/picatrix/templates/app.rb
235
- - lib/picatrix/templates/mason/submission_filter_template.json
236
- - lib/picatrix/templates/mason/view_submission.json
237
- - lib/picatrix/templates/mason/view_submission_min.json
238
- - lib/picatrix/templates/mason/view_submissions.json
272
+ - lib/picatrix/templates/control.rb
239
273
  - lib/picatrix/templates/type.rb
240
274
  - lib/picatrix/transform_to_hash.rb
241
275
  - lib/picatrix/version.rb
@@ -265,4 +299,3 @@ signing_key:
265
299
  specification_version: 4
266
300
  summary: 'input: dot file + yml files, output: ruby api'
267
301
  test_files: []
268
- has_rdoc:
@@ -1,27 +0,0 @@
1
- require 'ostruct'
2
- class ViewSubmission
3
- def self.find(item_id)
4
- OpenStruct.new(
5
- category: "cats",
6
- photo: "http://imgur.com/r/cats/myEqIZx",
7
- id: item_id
8
- )
9
- end
10
- def self.create(params)
11
- self.find(rand(10))
12
- end
13
- def self.all
14
- [
15
- OpenStruct.new(
16
- category: "cats",
17
- photo: "http://imgur.com/r/cats/myEqIZx",
18
- id: 1
19
- ),
20
- OpenStruct.new(
21
- category: "cats",
22
- photo: "http://imgur.com/r/cats/myEqIZx",
23
- id: 2
24
- )
25
- ]
26
- end
27
- end
@@ -1,12 +0,0 @@
1
- {
2
- "title": "Schema",
3
- "type": "object",
4
- "properties": {
5
- "category": {
6
- "type": "string"
7
- },
8
- "photo": {
9
- "type": "string"
10
- }
11
- }
12
- }
@@ -1,4 +0,0 @@
1
- {
2
- "category": "#{params[:category]}",
3
- "has_photo": "#{params[:has_photo]}"
4
- }
@@ -1,35 +0,0 @@
1
- {
2
- "@namespaces": {
3
- "s": {
4
- "name": "http://localhost:9292/rels#"
5
- }
6
- },
7
- "@controls": {
8
- "self": {
9
- "title": "submission #{item.id}",
10
- "href": "http://localhost:9292/view_submission/#{item.id}"
11
- },
12
- "up": {
13
- "title": "View submissions",
14
- "href": "http://localhost:9292/view_submissions/newest"
15
- },
16
- "s:edit": {
17
- "title": "update category or photo url",
18
- "encoding": "json",
19
- "href": "http://localhost:9292/view_submission/#{item.id}",
20
- "method": "PATCH",
21
- "template": {
22
- "photo": "{new_url}",
23
- "category": "{new_category}"
24
- }
25
- },
26
- "s:remove": {
27
- "title": "remove submission",
28
- "href": "http://localhost:9292/view_submission/#{item.id}",
29
- "method": "DELETE"
30
- }
31
- },
32
- "id": "#{item.id}",
33
- "category": "#{item.category}",
34
- "photo": "#{item.photo}"
35
- }
@@ -1,11 +0,0 @@
1
- {
2
- "@controls": {
3
- "self": {
4
- "title": "submission #{item.id}",
5
- "href": "http://localhost:9292/view_submission/#{item.id}"
6
- }
7
- },
8
- "id": "#{item.id}",
9
- "category": "#{item.category}",
10
- "photo": "#{item.photo}"
11
- }
@@ -1,36 +0,0 @@
1
- {
2
- "title": "View submissions",
3
- "description": "idk yet",
4
- "@meta": {
5
- "@title": "Submissions",
6
- "@description": "This resource represents submissions."
7
- },
8
- "@namespaces": {
9
- "s": {
10
- "name": "http://localhost:9292/rels#"
11
- }
12
- },
13
- "@controls": {
14
- "self": {
15
- "href": "http://localhost:9292/view_submissions/newest"
16
- },
17
- "s:filter": {
18
- "href": "http://localhost:9292/view_submissions/filter?category={category}&has_photo={has_photo}",
19
- "isHrefTemplate": true,
20
- "title": "filter submissions by category",
21
- "description": "in addition to category, you can ensure the submission has a photo"
22
- },
23
- "s:add": {
24
- "title": "add new submission",
25
- "encoding": "json",
26
- "href": "http://localhost:9292/submission_add_template/submit",
27
- "method": "POST",
28
- "template":
29
- {
30
- "title": "default title",
31
- "category": "cats",
32
- "photo_url": "http://cats.com"
33
- }
34
- }
35
- }
36
- }