graph_starter 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.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +38 -0
  5. data/app/assets/images/missing.png +0 -0
  6. data/app/assets/javascripts/graph_starter/application.coffee.erb +25 -0
  7. data/app/assets/javascripts/graph_starter/ember-template-compiler.js +22130 -0
  8. data/app/assets/javascripts/graph_starter/ember.js +52794 -0
  9. data/app/assets/javascripts/graph_starter/ember_apps/permissions_modal.coffee +146 -0
  10. data/app/assets/javascripts/graph_starter/ember_apps/user_list_dropdown.coffee +22 -0
  11. data/app/assets/javascripts/graph_starter/jquery-ui.min.js +7 -0
  12. data/app/assets/javascripts/graph_starter/underscore.js +1548 -0
  13. data/app/assets/stylesheets/graph_starter/application.scss +9 -0
  14. data/app/controllers/graph_starter/application_controller.rb +11 -0
  15. data/app/controllers/graph_starter/assets_controller.rb +76 -0
  16. data/app/controllers/graph_starter/authorizables_controller.rb +47 -0
  17. data/app/controllers/graph_starter/categories_controller.rb +79 -0
  18. data/app/controllers/graph_starter/groups_controller.rb +85 -0
  19. data/app/controllers/graph_starter/models_controller.rb +11 -0
  20. data/app/controllers/graph_starter/properties_controller.rb +11 -0
  21. data/app/helpers/graph_starter/application_helper.rb +11 -0
  22. data/app/models/concerns/graph_starter/authorizable.rb +30 -0
  23. data/app/models/graph_starter/asset.rb +161 -0
  24. data/app/models/graph_starter/can_access.rb +14 -0
  25. data/app/models/graph_starter/category.rb +24 -0
  26. data/app/models/graph_starter/group.rb +36 -0
  27. data/app/models/graph_starter/image.rb +16 -0
  28. data/app/models/graph_starter/model.rb +23 -0
  29. data/app/models/graph_starter/property.rb +19 -0
  30. data/app/models/graph_starter/view.rb +30 -0
  31. data/app/views/graph_starter/assets/TODO.md +7 -0
  32. data/app/views/graph_starter/assets/_cards.html.slim +34 -0
  33. data/app/views/graph_starter/assets/edit.html.slim +24 -0
  34. data/app/views/graph_starter/assets/home.html.slim +1 -0
  35. data/app/views/graph_starter/assets/index.html.slim +28 -0
  36. data/app/views/graph_starter/assets/show.html.slim +98 -0
  37. data/app/views/graph_starter/authorizables/show.json.jbuilder +20 -0
  38. data/app/views/graph_starter/authorizables/user_and_group_search.json.jbuilder +8 -0
  39. data/app/views/graph_starter/categories/show.html.slim +9 -0
  40. data/app/views/graph_starter/groups/_form.html.slim +30 -0
  41. data/app/views/graph_starter/groups/_list.html.slim +19 -0
  42. data/app/views/graph_starter/groups/edit.html.slim +8 -0
  43. data/app/views/graph_starter/groups/index.html.slim +18 -0
  44. data/app/views/graph_starter/groups/index.json.jbuilder +4 -0
  45. data/app/views/graph_starter/groups/new.html.slim +5 -0
  46. data/app/views/graph_starter/groups/show.html.slim +16 -0
  47. data/app/views/graph_starter/groups/show.json.jbuilder +1 -0
  48. data/app/views/graph_starter/models/index.html.slim +10 -0
  49. data/app/views/graph_starter/properties/_property.html.slim +34 -0
  50. data/app/views/layouts/graph_starter/_change_permissions_modal.html.slim +90 -0
  51. data/app/views/layouts/graph_starter/_custom_menu.html.slim +0 -0
  52. data/app/views/layouts/graph_starter/_menu.html.slim +7 -0
  53. data/app/views/layouts/graph_starter/_twitter_meta_tags.html.slim +17 -0
  54. data/app/views/layouts/graph_starter/application.html.slim +45 -0
  55. data/app/views/layouts/graph_starter/custom_menu.html.slim +0 -0
  56. data/config/routes.rb +24 -0
  57. data/lib/graph_starter.rb +4 -0
  58. data/lib/graph_starter/engine.rb +25 -0
  59. data/lib/graph_starter/query_authorizer.rb +81 -0
  60. data/lib/graph_starter/version.rb +3 -0
  61. data/lib/tasks/graph_starter_tasks.rake +4 -0
  62. data/test/dummy/README.rdoc +28 -0
  63. data/test/dummy/Rakefile +6 -0
  64. data/test/dummy/app/assets/javascripts/application.js +13 -0
  65. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  66. data/test/dummy/app/controllers/application_controller.rb +5 -0
  67. data/test/dummy/app/helpers/application_helper.rb +2 -0
  68. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  69. data/test/dummy/bin/bundle +3 -0
  70. data/test/dummy/bin/rails +4 -0
  71. data/test/dummy/bin/rake +4 -0
  72. data/test/dummy/bin/setup +29 -0
  73. data/test/dummy/config.ru +4 -0
  74. data/test/dummy/config/application.rb +26 -0
  75. data/test/dummy/config/boot.rb +5 -0
  76. data/test/dummy/config/database.yml +25 -0
  77. data/test/dummy/config/environment.rb +5 -0
  78. data/test/dummy/config/environments/development.rb +41 -0
  79. data/test/dummy/config/environments/production.rb +79 -0
  80. data/test/dummy/config/environments/test.rb +42 -0
  81. data/test/dummy/config/initializers/assets.rb +11 -0
  82. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  83. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  84. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  85. data/test/dummy/config/initializers/inflections.rb +16 -0
  86. data/test/dummy/config/initializers/mime_types.rb +4 -0
  87. data/test/dummy/config/initializers/session_store.rb +3 -0
  88. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  89. data/test/dummy/config/locales/en.yml +23 -0
  90. data/test/dummy/config/routes.rb +4 -0
  91. data/test/dummy/config/secrets.yml +22 -0
  92. data/test/dummy/public/404.html +67 -0
  93. data/test/dummy/public/422.html +67 -0
  94. data/test/dummy/public/500.html +66 -0
  95. data/test/dummy/public/favicon.ico +0 -0
  96. data/test/graph_starter_test.rb +7 -0
  97. data/test/integration/navigation_test.rb +10 -0
  98. data/test/test_helper.rb +20 -0
  99. metadata +262 -0
@@ -0,0 +1,14 @@
1
+ module GraphStarter
2
+ class CanAccess
3
+ include Neo4j::ActiveRel
4
+
5
+ from_class :any # [:User, :Group] ?
6
+ to_class :any
7
+ type :CAN_ACCESS
8
+
9
+ creates_unique
10
+
11
+ property :level, default: 'read'
12
+ validates :level, inclusion: {in: %w(read write)}
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ module GraphStarter
2
+ class Category
3
+ include Neo4j::ActiveNode
4
+ self.mapped_label_name = 'Category'
5
+
6
+ include Authorizable
7
+
8
+ property :name
9
+ property :standardized_name, constraint: :unique
10
+
11
+ property :icon_class, type: String
12
+
13
+ property :created_at
14
+ property :updated_at
15
+
16
+ has_many :in, :assets, origin: :categories
17
+
18
+ scope :ordered, -> { order(:name) }
19
+
20
+ def self.most_recently_updated
21
+ all.order(:updated_at).last
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ module GraphStarter
2
+ class Group
3
+ include Neo4j::ActiveNode
4
+ self.mapped_label_name = 'Group'
5
+
6
+ property :name, type: String
7
+
8
+ property :created_at
9
+ property :updated_at
10
+
11
+ has_one :in, :parent, model_class: :Group, type: :HAS_SUBGROUP
12
+ has_many :out, :sub_groups, model_class: :Group, type: :HAS_SUBGROUP
13
+
14
+ has_many :in, :members, model_class: :User, origin: :groups
15
+
16
+ def self.roots
17
+ all(:group).where('NOT(group<-[:HAS_SUBGROUP]-())')
18
+ end
19
+
20
+ def self.for_query(query_string)
21
+ query_regex = Regexp.new('.*' + query_string.gsub(/[\s\*]+/, '.*') + '.*', 'i')
22
+
23
+ as(:group)
24
+ .where('group.name =~ {query}')
25
+ .params(query: query_regex)
26
+ end
27
+
28
+ def addable_users
29
+ User.as(:user)
30
+ .groups(:group, nil, optional: true)
31
+ .where('group.uuid <> ?', id)
32
+ .query.with(:user)
33
+ .proxy_as(User, :user)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ module GraphStarter
2
+ class Image
3
+ include Neo4j::ActiveNode
4
+ self.mapped_label_name = 'Image'
5
+ include Neo4jrb::Paperclip
6
+
7
+ property :title
8
+ property :description
9
+ property :details
10
+ property :original_url
11
+ serialize :details
12
+
13
+ has_neo4jrb_attached_file :source
14
+ validates_attachment_content_type :source, content_type: ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ module GraphStarter
2
+ class Model
3
+ include Neo4j::ActiveNode
4
+ self.mapped_label_name = 'Model'
5
+
6
+ include Authorizable
7
+
8
+ id_property :name
9
+
10
+ has_many :out, :properties, origin: :model
11
+
12
+ def ruby_model
13
+ name.constantize
14
+ end
15
+
16
+ def authorized_properties(user)
17
+ require './lib/query_authorizer'
18
+ query_authorizer = QueryAuthorizer.new(properties(:property))
19
+
20
+ query_authorizer.authorized_pluck(:property, user)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module GraphStarter
2
+ class Property
3
+ include Neo4j::ActiveNode
4
+ self.mapped_label_name = 'Property'
5
+
6
+ include Authorizable
7
+
8
+ property :name
9
+ property :ruby_type
10
+ property :created_at
11
+ property :updated_at
12
+
13
+ has_one :in, :model, type: :HAS_PROPERTY, model_class: '::GraphStarter::Model'
14
+
15
+ def attribute_object
16
+ model.ruby_model.attributes[name]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ module GraphStarter
2
+ class View
3
+ include Neo4j::ActiveRel
4
+
5
+ from_class :User
6
+ to_class :any
7
+ type :VIEWED
8
+ creates_unique
9
+
10
+ property :viewed_at
11
+ property :browser_string
12
+
13
+ validates :browser_string, presence: true
14
+ validates :ip_address, ip_address: true
15
+
16
+ before_create :set_viewed_at
17
+
18
+ def set_viewed_at
19
+ self.viewed_at ||= Time.now
20
+ end
21
+
22
+ after_create :increment_destination_view_count
23
+
24
+ def increment_destination_view_count
25
+ to_node.view_count ||= 0
26
+ to_node.view_count += 1
27
+ to_node.save
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ TODO:
2
+
3
+ Property class
4
+ * description (default to humanize?)
5
+
6
+ Javascript UI to control authorization for all authorizable properties on pages (if the user is authorized ;)
7
+
@@ -0,0 +1,34 @@
1
+ - grouped_assets = assets.group_by(&:class)
2
+
3
+ - grouped_assets.each do |model_class, asset_group|
4
+ - if grouped_assets.size > 1
5
+ h2 = model_class.name.pluralize.humanize
6
+
7
+ - properties = model_class.properties
8
+
9
+ .ui.link.cards
10
+ - asset_group.each do |asset|
11
+ .card
12
+ .content
13
+ - if asset.private?
14
+ .right.floated.meta
15
+ i.hide.large.red.icon
16
+ .header = link_to asset.title, asset_path(asset)
17
+ - if asset.class.has_images?
18
+ .image
19
+ - if asset.first_image_source && asset.first_image_source.url.present?
20
+ = image_tag asset.first_image_source.url
21
+ - else
22
+ img src="missing"
23
+ - categories = Array(asset.categories).sort_by(&:name)
24
+ - if categories.present?
25
+ .content
26
+ - categories[0,2].each do |category|
27
+ .extra.content
28
+ i class="#{category.class.icon_class || 'folder'} icon"
29
+ = link_to category.name, asset_path(category)
30
+ - if categories.size > 2
31
+ .extra.content
32
+ i.ellipsis.horizontal.icon
33
+ | more categories
34
+
@@ -0,0 +1,24 @@
1
+ = javascript_include_tag 'jquery.datetimepicker', 'data-turbolinks-track' => true
2
+ = stylesheet_link_tag 'jquery.datetimepicker', media: 'all', 'data-turbolinks-track' => true
3
+
4
+
5
+ = form_for @asset, url: url_for(action: :update), html: {class: 'ui form'} do |f|
6
+ .field
7
+ label Title
8
+
9
+ = f.text_field :title
10
+
11
+ = image_tag @asset.image.url, class: 'ui medium image'
12
+ .field
13
+ label Image
14
+
15
+ = f.file_field :image
16
+
17
+ - @asset.class.authorized_properties_and_levels(current_user).each do |property, level|
18
+ .field
19
+ label = property.name.humanize
20
+
21
+ = render partial: 'properties/property', locals: {property: property, asset: @asset, level: level, form: f}
22
+
23
+
24
+ = f.submit 'Update', class: 'ui button'
@@ -0,0 +1,28 @@
1
+
2
+ - if @assets.present?
3
+ .ui.search
4
+ .ui.icon.input
5
+ input.prompt type="text" placeholder="Search..." style="width: 400px"
6
+ i.search.icon
7
+ .results
8
+
9
+ .ui.divider
10
+
11
+ javascript:
12
+ var controller = #{@model_class_scope.model_slug.to_json.html_safe}
13
+
14
+ coffee:
15
+ $('.ui.search').search
16
+ onSelect: (result, response) ->
17
+ window.location.replace(result.url)
18
+
19
+ $.fn.api.settings.api =
20
+ search: "/#{controller}/search/{query}.json"
21
+
22
+ - count = @assets.size
23
+ - total_count = @model_class_scope.count
24
+ .stacked.segment Displaying #{[count, total_count].min} of #{number_to_human(total_count)} total
25
+
26
+ .ui.divider
27
+
28
+ = render partial: 'graph_starter/assets/cards', locals: {assets: @assets}
@@ -0,0 +1,98 @@
1
+
2
+ - main_column_exists = @asset.class.has_images? && @asset.images.present? || @asset.body.present?
3
+ - side_column_width = (main_column_exists ? 'three' : 'eight')
4
+ - column_count = (main_column_exists ? 'three' : 'two')
5
+
6
+ .ui.huge.centered.header
7
+ .content
8
+ = @asset.title
9
+
10
+ .ui.grid
11
+ div class="#{side_column_width} wide column"
12
+ .ui.items
13
+ .item
14
+ .ui.statistic
15
+ .label
16
+ | Views
17
+ .value
18
+ = @asset.view_count || 0
19
+
20
+
21
+ - @asset.class.authorized_associations.each do |name, association|
22
+ - result = @asset.send(name)
23
+ - if result.present?
24
+ .item
25
+ .content
26
+ .ui.horizontal.divider = name.to_s.humanize
27
+ .description
28
+ - case association.type
29
+ - when :has_one
30
+ - object = result
31
+ i class="#{association.target_class.icon_class} icon"
32
+ = link_to object.title, asset_path(object)
33
+ - when :has_many
34
+ .ui.middle.aligned.big.divided.list
35
+ - result.each do |object|
36
+ .item
37
+ i class="#{association.target_class.icon_class} icon"
38
+ .content
39
+ = link_to object.title, asset_path(object)
40
+
41
+ - recommendations = @asset.secret_sauce_recommendations
42
+ - if recommendations.present?
43
+ .item
44
+ .content
45
+ .ui.horizontal.divider Recommended
46
+ .description
47
+ .ui.middle.aligned.big.divided.list
48
+ - recommendations.each do |recommendation|
49
+ .item data-score="#{recommendation.score}"
50
+ .content = link_to recommendation.asset.title, asset_path(recommendation.asset)
51
+
52
+ - if main_column_exists
53
+ .ten.wide.column
54
+ - if @asset.body.present?
55
+ = @asset.body
56
+ - if @asset.images.present?
57
+ .ui.link.cards
58
+ - @asset.images.each do |image|
59
+ .card
60
+ .image = image_tag image.source.url, class: 'ui image tiny rounded'
61
+ .content
62
+ .header = image.title if image.title.present?
63
+ - if image.description.present?
64
+ .meta = image.description
65
+ .extra.content
66
+ a.ui.large.green.button href="#{image.source.url}" Full Size
67
+
68
+
69
+ div class="#{side_column_width} wide column"
70
+ - if @current_user_is_admin
71
+ a.ui.labeled.icon.button.right.floated data-authorizable="#{@asset.to_json}"
72
+ | Edit Permissions
73
+
74
+ a.ui.labeled.icon.button.right.floated href="#{edit_asset_path(id: @asset)}"
75
+ i.edit.icon
76
+ | Edit
77
+
78
+ .ui.items
79
+ - @asset.class.authorized_properties(defined?(current_user) && current_user).each do |property|
80
+ - if @asset.read_attribute(property.name).present?
81
+ .item
82
+ .content
83
+ .ui.horizontal.divider data-authorizable="#{property.to_json}"
84
+ = property.name.humanize
85
+ - if property.private?
86
+ i.red.hide.icon
87
+ .description
88
+ = render partial: 'graph_starter/properties/property', locals: {property: property, asset: @asset, level: 'read'}
89
+
90
+ .item
91
+ .content
92
+ .ui.horizontal.divider Created
93
+ .description = @asset.created_at
94
+
95
+ .item
96
+ .content
97
+ .ui.horizontal.divider Last updated
98
+ .description = @asset.updated_at
@@ -0,0 +1,20 @@
1
+
2
+
3
+ json.call(@object, :id, :name, :created_at, :updated_at)
4
+ json.model_slug @object.class.name.tableize
5
+
6
+ json.private @object.private?
7
+
8
+ json.user_permissions @object.allowed_users.each_with_rel do |user, viewable_by|
9
+ json.user do
10
+ json.extract! user, :id, :username, :name
11
+ end
12
+ json.level viewable_by.level
13
+ end
14
+
15
+ json.group_permissions @object.allowed_groups.each_with_rel do |group, viewable_by|
16
+ json.group do
17
+ json.extract! group, :id, :name
18
+ end
19
+ json.level viewable_by.level
20
+ end
@@ -0,0 +1,8 @@
1
+
2
+ json.users @users do |user|
3
+ json.extract! user, :id, :username, :name
4
+ end
5
+
6
+ json.groups @groups do |group|
7
+ json.extract! group, :id, :name
8
+ end
@@ -0,0 +1,9 @@
1
+ p#notice = notice
2
+
3
+ = link_to 'Return to home', root_path
4
+
5
+ h1
6
+ Assets for category:
7
+ = @category.name
8
+
9
+ = render partial: 'assets/cards', locals: {assets: @category.assets.to_a}
@@ -0,0 +1,30 @@
1
+ = form_for @group, html: {class: 'ui form'} do |f|
2
+ - if @group.errors.any?
3
+ #error_explanation.ui.warning.message
4
+ i.close.icon
5
+ .header
6
+ = "#{pluralize(@group.errors.count, "error")} prohibited this group from being saved:"
7
+ .ui.segments
8
+ - @group.errors.full_messages.each do |message|
9
+ .ui.segment
10
+ p = message
11
+
12
+ = f.hidden_field :parent_id, value: params[:parent_id] || @group.parent_id
13
+
14
+ .field
15
+ label Name
16
+ = f.text_field :name
17
+
18
+ .field
19
+ label Users
20
+ = f.select :member_ids, options_from_collection_for_select(User.order(:name), :id, :name, @group.member_ids), {}, multiple: '', id: 'user-list-dropdown', class: 'ui dropdown'
21
+
22
+ coffee:
23
+ $('select.ui.dropdown').dropdown()
24
+
25
+
26
+
27
+ .actions = f.submit class: 'ui primary button'
28
+
29
+ /= javascript_include_tag 'ember_apps/user_list_dropdown', 'data-turbolinks-track' => true
30
+