├── test
├── dummy
│ ├── log
│ │ └── .keep
│ ├── lib
│ │ └── assets
│ │ │ └── .keep
│ ├── public
│ │ ├── favicon.ico
│ │ ├── apple-touch-icon.png
│ │ ├── apple-touch-icon-precomposed.png
│ │ ├── 500.html
│ │ ├── 422.html
│ │ └── 404.html
│ ├── .ruby-version
│ ├── app
│ │ ├── assets
│ │ │ ├── images
│ │ │ │ └── .keep
│ │ │ ├── javascripts
│ │ │ │ ├── channels
│ │ │ │ │ └── .keep
│ │ │ │ ├── cable.js
│ │ │ │ └── application.js
│ │ │ ├── config
│ │ │ │ └── manifest.js
│ │ │ └── stylesheets
│ │ │ │ └── application.css
│ │ ├── models
│ │ │ ├── concerns
│ │ │ │ └── .keep
│ │ │ └── application_record.rb
│ │ ├── controllers
│ │ │ ├── concerns
│ │ │ │ └── .keep
│ │ │ └── application_controller.rb
│ │ ├── views
│ │ │ └── layouts
│ │ │ │ ├── mailer.text.erb
│ │ │ │ ├── mailer.html.erb
│ │ │ │ └── application.html.erb
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── jobs
│ │ │ └── application_job.rb
│ │ ├── channels
│ │ │ └── application_cable
│ │ │ │ ├── channel.rb
│ │ │ │ └── connection.rb
│ │ └── mailers
│ │ │ └── application_mailer.rb
│ ├── package.json
│ ├── bin
│ │ ├── rake
│ │ ├── bundle
│ │ ├── rails
│ │ ├── yarn
│ │ ├── update
│ │ └── setup
│ ├── config
│ │ ├── spring.rb
│ │ ├── routes.rb
│ │ ├── environment.rb
│ │ ├── initializers
│ │ │ ├── mime_types.rb
│ │ │ ├── filter_parameter_logging.rb
│ │ │ ├── application_controller_renderer.rb
│ │ │ ├── cookies_serializer.rb
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── wrap_parameters.rb
│ │ │ ├── assets.rb
│ │ │ ├── inflections.rb
│ │ │ └── content_security_policy.rb
│ │ ├── cable.yml
│ │ ├── boot.rb
│ │ ├── application.rb
│ │ ├── database.yml
│ │ ├── locales
│ │ │ └── en.yml
│ │ ├── storage.yml
│ │ ├── puma.rb
│ │ └── environments
│ │ │ ├── test.rb
│ │ │ ├── development.rb
│ │ │ └── production.rb
│ ├── config.ru
│ └── Rakefile
├── kamiflex_test.rb
└── test_helper.rb
├── .tool-versions
├── lib
├── kamiflex
│ ├── version.rb
│ ├── railtie.rb
│ ├── custom.rb
│ ├── actions.rb
│ ├── quick_reply.rb
│ ├── basic_elements.rb
│ └── core.rb
├── tasks
│ └── kamiflex_tasks.rake
└── kamiflex.rb
├── image
├── todos_index.png
└── show_cases
│ └── cards.png
├── bin
└── test
├── example
├── custom_nav.rb
├── postback_button.rb
├── carousel.rb
├── carousel_with_index.rb
├── hero.rb
├── uri_action_demo.rb
├── url_button_demo.rb
├── hello_world.rb
├── apng_test.rb
├── 500_button_test.rb
├── box_with_resource.rb
├── box_with_resource_and_index.rb
├── menu.rb
├── absolute_box_test.rb
├── bubble_size_test.rb
├── quick_reply_demo.rb
├── todos_index.rb
├── show_cases
│ ├── menu.rb
│ ├── receipt.rb
│ ├── shopping.rb
│ ├── social.rb
│ ├── local_search.rb
│ ├── todo_app.rb
│ ├── real_estate.rb
│ ├── hotel.rb
│ ├── restaurant.rb
│ ├── ticket.rb
│ ├── apparel.rb
│ └── transit.rb
├── transit.rb
├── cards.rb
├── dialog.rb
└── dialog2.rb
├── .gitignore
├── Gemfile.lock
├── Rakefile
├── Gemfile
├── MIT-LICENSE
├── kamiflex.gemspec
└── README.md
/test/dummy/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/lib/assets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | ruby 3.0.2
2 |
--------------------------------------------------------------------------------
/test/dummy/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/.ruby-version:
--------------------------------------------------------------------------------
1 | ruby-2.5.1
--------------------------------------------------------------------------------
/test/dummy/app/assets/images/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/models/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/javascripts/channels/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/lib/kamiflex/version.rb:
--------------------------------------------------------------------------------
1 | module Kamiflex
2 | VERSION = '0.17.0'
3 | end
4 |
--------------------------------------------------------------------------------
/test/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/image/todos_index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etrex/kamiflex/HEAD/image/todos_index.png
--------------------------------------------------------------------------------
/test/dummy/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 |
--------------------------------------------------------------------------------
/image/show_cases/cards.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/etrex/kamiflex/HEAD/image/show_cases/cards.png
--------------------------------------------------------------------------------
/lib/kamiflex/railtie.rb:
--------------------------------------------------------------------------------
1 | module Kamiflex
2 | class Railtie < ::Rails::Railtie
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dummy",
3 | "private": true,
4 | "dependencies": {}
5 | }
6 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | end
3 |
--------------------------------------------------------------------------------
/test/dummy/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/lib/tasks/kamiflex_tasks.rake:
--------------------------------------------------------------------------------
1 | # desc "Explaining what the task does"
2 | # task :kamiflex do
3 | # # Task goes here
4 | # end
5 |
--------------------------------------------------------------------------------
/test/dummy/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/bin/test:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | $: << File.expand_path("../test", __dir__)
3 |
4 | require "bundler/setup"
5 | require "rails/plugin/test"
6 |
--------------------------------------------------------------------------------
/test/dummy/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/test/dummy/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/test/dummy/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/config/spring.rb:
--------------------------------------------------------------------------------
1 | %w[
2 | .ruby-version
3 | .rbenv-vars
4 | tmp/restart.txt
5 | tmp/caching-dev.txt
6 | ].each { |path| Spring.watch(path) }
7 |
--------------------------------------------------------------------------------
/test/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative 'config/environment'
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/test/dummy/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/test/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
3 | end
4 |
--------------------------------------------------------------------------------
/test/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/test/kamiflex_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class Kamiflex::Test < ActiveSupport::TestCase
4 | test "truth" do
5 | assert_kind_of Module, Kamiflex
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/example/custom_nav.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | bubble do
5 | body do
6 | nav "kamiflex"
7 | end
8 | end
9 | end
10 |
11 | puts json
--------------------------------------------------------------------------------
/test/dummy/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/lib/kamiflex/custom.rb:
--------------------------------------------------------------------------------
1 | module Kamiflex
2 | module Custom
3 | def nav(title, **params)
4 | horizontal_box do
5 | text 'nav'
6 | text title
7 | end
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/example/postback_button.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | bubble do
5 | body do
6 | postback_text_button "press me", "message", "data"
7 | end
8 | end
9 | end
10 |
11 | puts json
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | log/*.log
3 | pkg/
4 | test/dummy/db/*.sqlite3
5 | test/dummy/db/*.sqlite3-journal
6 | test/dummy/log/*.log
7 | test/dummy/node_modules/
8 | test/dummy/yarn-error.log
9 | test/dummy/storage/
10 | test/dummy/tmp/
11 | *.gem
--------------------------------------------------------------------------------
/example/carousel.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | carousel do
5 | bubbles [1,2,3] do |i|
6 | body do
7 | text "#{i}"
8 | end
9 | end
10 | end
11 | end
12 |
13 | puts json
--------------------------------------------------------------------------------
/test/dummy/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: dummy_production
11 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/test/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/test/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Set up gems listed in the Gemfile.
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
3 |
4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5 | $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
6 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | kamiflex (0.17.0)
5 | json
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | json (2.5.1)
11 |
12 | PLATFORMS
13 | ruby
14 |
15 | DEPENDENCIES
16 | kamiflex!
17 |
18 | BUNDLED WITH
19 | 2.2.25
20 |
--------------------------------------------------------------------------------
/example/carousel_with_index.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | carousel do
5 | bubbles ['a','b','c'] do |name, index|
6 | body do
7 | text "#{index}: #{name}"
8 | end
9 | end
10 | end
11 | end
12 |
13 | puts json
--------------------------------------------------------------------------------
/test/dummy/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/example/hero.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | bubble do
5 | hero 'https://via.placeholder.com/1024x400.png', "aspectRatio": "1024:400", "size": "full", "aspectMode": "cover"
6 | body do
7 | text "123"
8 | end
9 | end
10 | end
11 |
12 | puts json
13 |
--------------------------------------------------------------------------------
/example/uri_action_demo.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | alt_text "hello world!"
5 | bubble do
6 | body do
7 | text "這是文字方塊,但你點點看", action: uri_action("https://www.kamigo.tw/", desktop: "https://etrex.tw/")
8 | end
9 | end
10 | end
11 |
12 | puts json
13 |
--------------------------------------------------------------------------------
/example/url_button_demo.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | alt_text "hello world!"
5 | bubble do
6 | body do
7 | url_button "hello world!", "https://www.kamigo.tw", style: :primary, desktop: "https://etrex.tw/"
8 | end
9 | end
10 | end
11 |
12 | puts json
13 |
--------------------------------------------------------------------------------
/example/hello_world.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 | require 'clipboard'
3 |
4 | json = Kamiflex.json(self) do
5 | alt_text "hello world!"
6 | bubble do
7 | body borderColor: "#FF0000", borderWidth: :light do
8 | text "hello world!"
9 | end
10 | end
11 | end
12 |
13 | puts json
14 | Clipboard.copy json
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/test/dummy/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | begin
5 | exec "yarnpkg", *ARGV
6 | rescue Errno::ENOENT
7 | $stderr.puts "Yarn executable was not detected in the system."
8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9 | exit 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/example/apng_test.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 |
3 | hash = Kamiflex.hash(self) do
4 | alt_text "APNG 動畫測試"
5 | carousel do
6 | bubbles size: :giga do
7 | body paddingAll: "0px" do
8 | image "https://i.imgur.com/l505I10.png", size: :full, aspectRatio: "476:280", animated: true
9 | end
10 | end
11 | end
12 | end
13 |
14 | puts hash.to_json
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dummy
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 | <%= stylesheet_link_tag 'application', media: 'all' %>
9 | <%= javascript_include_tag 'application' %>
10 |
11 |
12 |
13 | <%= yield %>
14 |
15 |
16 |
--------------------------------------------------------------------------------
/example/500_button_test.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 |
3 | hash = Kamiflex.hash(self) do
4 | alt_text "500個按鈕測試"
5 | carousel do
6 | bubbles size: :giga do
7 | body paddingAll: "0px" do
8 | horizontal_box 125.times do |i|
9 | 4.times do |j|
10 | url_button "#{i*4+j+1}", "https://kamigo.tw"
11 | end
12 | end
13 | end
14 | end
15 | end
16 | end
17 |
18 | puts hash.to_json
--------------------------------------------------------------------------------
/example/box_with_resource.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | @resources = [
4 | {
5 | name: "ruby"
6 | },
7 | {
8 | name: "rails"
9 | },
10 | {
11 | name: "kamiflex"
12 | }
13 | ]
14 |
15 | json = Kamiflex.json(self) do
16 | bubble do
17 | body do
18 | vertical_box @resources do |resource|
19 | text "#{resource[:name]}"
20 | end
21 | end
22 | end
23 | end
24 |
25 | puts json
--------------------------------------------------------------------------------
/test/dummy/app/assets/javascripts/cable.js:
--------------------------------------------------------------------------------
1 | // Action Cable provides the framework to deal with WebSockets in Rails.
2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3 | //
4 | //= require action_cable
5 | //= require_self
6 | //= require_tree ./channels
7 |
8 | (function() {
9 | this.App || (this.App = {});
10 |
11 | App.cable = ActionCable.createConsumer();
12 |
13 | }).call(this);
14 |
--------------------------------------------------------------------------------
/example/box_with_resource_and_index.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | @resources = [
4 | {
5 | name: "ruby"
6 | },
7 | {
8 | name: "rails"
9 | },
10 | {
11 | name: "kamiflex"
12 | }
13 | ]
14 |
15 | json = Kamiflex.json(self) do
16 | bubble do
17 | body do
18 | vertical_box @resources do |resource, index|
19 | text "#{index}: #{resource[:name]}"
20 | end
21 | end
22 | end
23 | end
24 |
25 | puts json
--------------------------------------------------------------------------------
/test/dummy/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | begin
2 | require 'bundler/setup'
3 | rescue LoadError
4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5 | end
6 |
7 | require 'rdoc/task'
8 |
9 | RDoc::Task.new(:rdoc) do |rdoc|
10 | rdoc.rdoc_dir = 'rdoc'
11 | rdoc.title = 'Kamiflex'
12 | rdoc.options << '--line-numbers'
13 | rdoc.rdoc_files.include('README.md')
14 | rdoc.rdoc_files.include('lib/**/*.rb')
15 | end
16 |
17 | require 'bundler/gem_tasks'
18 |
19 | require 'rake/testtask'
20 |
21 | Rake::TestTask.new(:test) do |t|
22 | t.libs << 'test'
23 | t.pattern = 'test/**/*_test.rb'
24 | t.verbose = false
25 | end
26 |
27 | task default: :test
28 |
--------------------------------------------------------------------------------
/test/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | require 'rails/all'
4 |
5 | Bundler.require(*Rails.groups)
6 | require "kamiflex"
7 |
8 | module Dummy
9 | class Application < Rails::Application
10 | # Initialize configuration defaults for originally generated Rails version.
11 | config.load_defaults 5.2
12 |
13 | # Settings in config/environments/* take precedence over those specified here.
14 | # Application configuration can go into files in config/initializers
15 | # -- all .rb files in that directory are automatically loaded after loading
16 | # the framework and any gems in your application.
17 | end
18 | end
19 |
20 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | # Declare your gem's dependencies in kamiflex.gemspec.
5 | # Bundler will treat runtime dependencies like base dependencies, and
6 | # development dependencies will be added by default to the :development group.
7 | gemspec
8 |
9 | # Declare any dependencies that are still in development here instead of in
10 | # your gemspec. These might include edge Rails or gems from your path or
11 | # Git. Remember to move these dependencies to your gemspec before releasing
12 | # your gem to rubygems.org.
13 |
14 | # To use a debugger
15 | # gem 'byebug', group: [:development, :test]
16 |
--------------------------------------------------------------------------------
/example/menu.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | bubble do
5 | body do
6 | horizontal_box margin: "md" do
7 | message_button "道具1", "道具1", style: "primary", margin: "md"
8 | message_button "道具2", "道具2", style: "primary", margin: "md"
9 | message_button "道具3", "道具3", style: "primary", margin: "md"
10 | end
11 | horizontal_box margin: "md" do
12 | message_button "道具4", "道具4", style: "primary", margin: "md"
13 | message_button "道具5", "道具5", style: "primary", margin: "md"
14 | message_button "道具6", "道具6", style: "primary", margin: "md"
15 | end
16 | end
17 | end
18 | end
19 |
20 | puts json
21 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/test/dummy/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/example/absolute_box_test.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 |
3 | options = {
4 | position: :absolute,
5 | width: "37.5px",
6 | height: "37.5px",
7 | }
8 |
9 | colors = [
10 | "#333333",
11 | "#cccccc",
12 | ]
13 |
14 | hash = Kamiflex.hash(self) do
15 | alt_text "黑白棋盤"
16 | carousel do
17 | bubbles 2.times, size: :mega do
18 | body paddingAll: "0px", height: "300px" do
19 | 8.times do |x|
20 | 8.times do |y|
21 | color = colors[(x+y) % 2]
22 | horizontal_box **options, offsetTop: "#{x*37.5}px", offsetStart: "#{y*37.5}px", backgroundColor: color do
23 | end
24 | end
25 | end
26 | end
27 | end
28 | end
29 | end
30 |
31 | puts hash.to_json
--------------------------------------------------------------------------------
/test/dummy/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/example/bubble_size_test.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 |
3 | options = {
4 | position: :absolute,
5 | width: "20px",
6 | height: "20px",
7 | backgroundColor: "#cccccc"
8 | }
9 |
10 | # size = "nano"
11 | # size = "micro"
12 | # size = "kilo"
13 | # size = "mega"
14 | size = "giga"
15 |
16 | hash = Kamiflex.hash(self) do
17 | alt_text "Flex 尺寸測試"
18 | carousel do
19 | bubbles 2.times, size: size do
20 | body paddingAll: "0px", height: "500px" do
21 | text size, align: :center
22 | 25.times do |i|
23 | horizontal_box **options, offsetTop: "#{i*20}px", offsetStart: "#{i*20}px" do
24 | text "#{i}", align: :center, gravity: :center
25 | end
26 | end
27 | end
28 | end
29 | end
30 | end
31 |
32 | puts hash.to_json
--------------------------------------------------------------------------------
/lib/kamiflex/actions.rb:
--------------------------------------------------------------------------------
1 | module Kamiflex
2 | module Actions
3 | def message_action(label, **params)
4 | {
5 | type: "message",
6 | label: label,
7 | text: params[:text] || label
8 | }.merge(params)
9 | end
10 |
11 | def uri_action(uri, **params)
12 | action = {
13 | type: "uri",
14 | label: uri[0...40],
15 | uri: uri
16 | }
17 |
18 | return action if params.nil?
19 | return action if params[:desktop].nil?
20 |
21 | action[:altUri] = {
22 | desktop: params[:desktop]
23 | }
24 |
25 | action
26 | end
27 |
28 | def postback_action(data, **params)
29 | {
30 | type: "postback",
31 | data: data
32 | }.merge(params)
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require rails-ujs
14 | //= require activestorage
15 | //= require_tree .
16 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | # Configure Rails Environment
2 | ENV["RAILS_ENV"] = "test"
3 |
4 | require_relative "../test/dummy/config/environment"
5 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)]
6 | require "rails/test_help"
7 |
8 | # Filter out Minitest backtrace while allowing backtrace from other libraries
9 | # to be shown.
10 | Minitest.backtrace_filter = Minitest::BacktraceFilter.new
11 |
12 | require "rails/test_unit/reporter"
13 | Rails::TestUnitReporter.executable = 'bin/test'
14 |
15 | # Load fixtures from the engine
16 | if ActiveSupport::TestCase.respond_to?(:fixture_path=)
17 | ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__)
18 | ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
19 | ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
20 | ActiveSupport::TestCase.fixtures :all
21 | end
22 |
--------------------------------------------------------------------------------
/example/quick_reply_demo.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | bubble do
5 | body do
6 | horizontal_box margin: "md" do
7 | message_button "道具1", "道具1", style: "primary", margin: "md"
8 | message_button "道具2", "道具2", style: "primary", margin: "md"
9 | message_button "道具3", "道具3", style: "primary", margin: "md"
10 | end
11 | horizontal_box margin: "md" do
12 | message_button "道具4", "道具4", style: "primary", margin: "md"
13 | message_button "道具5", "道具5", style: "primary", margin: "md"
14 | message_button "道具6", "道具6", style: "primary", margin: "md"
15 | end
16 | end
17 | end
18 | quick_reply do
19 | message_action_item "道具1"
20 | message_action_item "道具2"
21 | message_action_item "道具3"
22 | message_action_item "道具4"
23 | message_action_item "道具5"
24 | message_action_item "道具6"
25 | end
26 | end
27 |
28 | puts json
29 |
--------------------------------------------------------------------------------
/test/dummy/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | puts "\n== Updating database =="
24 | system! 'bin/rails db:migrate'
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! 'bin/rails log:clear tmp:clear'
28 |
29 | puts "\n== Restarting application server =="
30 | system! 'bin/rails restart'
31 | end
32 |
--------------------------------------------------------------------------------
/test/dummy/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at http://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/example/todos_index.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | @todos = [
4 | {
5 | id: 1,
6 | name: "ruby"
7 | },
8 | {
9 | id: 2,
10 | name: "rails"
11 | },
12 | {
13 | id: 3,
14 | name: "kamiflex"
15 | }
16 | ]
17 |
18 | json = Kamiflex.json(self) do
19 | bubble do
20 | body do
21 | horizontal_box do
22 | text "🍔", flex: 0, action: message_action("/")
23 | text "Todos"
24 | text "🆕", align: "end", action: uri_action("https://www.kamigo.tw")
25 | end
26 | separator
27 | unless @todos.nil?
28 | vertical_box margin: "lg" do
29 | horizontal_box @todos, margin: "lg" do |todo|
30 | text todo[:name], action: message_action("/todos/#{todo[:id]}")
31 | text "❌", align: "end", action: message_action("DELETE /todos/#{todo[:id]}")
32 | end
33 | end
34 | else
35 | text "no contents yet", margin: "lg"
36 | end
37 | end
38 | end
39 | end
40 |
41 | puts json
--------------------------------------------------------------------------------
/test/dummy/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a starting point to setup your application.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | # puts "\n== Copying sample files =="
24 | # unless File.exist?('config/database.yml')
25 | # cp 'config/database.yml.sample', 'config/database.yml'
26 | # end
27 |
28 | puts "\n== Preparing database =="
29 | system! 'bin/rails db:setup'
30 |
31 | puts "\n== Removing old logs and tempfiles =="
32 | system! 'bin/rails log:clear tmp:clear'
33 |
34 | puts "\n== Restarting application server =="
35 | system! 'bin/rails restart'
36 | end
37 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 etrex kuo
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/lib/kamiflex/quick_reply.rb:
--------------------------------------------------------------------------------
1 | module Kamiflex
2 | module QuickReply
3 |
4 | def quick_reply
5 | _attributes, contents = flex_scope{ yield }
6 | @flex_attributes[:quickReply] = {
7 | items: contents
8 | }
9 | end
10 |
11 | def action_item(action)
12 | @flex_contents << {
13 | type: "action",
14 | action: action
15 | }
16 | end
17 |
18 | def message_action_item(label, **params)
19 | action_item(message_action(label, **params))
20 | end
21 |
22 | def postback_action_item(label, **params)
23 | action_item(postback_action(label, **params))
24 | end
25 |
26 | def datetime_picker_action_item(label, **params)
27 | action_item(message_action(label, **params))
28 | end
29 |
30 | def camera_action_item(label, **params)
31 | action_item(camera_action(label, **params))
32 | end
33 |
34 | def camera_roll_action_item(label, **params)
35 | action_item(camera_roll_action(label, **params))
36 | end
37 |
38 | def location_action_item(label, **params)
39 | action_item(location_action(label, **params))
40 | end
41 | end
42 | end
--------------------------------------------------------------------------------
/test/dummy/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 |
15 | # # Specify URI for violation reports
16 | # # policy.report_uri "/csp-violation-report-endpoint"
17 | # end
18 |
19 | # If you are using UJS then enable automatic nonce generation
20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
21 |
22 | # Report CSP violations to a specified URI
23 | # For further information see the following documentation:
24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
25 | # Rails.application.config.content_security_policy_report_only = true
26 |
--------------------------------------------------------------------------------
/test/dummy/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/lib/kamiflex.rb:
--------------------------------------------------------------------------------
1 | require "json"
2 | require_relative "./kamiflex/version"
3 | require_relative "./kamiflex/core"
4 | require_relative "./kamiflex/basic_elements"
5 | require_relative "./kamiflex/actions"
6 | require_relative "./kamiflex/quick_reply"
7 | require_relative "./kamiflex/custom"
8 |
9 | module Kamiflex
10 | def self.hash(parent, &block)
11 | parent.class.include Kamiflex::Core
12 | parent.class.include Kamiflex::BasicElements
13 | parent.class.include Kamiflex::Actions
14 | parent.class.include Kamiflex::QuickReply
15 | parent.class.include Kamiflex::Custom
16 |
17 | parent.instance_exec do
18 | flex do
19 | parent.instance_exec(&block)
20 | end
21 | end
22 | end
23 |
24 | def self.to_hash(parent, &block)
25 | self.hash(parent, &block)
26 | end
27 |
28 | def self.build(parent, &block)
29 | JSON.pretty_generate self.hash(parent, &block)
30 | end
31 |
32 | def self.json(parent, &block)
33 | self.build(parent, &block)
34 | end
35 |
36 | def self.compact_json(parent, &block)
37 | self.to_hash(parent, &block).to_json
38 | end
39 | end
40 |
41 | Mime::Type.register_alias "application/json", :line if defined?(Rails)
42 | require_relative "./kamiflex/railtie" if defined?(Rails)
--------------------------------------------------------------------------------
/kamiflex.gemspec:
--------------------------------------------------------------------------------
1 | $:.push File.expand_path("lib", __dir__)
2 |
3 | # Maintain your gem's version:
4 | require "kamiflex/version"
5 |
6 | # Describe your gem and declare its dependencies:
7 | Gem::Specification.new do |spec|
8 | spec.name = "kamiflex"
9 | spec.version = Kamiflex::VERSION
10 | spec.authors = ["etrex kuo"]
11 | spec.email = ["et284vu065k3@gmail.com"]
12 | spec.homepage = "https://github.com/etrex/kamiflex"
13 | spec.summary = "Kamiflex: generate JSON objects for Line flex message with a Builder-style DSL"
14 | spec.description = "Kamiflex provide you a clear way to manage your flex message. It's easy to build an complex flex message with conditionals and loops."
15 | spec.license = "MIT"
16 |
17 | # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18 | # to allow pushing to a single host or delete this section to allow pushing to any host.
19 | if spec.respond_to?(:metadata)
20 | spec.metadata["allowed_push_host"] = "https://rubygems.org"
21 | else
22 | raise "RubyGems 2.0 or newer is required to protect against " \
23 | "public gem pushes."
24 | end
25 |
26 | spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
27 |
28 | spec.add_dependency "json"
29 | end
30 |
--------------------------------------------------------------------------------
/example/show_cases/menu.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | json = Kamiflex.json(self) do
5 | alt_text "LINE Flex Message Showcase: Menu"
6 | bubble do
7 | hero "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png", size: :full, aspectRatio: "20:13", aspectMode: :cover, action: uri_action("https://linecorp.com")
8 | body spacing: :md, action: uri_action("https://linecorp.com") do
9 | text "Brown's Burger", size: :xl, weight: :bold
10 | vertical_box do
11 | baseline_box do
12 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/restaurant_regular_32.png"
13 | text "$10.5", weight: :bold, margin: :sm, flex: 0
14 | text "400kcl", size: :sm, align: :end, color: "#aaaaaa"
15 | end
16 |
17 | baseline_box do
18 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/restaurant_large_32.png"
19 | text "$15.5", weight: :bold, margin: :sm, flex: 0
20 | text "550kcl", size: :sm, align: :end, color: "#aaaaaa"
21 | end
22 | end
23 | text "Sauce, Onions, Pickles, Lettuce & Cheese", wrap: true, color: "#aaaaaa", size: :xxs
24 | end
25 |
26 | footer do
27 | spacer size: :xxl
28 | url_button "Add to Cart", "https://linecorp.com", style: :primary, color: "#905c44"
29 | end
30 | end
31 | end
32 |
33 | puts json
34 | Clipboard.copy json
35 |
--------------------------------------------------------------------------------
/test/dummy/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | threads threads_count, threads_count
9 |
10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
11 | #
12 | port ENV.fetch("PORT") { 3000 }
13 |
14 | # Specifies the `environment` that Puma will run in.
15 | #
16 | environment ENV.fetch("RAILS_ENV") { "development" }
17 |
18 | # Specifies the number of `workers` to boot in clustered mode.
19 | # Workers are forked webserver processes. If using threads and workers together
20 | # the concurrency of the application would be max `threads` * `workers`.
21 | # Workers do not work on JRuby or Windows (both of which do not support
22 | # processes).
23 | #
24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
25 |
26 | # Use the `preload_app!` method when specifying a `workers` number.
27 | # This directive tells Puma to first boot the application and load code
28 | # before forking the application. This takes advantage of Copy On Write
29 | # process behavior so workers use less memory.
30 | #
31 | # preload_app!
32 |
33 | # Allow puma to be restarted by `rails restart` command.
34 | plugin :tmp_restart
35 |
--------------------------------------------------------------------------------
/example/show_cases/receipt.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | def receipt_item(key, value, **options)
5 | horizontal_box **options do
6 | text key, size: "sm", color: "#555555", flex: 0
7 | text value, size: "sm", color: "#111111", align: "end"
8 | end
9 | end
10 |
11 | json = Kamiflex.json(self) do
12 | alt_text "LINE Flex Message Showcase: Receipt"
13 |
14 | bubble do
15 | body do
16 | vertical_box do
17 | text "RECEIPT", weight: :bold, color: "#1DB446", size: :sm
18 | text "Brown Store", weight: :bold, size: :xxl, margin: :md
19 | text "Miraina Tower, 4-1-6 Shinjuku, Tokyo", size: :xs, color: "#aaaaaa", wrap: true
20 | separator margin: :xxl, color: "#eaeaea"
21 | vertical_box margin: :xxl, spacing: :sm do
22 | receipt_item "Energy Drink", "$2.99"
23 | receipt_item "Chewing Gum", "$0.99"
24 | receipt_item "Bottled Water", "$3.33"
25 | separator margin: :xxl, color: "#eaeaea"
26 | receipt_item "ITEMS", "3", margin: :xxl
27 | receipt_item "TOTAL", "$7.31"
28 | receipt_item "CASH", "$8.0"
29 | receipt_item "CHANGE", "$0.69"
30 | separator margin: :xxl, color: "#eaeaea"
31 | horizontal_box margin: :md do
32 | text "PAYMENT ID", size: :xs, color: "#aaaaaa", flex: 0
33 | text "#743289384279", color: "#aaaaaa", size: :xs, align: :end
34 | end
35 | end
36 | end
37 | end
38 | styles footer: {separator: true}
39 | end
40 | end
41 |
42 | puts json
43 | Clipboard.copy json
44 |
--------------------------------------------------------------------------------
/example/show_cases/shopping.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | @products = [
5 | {
6 | name: "Arm Chair, White",
7 | price: 49.99,
8 | inventory: true,
9 | image: "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_5_carousel.png"
10 | },
11 | {
12 | name: "Metal Desk Lamp",
13 | price: 11.99,
14 | inventory: false,
15 | image: "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_6_carousel.png"
16 | },
17 | ]
18 |
19 | def shopping_price(price)
20 | baseline_box do
21 | price_parts = price.to_s.split(".")
22 | text "$#{price_parts[0]}", wrap: true, weight: :bold, size: :xl, flex: 0
23 | text ".#{price_parts[1]}", wrap: true, weight: :bold, size: :sm, flex: 0
24 | end
25 | end
26 |
27 | json = Kamiflex.json(self) do
28 | alt_text "LINE Flex Message Showcase: Shopping"
29 |
30 | carousel do
31 | bubbles @products do |product|
32 | hero product[:image], size: :full, aspectRatio: "20:13", aspectMode: :cover
33 | body spacing: :sm do
34 | text product[:name], wrap: true, weight: :bold, size: :xl
35 | shopping_price product[:price]
36 | unless product[:inventory]
37 | text "Temporarily out of stock", wrap: true, size: :xxs, margin: :md, color: "#ff5551", flex: 0
38 | end
39 | end
40 | footer do
41 | color = product[:inventory] ? "#00c300" : "#aaaaaa"
42 | url_button "Add to Cart", "https://linecorp.com", style: :primary, color: color
43 | url_button "Add to wishlist", "https://linecorp.com"
44 | end
45 | end
46 | end
47 | end
48 |
49 | puts json
50 | Clipboard.copy json
51 |
--------------------------------------------------------------------------------
/example/show_cases/social.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | json = Kamiflex.json(self) do
5 | alt_text "LINE Flex Message Showcase: Social"
6 |
7 | bubble do
8 | body paddingAll: "0px" do
9 | horizontal_box do
10 | image "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip7.jpg", size: "5xl", aspectMode: :cover, aspectRatio: "150:196", gravity: :center, flex: 1
11 |
12 | vertical_box flex: 1 do
13 | image "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip8.jpg", size: :full, aspectMode: :cover, aspectRatio: "150:98", gravity: :center
14 | image "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip9.jpg", size: :full, aspectMode: :cover, aspectRatio: "150:98", gravity: :center
15 | end
16 | end
17 |
18 | horizontal_box spacing: :xl, paddingAll: "20px" do
19 | vertical_box cornerRadius: "100px", width: "72px", height: "72px" do
20 | image "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip13.jpg", aspectMode: :cover, size: :full
21 | end
22 |
23 | vertical_box do
24 | text size: :sm, wrap: true do
25 | span "brown_05", weight: :bold, color: "#000000"
26 | span " "
27 | span "I went to the Brown&Cony cafe in Tokyo and took a picture"
28 | end
29 |
30 | baseline_box spacing: :sm, margin: :md do
31 | text "1,140,753 Like", size: :sm, color: "#bcbcbc"
32 | end
33 | end
34 | end
35 | end
36 | end
37 | end
38 |
39 | puts json
40 | Clipboard.copy json
41 |
--------------------------------------------------------------------------------
/example/show_cases/local_search.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | @local_searchs = [
5 | {
6 | name: "Brown Cafe",
7 | rating: 4,
8 | image: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip10.jpg"
9 | },
10 | {
11 | name: "Brow&Cony's Restaurant",
12 | rating: 4,
13 | image: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip11.jpg"
14 | },
15 | {
16 | name: "Tata",
17 | rating: 4,
18 | image: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip12.jpg"
19 | },
20 | ]
21 |
22 | def star_xs(n, m=5)
23 | baseline_box spacing: :xs do
24 | n.times do
25 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: :xs
26 | end
27 | (m-n).times do
28 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png", size: :xs
29 | end
30 | text "#{n}.0", color: "#8c8c8c", size: :xs, margin: :md, flex: 0
31 | end
32 | end
33 |
34 | json = Kamiflex.json(self) do
35 | alt_text "LINE Flex Message Showcase: Local Search"
36 | carousel do
37 | bubbles @local_searchs, size: :micro do |local_search|
38 | hero local_search[:image], size: :full, aspectMode: "cover", aspectRatio: "320:213"
39 | body spacing: :sm, paddingAll: "13px" do
40 | text local_search[:name], weight: :bold, size: :sm, wrap: true
41 |
42 | star_xs local_search[:rating]
43 |
44 | vertical_box do
45 | baseline_box spacing: :sm do
46 | text "東京旅行", wrap: true, color: "#8c8c8c", size: :xs, flex: 5
47 | end
48 | end
49 | end
50 | end
51 | end
52 | end
53 |
54 | puts json
55 | Clipboard.copy json
56 |
--------------------------------------------------------------------------------
/example/show_cases/todo_app.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | @todos = [
5 | {
6 | background_color: "#27ACB2",
7 | state_color: "#0D8186",
8 | state: "In Progress",
9 | percentage: "70%",
10 | statement: "Buy milk and lettuce before class",
11 | },
12 | {
13 | background_color: "#FF6B6E",
14 | state_color: "#DE5658",
15 | state: "Pending",
16 | percentage: "30%",
17 | statement: "Wash my car",
18 | },
19 | {
20 | background_color: "#A17DF5",
21 | state_color: "#7D51E4",
22 | state: "In Progress",
23 | percentage: "100%",
24 | statement: "Buy milk and lettuce before class",
25 | }
26 | ]
27 |
28 | json = Kamiflex.json(self) do
29 | alt_text "LINE Flex Message Showcase: TODO App"
30 |
31 | carousel do
32 | @todos.each do |todo|
33 | bubble size: :nano do
34 | header backgroundColor: todo[:background_color], paddingTop: "19px", paddingAll: "12px", paddingBottom: "16px" do
35 | text todo[:state], color: "#ffffff", align: :start, size: :md, gravity: :center
36 | text todo[:percentage], color: "#ffffff", align: :start, size: :xs, gravity: :center, margin: :lg
37 | vertical_box backgroundColor: "#9FD8E36E", height: "6px", margin: :sm do
38 | vertical_box width: todo[:percentage], backgroundColor: todo[:state_color], height: "6px" do
39 | filler
40 | end
41 | end
42 | end
43 | body spacing: :md, paddingAll: "12px" do
44 | horizontal_box flex: 1 do
45 | text todo[:statement], color: "#8C8C8C", size: :sm, wrap: true
46 | end
47 | end
48 | styles footer: { separator: false }
49 | end
50 | end
51 | end
52 | end
53 |
54 | puts json
55 | Clipboard.copy json
56 |
--------------------------------------------------------------------------------
/example/transit.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 |
3 | json = Kamiflex.json(self) do
4 | bubble do
5 | body do
6 | vertical_box do
7 | text "total 1 hour", color: "#b7b7b7", size: "xs"
8 | horizontal_box spacing: "lg", cornerRadius: "30px", margin: "xl" do
9 | text "20:30", color: "#b7b7b7", size: "sm", gravity: "center"
10 | vertical_box flex: 0 do
11 | filler
12 | vertical_box cornerRadius: "30px", height: "12px", width: "12px", borderColor: "#6486E3", borderWidth: "2px" do
13 | filler
14 | end
15 | filler
16 | end
17 | text "A station", size: "sm", gravity: "center", flex: 4
18 | end
19 |
20 | horizontal_box spacing: "lg", height: "64px" do
21 | baseline_box flex: 1 do
22 | filler
23 | end
24 | vertical_box width: "12px" do
25 | horizontal_box flex: 1 do
26 | filler
27 | vertical_box width: "2px", backgroundColor: "#B7B7B7" do
28 | filler
29 | end
30 | filler
31 | end
32 | end
33 | text "walk 1 hour", gravity: "center", flex: 4, size: "xs", color: "#8c8c8c"
34 | end
35 | horizontal_box spacing: "lg", cornerRadius: "30px" do
36 | text "21:30", color: "#b7b7b7", size: "sm", gravity: "center"
37 | vertical_box flex: 0 do
38 | filler
39 | vertical_box cornerRadius: "30px", height: "12px", width: "12px", borderColor: "#6486E3", borderWidth: "2px" do
40 | filler
41 | end
42 | filler
43 | end
44 | text "B station", size: "sm", gravity: "center", flex: 4
45 | end
46 | end
47 |
48 |
49 | end
50 | end
51 | end
52 |
53 | puts json
54 |
--------------------------------------------------------------------------------
/example/show_cases/real_estate.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | json = Kamiflex.json(self) do
5 | alt_text "LINE Flex Message Showcase: Real Estate"
6 |
7 | bubble do
8 | header paddingAll: "0px" do
9 | horizontal_box do
10 | image "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip4.jpg", size: :full, aspectMode: :cover, aspectRatio: "150:196", gravity: :center, flex: 1
11 | vertical_box flex: 1 do
12 | image "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip5.jpg", size: :full, aspectMode: :cover, aspectRatio: "150:98", gravity: :center
13 | image "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip6.jpg", size: :full, aspectMode: :cover, aspectRatio: "150:98", gravity: :center
14 | end
15 |
16 | horizontal_box backgroundColor: "#EC3D44", paddingAll: "2px", paddingStart: "4px", paddingEnd: "4px", flex: 0,
17 | position: :absolute, offsetStart: "18px", offsetTop: "18px", cornerRadius: "100px", width: "48px", height: "25px" do
18 | text "NEW", size: :xs, color: "#ffffff", align: :center, gravity: :center
19 | end
20 | end
21 | end
22 |
23 | body paddingAll: "20px", backgroundColor: "#464F69" do
24 | vertical_box spacing: "sm" do
25 | text "Cony Residence", size: :xl, wrap: true, color: "#ffffff", weight: :bold
26 | text "3 Bedrooms, ¥35,000", color: "#ffffffcc", size: :sm
27 | end
28 |
29 | vertical_box paddingAll: "13px", backgroundColor: "#ffffff1A", cornerRadius: "2px", margin: :xl do
30 | vertical_box do
31 | text "Private Pool, Delivery box, Floor heating, Private Cinema", size: :sm, wrap: true, margin: :lg, color: "#ffffffde"
32 | end
33 | end
34 | end
35 | end
36 | end
37 |
38 | puts json
39 | Clipboard.copy json
40 |
--------------------------------------------------------------------------------
/example/show_cases/hotel.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | def star(num)
5 | baseline_box margin: :md do
6 | (0...num).each do
7 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: :sm
8 | end
9 | (num...5).each do
10 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png", size: :sm
11 | end
12 | text "#{num}.0", size: :sm, color: "#999999", margin: :md, flex: 0
13 | end
14 | end
15 |
16 | json = Kamiflex.json(self) do
17 | alt_text "LINE Flex Message Showcase: Hotel"
18 | bubble do
19 | body paddingAll: "0px" do
20 | image "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip3.jpg", size: :full, aspectMode: :cover, aspectRatio: "1:1", gravity: :center
21 |
22 | vertical_box position: :absolute, width: "100%", height: "40%", offsetBottom: "0px", offsetStart: "0px", offsetEnd: "0px",
23 | background: {
24 | type: :linearGradient,
25 | angle: "0deg",
26 | endColor: "#00000000",
27 | startColor: "#00000099"
28 | } do
29 | end
30 |
31 | horizontal_box position: :absolute, offsetBottom: "0px", offsetStart: "0px", offsetEnd: "0px", paddingAll: "20px" do
32 | vertical_box spacing: :xs do
33 | horizontal_box do
34 | text "Brown Grand Hotel", size: :xl, color: "#ffffff"
35 | end
36 |
37 | star 4
38 |
39 | horizontal_box do
40 | baseline_box spacing: :lg, flex: 0 do
41 | text "¥62,000", color: "#ffffff", size: :md, flex: 0, align: :end
42 | text "¥82,000", color: "#a9a9a9", decoration: "line-through", size: :sm, align: :end
43 | end
44 | end
45 | end
46 | end
47 | end
48 | end
49 | end
50 |
51 | puts json
52 | Clipboard.copy json
--------------------------------------------------------------------------------
/example/show_cases/restaurant.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | json = Kamiflex.json(self) do
5 | alt_text "LINE Flex Message Showcase: Restaurant"
6 |
7 | bubble do
8 | hero "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png",
9 | size: :full, aspectRatio: "20:13", aspectMode: :cover, action: uri_action("http://linecorp.com/")
10 | body do
11 | text "Brown Cafe", weight: :bold, size: :xl
12 | baseline_box margin: :md do
13 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: :sm
14 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: :sm
15 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: :sm
16 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: :sm
17 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png", size: :sm
18 | text "4.0", size: :sm, color: "#999999", margin: :md, flex: 0
19 | end
20 | vertical_box margin: :lg, spacing: :sm do
21 | baseline_box spacing: :sm do
22 | text "Place", color: "#aaaaaa", size: :sm, flex: 1
23 | text "Miraina Tower, 4-1-6 Shinjuku, Tokyo", wrap: true, color: "#666666", size: :sm, flex: 5
24 | end
25 | baseline_box spacing: :sm do
26 | text "Time", color: "#aaaaaa", size: :sm, flex: 1
27 | text "10:00 - 23:00", "wrap": true, color: "#666666", size: :sm, flex: 5
28 | end
29 | end
30 | end
31 | footer spacing: :sm, flex: 0 do
32 | url_button "CALL", "https://linecorp.com", style: :link, height: :sm
33 | url_button "WEBSITE", "https://linecorp.com", style: :link, height: :sm
34 | spacer size: :sm
35 | end
36 | end
37 | end
38 |
39 | puts json
40 | Clipboard.copy json
41 |
--------------------------------------------------------------------------------
/test/dummy/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/test/dummy/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/dummy/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/example/show_cases/ticket.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | def star_sm(n, m=5)
5 | baseline_box margin: :md do
6 | n.times do
7 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: :sm
8 | end
9 | (m-n).times do
10 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png", size: :sm
11 | end
12 | text "#{n}.0", color: "#999999", size: :sm, margin: :md, flex: 0
13 | end
14 | end
15 |
16 | json = Kamiflex.json(self) do
17 | alt_text "LINE Flex Message Showcase: Ticket"
18 |
19 | bubble do
20 | hero "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_3_movie.png", size: :full, aspectRatio: "20:13", aspectMode: :cover, action: uri_action("http://linecorp.com/")
21 | body spacing: :md do
22 | text "BROWN'S ADVENTURE\nIN MOVIE", wrap: true, weight: :bold, gravity: :center, size: :xl
23 | star_sm 4
24 | vertical_box margin: :lg, spacing: :sm do
25 | baseline_box spacing: :sm do
26 | text "Date", color: "#aaaaaa", size: :sm, flex: 1
27 | text "Monday 25, 9:00PM", wrap: true, size: :sm, color: "#666666", flex: 4
28 | end
29 |
30 | baseline_box spacing: :sm do
31 | text "Place", color: "#aaaaaa", size: :sm, flex: 1
32 | text "7 Floor, No.3", wrap: true, color: "#666666", size: :sm, flex: 4
33 | end
34 |
35 | baseline_box spacing: :sm do
36 | text "Seats", "color": "#aaaaaa", size: :sm, flex: 1
37 | text "C Row, 18 Seat", wrap: true, color: "#666666", size: :sm, flex: 4
38 | end
39 | end
40 |
41 | vertical_box margin: :xxl do
42 | spacer
43 | image "https://scdn.line-apps.com/n/channel_devcenter/img/fx/linecorp_code_withborder.png", aspectMode: :cover, size: :xl
44 | text "You can enter the theater by using this code instead of a ticket", color: "#aaaaaa", wrap: true, margin: :xxl, size: :xs
45 | end
46 | end
47 | end
48 | end
49 |
50 | puts json
51 | Clipboard.copy json
52 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure public file server for tests with Cache-Control for performance.
16 | config.public_file_server.enabled = true
17 | config.public_file_server.headers = {
18 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
19 | }
20 |
21 | # Show full error reports and disable caching.
22 | config.consider_all_requests_local = true
23 | config.action_controller.perform_caching = false
24 |
25 | # Raise exceptions instead of rendering exception templates.
26 | config.action_dispatch.show_exceptions = false
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 |
31 | # Store uploaded files on the local file system in a temporary directory
32 | config.active_storage.service = :test
33 |
34 | config.action_mailer.perform_caching = false
35 |
36 | # Tell Action Mailer not to deliver emails to the real world.
37 | # The :test delivery method accumulates sent emails in the
38 | # ActionMailer::Base.deliveries array.
39 | config.action_mailer.delivery_method = :test
40 |
41 | # Print deprecation notices to the stderr.
42 | config.active_support.deprecation = :stderr
43 |
44 | # Raises error for missing translations
45 | # config.action_view.raise_on_missing_translations = true
46 | end
47 |
--------------------------------------------------------------------------------
/example/cards.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | @products = [
5 | {
6 | name: "kamigo",
7 | star: 4,
8 | place: "https://github.com/etrex/kamigo",
9 | time: "10:00 - 23:00",
10 | contact_url: "https://github.com/etrex/kamigo",
11 | product_url: "https://github.com/etrex/kamigo"
12 | },
13 | {
14 | name: "kamiliff",
15 | star: 3,
16 | place: "https://github.com/etrex/kamiliff",
17 | time: "11:00 - 23:00",
18 | contact_url: "https://github.com/etrex/kamiliff",
19 | product_url: "https://github.com/etrex/kamiliff"
20 | },
21 | {
22 | name: "kamiflex",
23 | star: 5,
24 | place: "https://github.com/etrex/kamiflex",
25 | time: "09:00 - 23:00",
26 | contact_url: "https://github.com/etrex/kamiflex",
27 | product_url: "https://github.com/etrex/kamiflex"
28 | },
29 | ]
30 |
31 | def star(num)
32 | baseline_box margin: :md do
33 | (0...num).each do
34 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: :sm
35 | end
36 | (num...5).each do
37 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png", size: :sm
38 | end
39 | text "#{num}.0", size: :sm, color: "#999999", margin: :md, flex: 0
40 | end
41 | end
42 |
43 | def field(key, value)
44 | baseline_box spacing: :sm do
45 | text key, "color":"#aaaaaa","size":"sm","flex":1
46 | text value, "wrap":true,"color":"#666666","size":"sm","flex":5
47 | end
48 | end
49 |
50 | json = Kamiflex.json(self) do
51 | alt_text "LINE Flex Message Showcase: Cards"
52 | carousel do
53 | bubbles @products do |product|
54 | hero "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png",
55 | size: :full, aspectRatio: "20:13", aspectMode: :cover, action: uri_action("http://linecorp.com/")
56 | body do
57 | text product[:name], weight: :bold, size: :xl
58 | star(product[:star])
59 | vertical_box margin: :lg, spacing: :sm do
60 | field "Place", product[:place]
61 | field "Time", product[:time]
62 | end
63 | end
64 | footer spacing: :sm, flex: 0 do
65 | url_button "CALL", product[:contact_url], style: :link, height: :sm
66 | url_button "WEBSITE", product[:product_url], style: :link, height: :sm
67 | spacer size: :sm
68 | end
69 | end
70 | end
71 | end
72 |
73 | puts json
--------------------------------------------------------------------------------
/test/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | # Run rails dev:cache to toggle caching.
17 | if Rails.root.join('tmp', 'caching-dev.txt').exist?
18 | config.action_controller.perform_caching = true
19 |
20 | config.cache_store = :memory_store
21 | config.public_file_server.headers = {
22 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
23 | }
24 | else
25 | config.action_controller.perform_caching = false
26 |
27 | config.cache_store = :null_store
28 | end
29 |
30 | # Store uploaded files on the local file system (see config/storage.yml for options)
31 | config.active_storage.service = :local
32 |
33 | # Don't care if the mailer can't send.
34 | config.action_mailer.raise_delivery_errors = false
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | # Print deprecation notices to the Rails logger.
39 | config.active_support.deprecation = :log
40 |
41 | # Raise an error on page load if there are pending migrations.
42 | config.active_record.migration_error = :page_load
43 |
44 | # Highlight code that triggered database queries in logs.
45 | config.active_record.verbose_query_logs = true
46 |
47 | # Debug mode disables concatenation and preprocessing of assets.
48 | # This option may cause significant delays in view rendering with a large
49 | # number of complex assets.
50 | config.assets.debug = true
51 |
52 | # Suppress logger output for asset requests.
53 | config.assets.quiet = true
54 |
55 | # Raises error for missing translations
56 | # config.action_view.raise_on_missing_translations = true
57 |
58 | # Use an evented file watcher to asynchronously detect changes in source code,
59 | # routes, locales, etc. This feature depends on the listen gem.
60 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
61 | end
62 |
--------------------------------------------------------------------------------
/example/show_cases/apparel.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | def delimited(number)
5 | number.to_s.reverse.scan(/.{1,3}/).join(',').reverse
6 | end
7 |
8 | @apparels = [
9 | {
10 | name: "Brown's T-shirts",
11 | price: 35800,
12 | original_price: 75000,
13 | color: "#03303Acc",
14 | image: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip1.jpg"
15 | },
16 | {
17 | name: "Cony's T-shirts",
18 | price: 35800,
19 | original_price: 75000,
20 | color: "#9C8E7Ecc",
21 | image: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip2.jpg"
22 | }
23 | ]
24 |
25 | json = Kamiflex.json(self) do
26 | alt_text "LINE Flex Message Showcase: Apparel"
27 | carousel do
28 | bubbles @apparels do |apparel|
29 | body paddingAll: "0px" do
30 | # 主要圖片
31 | image apparel[:image], size: :full, aspectMode: :cover, aspectRatio: "2:3", gravity: :top
32 |
33 | vertical_box position: :absolute, offsetBottom: "0px", offsetStart: "0px", offsetEnd: "0px", backgroundColor: apparel[:color], paddingAll: "20px", paddingTop: "18px" do
34 | # 品名
35 | vertical_box do
36 | text apparel[:name], size: :xl, color: "#ffffff", weight: :bold
37 | end
38 |
39 | # 價格
40 | baseline_box spacing: :lg do
41 | text "¥#{delimited(apparel[:price])}", color: "#ebebeb", size: :sm, flex: 0
42 | text "¥#{delimited(apparel[:original_price])}", color: "#ffffffcc", decoration: "line-through", gravity: :bottom, flex: 0, size: :sm
43 | end
44 |
45 | # 加入購物車
46 | vertical_box borderWidth: "1px", cornerRadius: "4px", spacing: :sm, borderColor: "#ffffff", margin: :xxl, height: "40px" do
47 | filler
48 | baseline_box spacing: :sm do
49 | filler
50 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip14.png"
51 | text "Add to cart", color: "#ffffff", flex: 0, offsetTop: "-2px"
52 | filler
53 | end
54 | filler
55 | end
56 | end
57 |
58 | # 左上角的標記
59 | vertical_box position: :absolute, cornerRadius: "20px", offsetTop: "18px", backgroundColor: "#ff334b", offsetStart: "18px", height: "25px", width: "53px" do
60 | text "SALE", color: "#ffffff", align: :center, size: :xs, offsetTop: "3px"
61 | end
62 | end
63 | end
64 | end
65 | end
66 |
67 | puts json
68 | Clipboard.copy json
--------------------------------------------------------------------------------
/lib/kamiflex/basic_elements.rb:
--------------------------------------------------------------------------------
1 | module Kamiflex
2 | module BasicElements
3 | def text(message = nil, **params)
4 | if block_given?
5 | _attributes, contents = flex_scope{ yield }
6 | @flex_contents << {
7 | type: "text",
8 | contents: contents,
9 | }.merge(params)
10 | else
11 | @flex_contents << {
12 | type: "text",
13 | text: message
14 | }.merge(params)
15 | end
16 | end
17 |
18 | def span(message, **params)
19 | @flex_contents << {
20 | "type": "span",
21 | "text": message
22 | }.merge(params)
23 | end
24 |
25 | def image(url, **params)
26 | @flex_contents << {
27 | "type": "image",
28 | "url": url
29 | }.merge(params)
30 | end
31 |
32 | def icon(url, **params)
33 | @flex_contents << {
34 | "type": "icon",
35 | "url": url
36 | }.merge(params)
37 | end
38 |
39 | def separator(**params)
40 | @flex_contents << {
41 | "type": "separator",
42 | "margin": "md",
43 | "color": "#000000",
44 | }.merge(params)
45 | end
46 |
47 | def spacer(**params)
48 | @flex_contents << {
49 | "type": "spacer",
50 | "size": "md",
51 | }.merge(params)
52 | end
53 |
54 | def filler
55 | @flex_contents << {
56 | "type": "filler"
57 | }
58 | end
59 |
60 | def url_button(label, url, **params)
61 | action = {
62 | type: "uri",
63 | label: label,
64 | uri: url,
65 | }
66 |
67 | if params&.dig(:desktop)
68 | action[:altUri] = {
69 | desktop: params&.dig(:desktop)
70 | }
71 | params = params.reject {|key, value| key == :desktop }
72 | end
73 |
74 | @flex_contents << {
75 | type: "button",
76 | action: action
77 | }.merge(params)
78 | end
79 |
80 | def message_button(label, message, **params)
81 | @flex_contents << {
82 | "type": "button",
83 | "action": {
84 | "type": "message",
85 | "label": label,
86 | "text": message
87 | }
88 | }.merge(params)
89 | end
90 |
91 | def postback_button(label, data, **params)
92 | @flex_contents << {
93 | "type": "button",
94 | "action": {
95 | "type": "postback",
96 | "label": label,
97 | "data": data,
98 | }
99 | }.merge(params)
100 | end
101 |
102 | def postback_text_button(label, message, data, **params)
103 | @flex_contents << {
104 | "type": "button",
105 | "action": {
106 | "type": "postback",
107 | "label": label,
108 | "displayText": message,
109 | "data": data,
110 | }
111 | }.merge(params)
112 | end
113 | end
114 | end
115 |
--------------------------------------------------------------------------------
/example/show_cases/transit.rb:
--------------------------------------------------------------------------------
1 | require 'kamiflex'
2 | require 'clipboard'
3 |
4 | json = Kamiflex.json(self) do
5 | alt_text "LINE Flex Message Showcase: Transit"
6 |
7 | bubble size: "mega" do
8 | header paddingAll: "20px", backgroundColor: "#0367D3", spacing: :md, height: "154px", paddingTop: "22px" do
9 | vertical_box do
10 | text "FROM", color: "#ffffff66", size: :sm
11 | text "Akihabara", color: "#ffffff", size: :xl, flex: 4, weight: :bold
12 | end
13 | vertical_box do
14 | text "TO", color: "#ffffff66", size: :sm
15 | text "Shinjuku", color: "#ffffff", size: :xl, flex: 4, weight: :bold
16 | end
17 | end
18 | body do
19 | text "Total: 1 hour", color: "#b7b7b7", size: :xs
20 |
21 | horizontal_box spacing: :lg, cornerRadius: "30px", margin: :xl do
22 | text "20:30", size: :sm, gravity: :center
23 | vertical_box flex: 0 do
24 | filler
25 | vertical_box cornerRadius: "30px", height: "12px", width: "12px", borderColor: "#EF454D", borderWidth: "2px" do
26 | end
27 | filler
28 | end
29 | text "Akihabara", gravity: :center, flex: 4, size: :sm
30 | end
31 |
32 | horizontal_box spacing: :lg, height: "64px" do
33 | baseline_box do
34 | filler
35 | end
36 | vertical_box width: "12px" do
37 | horizontal_box flex: 1 do
38 | filler
39 | vertical_box width: "2px", backgroundColor: "#B7B7B7" do
40 | end
41 | filler
42 | end
43 | end
44 | text "Walk 4 min", gravity: :center, flex: 4, size: :xs, color: "#8c8c8c"
45 | end
46 |
47 | horizontal_box spacing: :lg, cornerRadius: "30px" do
48 | horizontal_box flex: 1 do
49 | text "20:34", gravity: :center, size: :sm
50 | end
51 | vertical_box flex: 0 do
52 | filler
53 | vertical_box cornerRadius: "30px", width: "12px", height: "12px", borderWidth: "2px", borderColor: "#6486E3" do
54 | end
55 | filler
56 | end
57 | text "Ochanomizu", gravity: :center, flex: 4, size: :sm
58 | end
59 |
60 | horizontal_box spacing: :lg, height: "64px" do
61 | baseline_box flex: 1 do
62 | filler
63 | end
64 | vertical_box width: "12px" do
65 | horizontal_box flex: 1 do
66 | filler
67 | vertical_box width: "2px", backgroundColor: "#6486E3" do
68 | end
69 | filler
70 | end
71 | end
72 | text "Metro 1hr", gravity: :center, flex: 4, size: :xs, color: "#8c8c8c"
73 | end
74 |
75 | horizontal_box spacing: :lg, cornerRadius: "30px" do
76 | text "20:40", gravity: :center, size: :sm
77 | vertical_box flex: 0 do
78 | filler
79 | vertical_box cornerRadius: "30px", width: "12px", height: "12px", borderColor: "#6486E3", borderWidth: "2px" do
80 | end
81 | filler
82 | end
83 | text "Shinjuku", gravity: :center, flex: 4, size: :sm
84 | end
85 | end
86 | end
87 | end
88 |
89 | puts json
90 | Clipboard.copy json
91 |
--------------------------------------------------------------------------------
/example/dialog.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 | require 'clipboard'
3 |
4 | def border(color)
5 | {
6 | borderColor: color,
7 | borderWidth: :light
8 | }
9 | end
10 |
11 | def green_box(options = {})
12 | horizontal_box **border("#00FF00").merge(options) do
13 | yield if block_given?
14 | end
15 | end
16 |
17 | def blue_box(options = {})
18 | horizontal_box **border("#0000FF").merge(options) do
19 | yield if block_given?
20 | end
21 | end
22 |
23 | def blue_vertical_box(options = {})
24 | vertical_box **border("#0000FF").merge(options) do
25 | yield if block_given?
26 | end
27 | end
28 |
29 | def dialog_box(message)
30 | horizontal_box position: :absolute,
31 | offsetStart: "1%", offsetEnd: "1%",
32 | offsetBottom: "#{100/62.0}%", height: "32%",
33 | borderColor: "#000000", borderWidth: "semi-bold",
34 | backgroundColor: "#876444",
35 | cornerRadius: :xl,
36 | paddingAll: "2px" do
37 | horizontal_box width: "100%", height: "100%",
38 | borderColor: "#d1baa5", borderWidth: "semi-bold",
39 | backgroundColor: "#f7edc8",
40 | cornerRadius: :lg,
41 | paddingAll: "15px" do
42 | text message, wrap: true, size: :xs, maxLines: 3
43 | end
44 | end
45 | end
46 |
47 | def avatar_image_box(url)
48 | horizontal_box position: :absolute,
49 | offsetStart: "60%", width: "50%",
50 | offsetBottom: "35%", height: "40%" do
51 | image url, size: :full, aspectRatio: "2:1"
52 | end
53 | end
54 |
55 | def avatar_name_box(name)
56 | # 陰影
57 | horizontal_box position: :absolute,
58 | offsetStart: "60%", width: "50%",
59 | offsetBottom: "28.5%", height: "8%",
60 | backgroundColor: "#00000066",
61 | cornerRadius: :xl do
62 | end
63 | # 文字
64 | horizontal_box position: :absolute,
65 | offsetStart: "60%", width: "50%",
66 | offsetBottom: "30%", height: "8%",
67 | borderColor: "#f7edc8", borderWidth: "medium",
68 | backgroundColor: "#876444",
69 | cornerRadius: :xl do
70 | text name, size: :xs, color: "#FFFFFF", weight: :bold,
71 | align: :center, gravity: :center
72 | end
73 | end
74 |
75 | text_message = "在 卡米哥 的「 深入 Flex Message - 以對話遊戲為例 」主題中,將會分享:使用 Flex Message 時的實作細節,包含遊戲風格的對話框介面、特殊樣式的按鈕、隱藏式的感應區、跨裝置的排版等。也會說明在設計一個新的 Flex Message 版型時,所採用的開發流程。"
76 | url = "https://i.imgur.com/WWbqH9A.png"
77 | kamigo_url = "https://www.kamigo.tw/assets/kamigo-c3b10dff4cdb60fa447496b22edad6c32fffde96de20262efba690892e4461e8.png"
78 | bg_url = "https://1.bp.blogspot.com/-IRrkMk_r1_c/XJB6NdPZATI/AAAAAAABSAg/zJxTXh-zSIAHpcq8vIrsnzlhTnUVc9h1ACLcBGAs/s800/bg_kamogawa.jpg"
79 | body_url = "https://i.imgur.com/zUttWD1.png"
80 |
81 | json = Kamiflex.json(self) do
82 | alt_text "hello world!"
83 | bubble size: :giga do
84 | body paddingAll: "0px", backgroundColor: "#333333" do
85 | image body_url, size: :full, aspectRatio: "100:62"
86 | # 背景
87 | image bg_url, size: :full, position: :absolute, aspectRatio: "800:450"
88 | # 對話框
89 | dialog_box text_message
90 | # 角色圖
91 | avatar_image_box kamigo_url
92 | # 角色名稱
93 | avatar_name_box "卡米狗"
94 | end
95 | end
96 | end
97 |
98 | puts json
99 | Clipboard.copy json
--------------------------------------------------------------------------------
/example/dialog2.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/kamiflex'
2 | require 'clipboard'
3 |
4 | @body_url = "https://i.imgur.com/zUttWD1.png"
5 |
6 | def border(color)
7 | {
8 | borderColor: color,
9 | borderWidth: :light
10 | }
11 | end
12 |
13 | def green_box(options = {})
14 | horizontal_box **border("#00FF00").merge(options) do
15 | yield if block_given?
16 | end
17 | end
18 |
19 | def blue_box(options = {})
20 | horizontal_box **border("#0000FF").merge(options) do
21 | yield if block_given?
22 | end
23 | end
24 |
25 | def blue_vertical_box(options = {})
26 | vertical_box **border("#0000FF").merge(options) do
27 | yield if block_given?
28 | end
29 | end
30 |
31 | def dialog_box(message)
32 | vertical_box do
33 | image @body_url, size: :full, aspectRatio: "100:50"
34 | horizontal_box paddingAll: "5px" do
35 | horizontal_box borderColor: "#000000", borderWidth: "semi-bold",
36 | backgroundColor: "#876444",
37 | cornerRadius: :xl,
38 | paddingAll: "2px" do
39 | horizontal_box borderColor: "#d1baa5", borderWidth: "semi-bold",
40 | backgroundColor: "#f7edc8",
41 | cornerRadius: :lg,
42 | paddingAll: "15px" do
43 | text message, wrap: true, size: :xs
44 | end
45 | end
46 | end
47 | end
48 | end
49 |
50 | def avatar_image_box(url)
51 | vertical_box position: :absolute,
52 | offsetStart: "60%", width: "50%",
53 | offsetTop: "0px" do
54 | image @body_url, size: :full, aspectRatio: "50:25"
55 | image url, size: :full, aspectRatio: "2:1"
56 | end
57 | end
58 |
59 | def avatar_name_box(name)
60 | # 陰影
61 | vertical_box position: :absolute,
62 | offsetStart: "60%", width: "50%",
63 | offsetTop: "10px" do
64 | image @body_url, size: :full, aspectRatio: "50:45"
65 | horizontal_box height: "30px", backgroundColor: "#00000066", cornerRadius: :xl do
66 | end
67 | end
68 | # 文字
69 | vertical_box position: :absolute,
70 | offsetStart: "60%", width: "50%",
71 | offsetTop: "5px" do
72 | image @body_url, size: :full, aspectRatio: "50:45"
73 | horizontal_box height: "30px", borderColor: "#f7edc8", borderWidth: "medium", backgroundColor: "#876444", cornerRadius: :xl do
74 | text name, size: :xs, color: "#FFFFFF", weight: :bold, align: :center, gravity: :center
75 | end
76 | end
77 | end
78 |
79 | text_message = "在 卡米哥 的「 深入 Flex Message - 以對話遊戲為例 」主題中,將會分享:使用 Flex Message 時的實作細節,包含遊戲風格的對話框介面、特殊樣式的按鈕、隱藏式的感應區、跨裝置的排版等。也會說明在設計一個新的 Flex Message 版型時,所採用的開發流程。"
80 | url = "https://i.imgur.com/WWbqH9A.png"
81 | kamigo_url = "https://www.kamigo.tw/assets/kamigo-c3b10dff4cdb60fa447496b22edad6c32fffde96de20262efba690892e4461e8.png"
82 | bg_url = "https://1.bp.blogspot.com/-IRrkMk_r1_c/XJB6NdPZATI/AAAAAAABSAg/zJxTXh-zSIAHpcq8vIrsnzlhTnUVc9h1ACLcBGAs/s800/bg_kamogawa.jpg"
83 | body_url = "https://i.imgur.com/zUttWD1.png"
84 |
85 | json = Kamiflex.json(self) do
86 | alt_text "hello world!"
87 | bubble size: :giga do
88 | body paddingAll: "0px", backgroundColor: "#333333" do
89 | image body_url, size: :full, aspectRatio: "100:62", position: :absolute
90 | # 背景
91 | image bg_url, size: :full, position: :absolute, aspectRatio: "800:450"
92 | # 對話框
93 | dialog_box text_message
94 | # 角色圖
95 | avatar_image_box kamigo_url
96 | # 角色名稱
97 | avatar_name_box "卡米狗"
98 | end
99 | end
100 | end
101 |
102 | puts json
103 | Clipboard.copy json
--------------------------------------------------------------------------------
/lib/kamiflex/core.rb:
--------------------------------------------------------------------------------
1 | module Kamiflex
2 | module Core
3 | def flex
4 | attributes, _contents = flex_scope{ yield }
5 | {
6 | type: "flex",
7 | altText: "this is a flex message",
8 | contents: _contents.first
9 | }.merge(attributes.slice(:quickReply, :altText))
10 | end
11 |
12 | def bubble(**params)
13 | attributes, _contents = flex_scope{ yield }
14 | @flex_contents << {
15 | type: "bubble"
16 | }.merge(attributes.slice(:size, :direction, :header, :hero, :body, :footer, :styles, :action))
17 | .merge(params)
18 | end
19 |
20 | def bubbles(resources, **params)
21 | resources.each_with_index do |resource, index|
22 | attributes, _contents = flex_scope{ yield(resource, index) }
23 | @flex_contents << {
24 | type: "bubble",
25 | }.merge(attributes.slice(:size, :direction, :header, :hero, :body, :footer, :styles, :action))
26 | .merge(params)
27 | end
28 | end
29 |
30 | def carousel
31 | attributes, _contents = flex_scope{ yield }
32 | @flex_contents << {
33 | type: "carousel",
34 | contents: _contents
35 | }
36 | end
37 |
38 | # bubble attributes
39 | def header(**params)
40 | _attributes, contents = flex_scope{ yield }
41 | @flex_attributes[:header] = {
42 | type: "box",
43 | layout: "vertical",
44 | contents: contents
45 | }.merge(params)
46 | end
47 |
48 | def hero(image_url, **params)
49 | @flex_attributes[:hero] = {
50 | "type": "image",
51 | "url": image_url
52 | }.merge(params)
53 | end
54 |
55 | def body(**params)
56 | _attributes, contents = flex_scope{ yield }
57 | @flex_attributes[:body] = {
58 | type: "box",
59 | layout: "vertical",
60 | contents: contents
61 | }.merge(params)
62 | end
63 |
64 | def footer(**params)
65 | _attributes, contents = flex_scope{ yield }
66 | @flex_attributes[:footer] = {
67 | type: "box",
68 | layout: "vertical",
69 | contents: contents
70 | }.merge(params)
71 | end
72 |
73 | # style
74 | def styles(params)
75 | @flex_attributes[:styles] = params
76 | end
77 |
78 | def alt_text(text)
79 | @flex_attributes[:altText] = text
80 | end
81 |
82 | # container
83 | def horizontal_box(resources = [nil], **params)
84 | resources.each_with_index do |resource, index|
85 | _attributes, contents = flex_scope{ yield(resource, index) } if block_given?
86 | @flex_contents << {
87 | type: "box",
88 | layout: "horizontal",
89 | contents: contents || []
90 | }.merge(params)
91 | end
92 | end
93 |
94 | def vertical_box(resources = [nil], **params, &block)
95 | horizontal_box(resources, layout: "vertical", **params, &block)
96 | end
97 |
98 | def baseline_box(resources = [nil], **params, &block)
99 | horizontal_box(resources, layout: "baseline", **params, &block)
100 | end
101 |
102 | private
103 |
104 | def flex_scope
105 | parent_attributes, parent_contents = @flex_attributes, @flex_contents
106 | @flex_attributes, @flex_contents = {}, []
107 | yield self
108 | [@flex_attributes, @flex_contents]
109 | ensure
110 | @flex_attributes, @flex_contents = parent_attributes, parent_contents
111 | end
112 | end
113 | end
114 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
19 | # config.require_master_key = true
20 |
21 | # Disable serving static files from the `/public` folder by default since
22 | # Apache or NGINX already handles this.
23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
33 |
34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
35 | # config.action_controller.asset_host = 'http://assets.example.com'
36 |
37 | # Specifies the header that your server uses for sending files.
38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
40 |
41 | # Store uploaded files on the local file system (see config/storage.yml for options)
42 | config.active_storage.service = :local
43 |
44 | # Mount Action Cable outside main process or domain
45 | # config.action_cable.mount_path = nil
46 | # config.action_cable.url = 'wss://example.com/cable'
47 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
48 |
49 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
50 | # config.force_ssl = true
51 |
52 | # Use the lowest log level to ensure availability of diagnostic information
53 | # when problems arise.
54 | config.log_level = :debug
55 |
56 | # Prepend all log lines with the following tags.
57 | config.log_tags = [ :request_id ]
58 |
59 | # Use a different cache store in production.
60 | # config.cache_store = :mem_cache_store
61 |
62 | # Use a real queuing backend for Active Job (and separate queues per environment)
63 | # config.active_job.queue_adapter = :resque
64 | # config.active_job.queue_name_prefix = "dummy_#{Rails.env}"
65 |
66 | config.action_mailer.perform_caching = false
67 |
68 | # Ignore bad email addresses and do not raise email delivery errors.
69 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
70 | # config.action_mailer.raise_delivery_errors = false
71 |
72 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
73 | # the I18n.default_locale when a translation cannot be found).
74 | config.i18n.fallbacks = true
75 |
76 | # Send deprecation notices to registered listeners.
77 | config.active_support.deprecation = :notify
78 |
79 | # Use default logging formatter so that PID and timestamp are not suppressed.
80 | config.log_formatter = ::Logger::Formatter.new
81 |
82 | # Use a different logger for distributed setups.
83 | # require 'syslog/logger'
84 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
85 |
86 | if ENV["RAILS_LOG_TO_STDOUT"].present?
87 | logger = ActiveSupport::Logger.new(STDOUT)
88 | logger.formatter = config.log_formatter
89 | config.logger = ActiveSupport::TaggedLogging.new(logger)
90 | end
91 |
92 | # Do not dump schema after migrations.
93 | config.active_record.dump_schema_after_migration = false
94 | end
95 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kamiflex
2 | Kamiflex provide a pretty DSL to build your [flex message of line messaging api](https://developers.line.biz/en/docs/messaging-api/using-flex-messages/) like this:
3 |
4 | # Document
5 |
6 | [https://etrex.tw/kamigo/05_kamiflex.html](https://etrex.tw/kamigo/05_kamiflex.html)
7 |
8 | # Live Demo
9 |
10 | [Kamiflex Simulator](https://kamiflex.etrex.tw/)
11 |
12 | #### in pure ruby
13 |
14 | # [example/cards.rb](example/cards.rb)
15 |
16 | ``` ruby
17 | require 'kamiflex'
18 |
19 | @products = [
20 | {
21 | name: "kamigo",
22 | star: 4,
23 | place: "https://github.com/etrex/kamigo",
24 | time: "10:00 - 23:00",
25 | contact_url: "https://github.com/etrex/kamigo",
26 | product_url: "https://github.com/etrex/kamigo"
27 | },
28 | {
29 | name: "kamiliff",
30 | star: 3,
31 | place: "https://github.com/etrex/kamiliff",
32 | time: "11:00 - 23:00",
33 | contact_url: "https://github.com/etrex/kamiliff",
34 | product_url: "https://github.com/etrex/kamiliff"
35 | },
36 | {
37 | name: "kamiflex",
38 | star: 5,
39 | place: "https://github.com/etrex/kamiflex",
40 | time: "09:00 - 23:00",
41 | contact_url: "https://github.com/etrex/kamiflex",
42 | product_url: "https://github.com/etrex/kamiflex"
43 | },
44 | ]
45 |
46 | def star(num)
47 | baseline_box margin: :md do
48 | (0...num).each do
49 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png", size: :sm
50 | end
51 | (num...5).each do
52 | icon "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png", size: :sm
53 | end
54 | text "#{num}.0", size: :sm, color: "#999999", margin: :md, flex: 0
55 | end
56 | end
57 |
58 | def field(key, value)
59 | baseline_box spacing: :sm do
60 | text key, "color":"#aaaaaa","size":"sm","flex":1
61 | text value, "wrap":true,"color":"#666666","size":"sm","flex":5
62 | end
63 | end
64 |
65 | json = Kamiflex.build(self) do
66 | carousel do
67 | bubbles @products do |product|
68 | hero "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png",
69 | size: :full, aspectRatio: "20:13", aspectMode: :cover, action: uri_action("http://linecorp.com/")
70 | body do
71 | text product[:name], weight: :bold, size: :xl
72 | star(product[:star])
73 | vertical_box margin: :lg, spacing: :sm do
74 | field "Place", product[:place]
75 | field "Time", product[:time]
76 | end
77 | end
78 | footer spacing: :sm, flex: 0 do
79 | url_button "CALL", product[:contact_url], style: :link, height: :sm
80 | url_button "WEBSITE", product[:product_url], style: :link, height: :sm
81 | spacer size: :sm
82 | end
83 | end
84 | end
85 | end
86 |
87 | puts json
88 | ```
89 |
90 | 
91 |
92 | #### in rails
93 | ``` ruby
94 | # todos/index.line.erb
95 | <%= raw Kamiflex.build(self) do
96 | bubble do
97 | body do
98 | horizontal_box do
99 | text "🍔", flex: 0, action: message_action("/")
100 | text "Todos"
101 | text "🆕", align: "end", action: uri_action(new_todo_path)
102 | end
103 | separator
104 | if @todos.present?
105 | vertical_box margin: "lg" do
106 | horizontal_box @todos, margin: "lg" do |todo|
107 | text todo.name, action: message_action("/todos/#{todo.id}")
108 | text "❌", align: "end", action: message_action("DELETE /todos/#{todo.id}")
109 | end
110 | end
111 | else
112 | text "no contents yet", margin: "lg"
113 | end
114 | end
115 | end
116 | end %>
117 | ```
118 |
119 | I will make a template name `flex` for rails in the future.
120 |
121 | With this template, it's more clear then `erb`.
122 |
123 | ``` ruby
124 | # todos/index.line.flex
125 | bubble do
126 | body do
127 | horizontal_box do
128 | text "🍔", flex: 0, action: message_action("/")
129 | text "Todos"
130 | text "🆕", align: "end", action: uri_action(new_todo_path)
131 | end
132 | separator
133 | if @todos.present?
134 | vertical_box margin: "lg" do
135 | horizontal_box @todos, margin: "lg" do |todo|
136 | text todo.name, action: message_action("/todos/#{todo.id}")
137 | text "❌", align: "end", action: message_action("DELETE /todos/#{todo.id}")
138 | end
139 | end
140 | else
141 | text "no contents yet", margin: "lg"
142 | end
143 | end
144 | end
145 | ```
146 |
147 | ## Installation
148 | Add this line to your application's Gemfile:
149 |
150 | ```ruby
151 | gem 'kamiflex'
152 | ```
153 |
154 | ## Author
155 | Create by [etrex](https://etrex.tw)
156 |
157 | ## License
158 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
159 |
--------------------------------------------------------------------------------