picatrix 0.2.0 → 0.5.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.
@@ -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
- }